001    package groovy.lang;
002    
003    import groovy.security.GroovyCodeSourcePermission;
004    
005    import java.io.ByteArrayInputStream;
006    import java.io.File;
007    import java.io.FileInputStream;
008    import java.io.FileNotFoundException;
009    import java.io.IOException;
010    import java.io.InputStream;
011    import java.net.MalformedURLException;
012    import java.net.URL;
013    import java.security.AccessController;
014    import java.security.CodeSource;
015    import java.security.PrivilegedActionException;
016    import java.security.PrivilegedExceptionAction;
017    import java.security.cert.Certificate;
018    
019    /**
020     * CodeSource wrapper class that allows specific security policies to be associated with a class
021     * compiled from groovy source.
022     * 
023     * @author Steve Goetze
024     */
025    public class GroovyCodeSource {
026            
027            /** 
028             * The codeSource to be given the generated class.  This can be used by policy file
029             * grants to administer security.
030             */
031            private CodeSource codeSource;
032            /** The name given to the generated class */
033            private String name;
034            /** The groovy source to be compiled and turned into a class */
035            private InputStream inputStream;
036            /** The certificates used to sign the items from the codesource */
037            Certificate[] certs;
038        
039            private File file;
040            
041            public GroovyCodeSource(String script, String name, String codeBase) {
042                    this(new ByteArrayInputStream(script.getBytes()), name, codeBase);
043            }
044            
045            /**
046             * Construct a GroovyCodeSource for an inputStream of groovyCode that has an
047             * unknown provenance -- meaning it didn't come from a File or a URL (e.g. a String).
048             * The supplied codeBase will be used to construct a File URL that should match up
049             * with a java Policy entry that determines the grants to be associated with the
050             * class that will be built from the InputStream.
051             * 
052             * The permission groovy.security.GroovyCodeSourcePermission will be used to determine if the given codeBase
053             * may be specified.  That is, the current Policy set must have a GroovyCodeSourcePermission that implies
054             * the codeBase, or an exception will be thrown.  This is to prevent callers from hijacking
055             * existing codeBase policy entries unless explicitly authorized by the user.
056             */
057            public GroovyCodeSource(InputStream inputStream, String name, String codeBase) {
058                    this.inputStream = inputStream;
059                    this.name = name;
060                    SecurityManager sm = System.getSecurityManager();
061                    if (sm != null) {
062                        sm.checkPermission(new GroovyCodeSourcePermission(codeBase));
063                    }
064                    try {
065                            this.codeSource = new CodeSource(new URL("file", "", codeBase), (java.security.cert.Certificate[])null);
066                    } catch (MalformedURLException murle) {
067                            throw new RuntimeException("A CodeSource file URL cannot be constructed from the supplied codeBase: " + codeBase);
068                    }
069            }
070    
071            /** 
072             * Package private constructor called by GroovyClassLoader for signed jar entries
073             */
074            GroovyCodeSource(InputStream inputStream, String name, final File path, final Certificate[] certs) {
075                    this.inputStream = inputStream;
076                    this.name = name;
077                    try {
078                            this.codeSource = (CodeSource) AccessController.doPrivileged( new PrivilegedExceptionAction() {
079                                    public Object run() throws MalformedURLException {
080                                            //toURI().toURL() will encode, but toURL() will not.
081                                            return new CodeSource(path.toURI().toURL(), certs);
082                                    }
083                            });
084                    } catch (PrivilegedActionException pae) {
085                            //shouldn't happen
086                            throw new RuntimeException("Could not construct a URL from: " + path);
087                    }
088            }
089            
090            public GroovyCodeSource(final File file) throws FileNotFoundException {
091                    if (!file.exists())
092                        throw new FileNotFoundException(file.toString() + " (" +  file.getAbsolutePath() +  ")");
093                    else {
094                       try {
095                           if (!file.canRead())
096                               throw new RuntimeException(file.toString() + " can not be read. Check the read permisson of the file \"" + file.toString() + "\" (" +  file.getAbsolutePath() +  ").");
097                       }
098                       catch (SecurityException e) {
099                            throw e;
100                        }
101                    }
102    
103                    //this.inputStream = new FileInputStream(file);
104                    this.file = file;
105                    this.inputStream = null;
106                    //The calls below require access to user.dir - allow here since getName() and getCodeSource() are
107                    //package private and used only by the GroovyClassLoader.
108                    try {
109                            Object[] info = (Object[]) AccessController.doPrivileged( new PrivilegedExceptionAction() {
110                                    public Object run() throws MalformedURLException {
111                                            Object[] info = new Object[2];
112                                            info[0] = file.getAbsolutePath();
113                                            //toURI().toURL() will encode, but toURL() will not.
114                                            info[1] = new CodeSource(file.toURI().toURL(), (Certificate[]) null);
115                                            return info;
116                                    }
117                            });
118                            this.name = (String) info[0];
119                            this.codeSource = (CodeSource) info[1];
120                    } catch (PrivilegedActionException pae) {
121                            throw new RuntimeException("Could not construct a URL from: " + file);
122                    }
123            }
124            
125            public GroovyCodeSource(URL url) throws IOException {
126                    if (url == null) {
127                            throw new RuntimeException("Could not construct a GroovyCodeSource from a null URL");
128                    }
129                    this.inputStream = url.openStream();
130                    this.name = url.toExternalForm();
131                    this.codeSource = new CodeSource(url, (java.security.cert.Certificate[])null);
132            }
133            
134            CodeSource getCodeSource() {
135                    return codeSource;
136            }
137    
138            public InputStream getInputStream() {
139            try {
140                if (file!=null) return new FileInputStream(file);
141            } catch (FileNotFoundException fnfe) {}
142                    return inputStream;
143            }
144    
145            public String getName() {
146                    return name;
147            }
148        
149        public File getFile() {
150            return file;
151        }
152    }