View Javadoc

1   package groovy.lang;
2   
3   import groovy.security.GroovyCodeSourcePermission;
4   
5   import java.io.ByteArrayInputStream;
6   import java.io.File;
7   import java.io.FileInputStream;
8   import java.io.FileNotFoundException;
9   import java.io.IOException;
10  import java.io.InputStream;
11  import java.net.MalformedURLException;
12  import java.net.URL;
13  import java.security.AccessController;
14  import java.security.CodeSource;
15  import java.security.PrivilegedActionException;
16  import java.security.PrivilegedExceptionAction;
17  import java.security.cert.Certificate;
18  
19  /***
20   * CodeSource wrapper class that allows specific security policies to be associated with a class
21   * compiled from groovy source.
22   * 
23   * @author Steve Goetze
24   */
25  public class GroovyCodeSource {
26  	
27  	/*** 
28  	 * The codeSource to be given the generated class.  This can be used by policy file
29  	 * grants to administer security.
30  	 */
31  	private CodeSource codeSource;
32  	/*** The name given to the generated class */
33  	private String name;
34  	/*** The groovy source to be compiled and turned into a class */
35  	private InputStream inputStream;
36  	/*** The certificates used to sign the items from the codesource */
37  	Certificate[] certs;
38      private boolean cachable = false;
39      
40  	private File file;
41  	
42  	public GroovyCodeSource(String script, String name, String codeBase) {
43  		this(new ByteArrayInputStream(script.getBytes()), name, codeBase);
44  	}
45  	
46  	/***
47  	 * Construct a GroovyCodeSource for an inputStream of groovyCode that has an
48  	 * unknown provenance -- meaning it didn't come from a File or a URL (e.g. a String).
49  	 * The supplied codeBase will be used to construct a File URL that should match up
50  	 * with a java Policy entry that determines the grants to be associated with the
51  	 * class that will be built from the InputStream.
52  	 * 
53  	 * The permission groovy.security.GroovyCodeSourcePermission will be used to determine if the given codeBase
54  	 * may be specified.  That is, the current Policy set must have a GroovyCodeSourcePermission that implies
55  	 * the codeBase, or an exception will be thrown.  This is to prevent callers from hijacking
56  	 * existing codeBase policy entries unless explicitly authorized by the user.
57  	 */
58  	public GroovyCodeSource(InputStream inputStream, String name, String codeBase) {
59  		this.inputStream = inputStream;
60  		this.name = name;
61  		SecurityManager sm = System.getSecurityManager();
62  		if (sm != null) {
63  		    sm.checkPermission(new GroovyCodeSourcePermission(codeBase));
64  		}
65  		try {
66  			this.codeSource = new CodeSource(new URL("file", "", codeBase), (java.security.cert.Certificate[])null);
67  		} catch (MalformedURLException murle) {
68  			throw new RuntimeException("A CodeSource file URL cannot be constructed from the supplied codeBase: " + codeBase);
69  		}
70  	}
71  
72  	/*** 
73  	 * Package private constructor called by GroovyClassLoader for signed jar entries
74  	 */
75  	GroovyCodeSource(InputStream inputStream, String name, final File path, final Certificate[] certs) {
76  		this.inputStream = inputStream;
77  		this.name = name;
78  		try {
79  			this.codeSource = (CodeSource) AccessController.doPrivileged( new PrivilegedExceptionAction() {
80  				public Object run() throws MalformedURLException {
81  					//toURI().toURL() will encode, but toURL() will not.
82  					return new CodeSource(path.toURI().toURL(), certs);
83  				}
84  			});
85  		} catch (PrivilegedActionException pae) {
86  			//shouldn't happen
87  			throw new RuntimeException("Could not construct a URL from: " + path);
88  		}
89  	}
90  	
91  	public GroovyCodeSource(final File file) throws FileNotFoundException {
92  		if (!file.exists())
93  		    throw new FileNotFoundException(file.toString() + " (" +  file.getAbsolutePath() +  ")");
94  		else {
95  		   try {
96  		       if (!file.canRead())
97  		           throw new RuntimeException(file.toString() + " can not be read. Check the read permisson of the file \"" + file.toString() + "\" (" +  file.getAbsolutePath() +  ").");
98  		   }
99  		   catch (SecurityException e) {
100 		        throw e;
101 		    }
102 		}
103 
104 		//this.inputStream = new FileInputStream(file);
105 		this.file = file;
106 		this.inputStream = null;
107         this.cachable = true;
108 		//The calls below require access to user.dir - allow here since getName() and getCodeSource() are
109 		//package private and used only by the GroovyClassLoader.
110 		try {
111             Object[] info = (Object[]) AccessController.doPrivileged( new PrivilegedExceptionAction() {
112 				public Object run() throws MalformedURLException {
113                     Object[] info = new Object[2];
114                     URL url = file.toURI().toURL();
115                     info[0] = url.toExternalForm();
116 					//toURI().toURL() will encode, but toURL() will not.
117 					info[1] = new CodeSource(url, (Certificate[]) null);
118                     return info;
119 				}
120 			});
121 			this.name = (String) info[0];
122             this.codeSource = (CodeSource) info[1];
123 		} catch (PrivilegedActionException pae) {
124 			throw new RuntimeException("Could not construct a URL from: " + file);
125 		}
126 	}
127 	
128 	public GroovyCodeSource(URL url) throws IOException {
129 		if (url == null) {
130 			throw new RuntimeException("Could not construct a GroovyCodeSource from a null URL");
131 		}
132 		this.inputStream = url.openStream();
133 		this.name = url.toExternalForm();
134 		this.codeSource = new CodeSource(url, (java.security.cert.Certificate[])null);
135 	}
136 	
137 	CodeSource getCodeSource() {
138 		return codeSource;
139 	}
140 
141 	public InputStream getInputStream() {
142         try {
143             if (file!=null) return new FileInputStream(file);
144         } catch (FileNotFoundException fnfe) {}
145 		return inputStream;
146 	}
147 
148 	public String getName() {
149 		return name;
150 	}
151     
152     public File getFile() {
153         return file;
154     }
155     
156     public void setCachable(boolean b) {
157         cachable = b;
158     }
159 
160     public boolean isCachable() {
161         return cachable;
162     }
163 }