View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl2.introspection;
18  
19  import java.util.HashMap;
20  import java.util.HashSet;
21  import java.util.Map;
22  import java.util.Set;
23  
24  /**
25   * A sandbox describes permissions on a class by explicitly allowing or forbidding access to methods and properties
26   * through "whitelists" and "blacklists".
27   * <p>
28   * A <b>whitelist</b> explicitly allows methods/properties for a class;
29   * <ul>
30   * <li>
31   * If a whitelist is empty and thus does not contain any names, all properties/methods are allowed for its class.
32   * </li>
33   * <li>
34   * If it is not empty, the only allowed properties/methods are the ones contained.
35   * </li>
36   * </ul>
37   * </p>
38   * <p>
39   * A <b>blacklist</b> explicitly forbids methods/properties for a class;
40   * <ul>
41   * <li>
42   * If a blacklist is empty and thus does not contain any names, all properties/methods are forbidden for its class.
43   * </li>
44   * <li>
45   * If it is not empty, the only forbidden properties/methods are the ones contained.
46   * </li>
47   * </ul>
48   * <p>
49   * Permissions are composed of three lists, read, write, execute, each being "white" or "black":
50   * <ul>
51   * <li><b>read</b> controls readable properties </li>
52   * <li><b>write</b> controls writeable properties</li>
53   * <li><b>execute</b> controls executable methods and constructor</li>
54   * </ul>
55   * </p>
56   * @since 2.1
57   */
58  public final class Sandbox {
59      /**
60       * The map from class names to permissions.
61       */
62      private final Map<String, Permissions> sandbox;
63  
64      /**
65       * Creates a new default sandbox.
66       */
67      public Sandbox() {
68          this(new HashMap<String, Permissions>());
69      }
70  
71      /**
72       * Creates a sandbox based on an existing permissions map.
73       * @param map the permissions map
74       */
75      protected Sandbox(Map<String, Permissions> map) {
76          sandbox = map;
77      }
78  
79      /**
80       * Gets the read permission value for a given property of a class.
81       * @param clazz the class
82       * @param name the property name
83       * @return null if not allowed, the name of the property to use otherwise
84       */
85      public String read(Class<?> clazz, String name) {
86          return read(clazz.getName(), name);
87      }
88  
89      /**
90       * Gets the read permission value for a given property of a class.
91       * @param clazz the class name
92       * @param name the property name
93       * @return null if not allowed, the name of the property to use otherwise
94       */
95      public String read(String clazz, String name) {
96          Permissions permissions = sandbox.get(clazz);
97          if (permissions == null) {
98              return name;
99          } else {
100             return permissions.read().get(name);
101         }
102     }
103 
104     /**
105      * Gets the write permission value for a given property of a class.
106      * @param clazz the class
107      * @param name the property name
108      * @return null if not allowed, the name of the property to use otherwise
109      */
110     public String write(Class<?> clazz, String name) {
111         return write(clazz.getName(), name);
112     }
113 
114     /**
115      * Gets the write permission value for a given property of a class.
116      * @param clazz the class name
117      * @param name the property name
118      * @return null if not allowed, the name of the property to use otherwise
119      */
120     public String write(String clazz, String name) {
121         Permissions permissions = sandbox.get(clazz);
122         if (permissions == null) {
123             return name;
124         } else {
125             return permissions.write().get(name);
126         }
127     }
128 
129     /**
130      * Gets the execute permission value for a given method of a class.
131      * @param clazz the class
132      * @param name the method name
133      * @return null if not allowed, the name of the method to use otherwise
134      */
135     public String execute(Class<?> clazz, String name) {
136         return execute(clazz.getName(), name);
137     }
138 
139     /**
140      * Gets the execute permission value for a given method of a class.
141      * @param clazz the class name
142      * @param name the method name
143      * @return null if not allowed, the name of the method to use otherwise
144      */
145     public String execute(String clazz, String name) {
146         Permissions permissions = sandbox.get(clazz);
147         if (permissions == null) {
148             return name;
149         } else {
150             return permissions.execute().get(name);
151         }
152     }
153 
154     /**
155      * A base set of names.
156      */
157     public abstract static class Names {
158         /**
159          * Adds a name to this set.
160          * @param name the name to add
161          * @return  true if the name was really added, false if not
162          */
163         public abstract boolean add(String name);
164 
165         /**
166          * Adds an alias to a name to this set.
167          * <p>This only has an effect on white lists.</p>
168          * @param name the name to alias
169          * @param alias the alias
170          * @return  true if the alias was added, false if it was already present
171          */
172         public boolean alias(String name, String alias) {
173             return false;
174         }
175 
176         /**
177          * Whether a given name is allowed or not.
178          * @param name the method/property name to check
179          * @return null if not allowed, the actual name to use otherwise
180          */
181         public String get(String name) {
182             return name;
183         }
184     }
185     /**
186      * The pass-thru name set.
187      */
188     private static final Names WHITE_NAMES = new Names() {
189         @Override
190         public boolean add(String name) {
191             return false;
192         }
193     };
194 
195     /**
196      * A white set of names.
197      */
198     public static final class WhiteSet extends Names {
199         /** The map of controlled names and aliases. */
200         private Map<String, String> names = null;
201 
202         @Override
203         public boolean add(String name) {
204             if (names == null) {
205                 names = new HashMap<String, String>();
206             }
207             return names.put(name, name) == null;
208         }
209 
210         @Override
211         public boolean alias(String name, String alias) {
212             if (names == null) {
213                 names = new HashMap<String, String>();
214             }
215             return names.put(alias, name) == null;
216         }
217 
218         @Override
219         public String get(String name) {
220             if (names == null) {
221                 return name;
222             } else {
223                 return names.get(name);
224             }
225         }
226     }
227 
228     /**
229      * A black set of names.
230      */
231     public static final class BlackSet extends Names {
232         /** The set of controlled names. */
233         private Set<String> names = null;
234 
235         @Override
236         public boolean add(String name) {
237             if (names == null) {
238                 names = new HashSet<String>();
239             }
240             return names.add(name);
241         }
242 
243         @Override
244         public String get(String name) {
245             return names != null && !names.contains(name) ? name : null;
246         }
247     }
248 
249     /**
250      * Contains the white or black lists for properties and methods for a given class.
251      */
252     public static final class Permissions {
253         /** The controlled readable properties. */
254         private final Names read;
255         /** The controlled  writeable properties. */
256         private final Names write;
257         /** The controlled methods. */
258         private final Names execute;
259 
260         /**
261          * Creates a new permissions instance.
262          * @param readFlag whether the read property list is white or black
263          * @param writeFlag whether the write property list is white or black
264          * @param executeFlag whether the method list is white of black
265          */
266         Permissions(boolean readFlag, boolean writeFlag, boolean executeFlag) {
267             this(readFlag ? new WhiteSet() : new BlackSet(),
268                     writeFlag ? new WhiteSet() : new BlackSet(),
269                     executeFlag ? new WhiteSet() : new BlackSet());
270         }
271 
272         /**
273          * Creates a new permissions instance.
274          * @param nread the read set
275          * @param nwrite the write set
276          * @param nexecute the method set 
277          */
278         Permissions(Names nread, Names nwrite, Names nexecute) {
279             this.read = nread != null ? nread : WHITE_NAMES;
280             this.write = nwrite != null ? nwrite : WHITE_NAMES;
281             this.execute = nexecute != null ? nexecute : WHITE_NAMES;
282         }
283 
284         /**
285          * Adds a list of readable property names to these permissions.
286          * @param pnames the property names
287          * @return this instance of permissions
288          */
289         public Permissions read(String... pnames) {
290             for (String pname : pnames) {
291                 read.add(pname);
292             }
293             return this;
294         }
295 
296         /**
297          * Adds a list of writeable property names to these permissions.
298          * @param pnames the property names
299          * @return this instance of permissions
300          */
301         public Permissions write(String... pnames) {
302             for (String pname : pnames) {
303                 write.add(pname);
304             }
305             return this;
306         }
307 
308         /**
309          * Adds a list of executable methods names to these permissions.
310          * <p>The constructor is denoted as the empty-string, all other methods by their names.</p>
311          * @param mnames the method names
312          * @return this instance of permissions
313          */
314         public Permissions execute(String... mnames) {
315             for (String mname : mnames) {
316                 execute.add(mname);
317             }
318             return this;
319         }
320 
321         /**
322          * Gets the set of readable property names in these permissions.
323          * @return the set of property names
324          */
325         public Names read() {
326             return read;
327         }
328 
329         /**
330          * Gets the set of writeable property names in these permissions.
331          * @return the set of property names
332          */
333         public Names write() {
334             return write;
335         }
336 
337         /**
338          * Gets the set of method names in these permissions.
339          * @return the set of method names
340          */
341         public Names execute() {
342             return execute;
343         }
344     }
345     
346     /**
347      * The pass-thru permissions.
348      */
349     private static final Permissions ALL_WHITE = new Permissions(WHITE_NAMES, WHITE_NAMES, WHITE_NAMES);
350 
351     /**
352      * Creates the set of permissions for a given class.
353      * @param clazz the class for which these permissions apply
354      * @param readFlag whether the readable property list is white - true - or black - false -
355      * @param writeFlag whether the writeable property list is white - true - or black - false -
356      * @param executeFlag whether the executable method list is white white - true - or black - false -
357      * @return the set of permissions
358      */
359     public Permissions permissions(String clazz, boolean readFlag, boolean writeFlag, boolean executeFlag) {
360         Permissions box = new Permissions(readFlag, writeFlag, executeFlag);
361         sandbox.put(clazz, box);
362         return box;
363     }
364 
365     /**
366      * Creates a new set of permissions based on white lists for methods and properties for a given class.
367      * @param clazz the whitened class name
368      * @return the permissions instance
369      */
370     public Permissions white(String clazz) {
371         return permissions(clazz, true, true, true);
372     }
373 
374     /**
375      * Creates a new set of permissions based on black lists for methods and properties for a given class.
376      * @param clazz the blackened class name
377      * @return the permissions instance
378      */
379     public Permissions black(String clazz) {
380         return permissions(clazz, false, false, false);
381     }
382 
383     /**
384      * Gets the set of permissions associated to a class.
385      * @param clazz the class name
386      * @return the defined permissions or an all-white permission instance if none were defined
387      */
388     public Permissions get(String clazz) {
389         Permissions permissions = sandbox.get(clazz);
390         if (permissions == null) {
391             return ALL_WHITE;
392         } else {
393             return permissions;
394         }
395     }
396 }