001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.jexl2.introspection;
018
019 import java.util.HashMap;
020 import java.util.HashSet;
021 import java.util.Map;
022 import java.util.Set;
023
024 /**
025 * A sandbox describes permissions on a class by explicitly allowing or forbidding access to methods and properties
026 * through "whitelists" and "blacklists".
027 * <p>
028 * A <b>whitelist</b> explicitly allows methods/properties for a class;
029 * <ul>
030 * <li>
031 * If a whitelist is empty and thus does not contain any names, all properties/methods are allowed for its class.
032 * </li>
033 * <li>
034 * If it is not empty, the only allowed properties/methods are the ones contained.
035 * </li>
036 * </ul>
037 * </p>
038 * <p>
039 * A <b>blacklist</b> explicitly forbids methods/properties for a class;
040 * <ul>
041 * <li>
042 * If a blacklist is empty and thus does not contain any names, all properties/methods are forbidden for its class.
043 * </li>
044 * <li>
045 * If it is not empty, the only forbidden properties/methods are the ones contained.
046 * </li>
047 * </ul>
048 * <p>
049 * Permissions are composed of three lists, read, write, execute, each being "white" or "black":
050 * <ul>
051 * <li><b>read</b> controls readable properties </li>
052 * <li><b>write</b> controls writeable properties</li>
053 * <li><b>execute</b> controls executable methods and constructor</li>
054 * </ul>
055 * </p>
056 * @since 2.1
057 */
058 public final class Sandbox {
059 /**
060 * The map from class names to permissions.
061 */
062 private final Map<String, Permissions> sandbox;
063
064 /**
065 * Creates a new default sandbox.
066 */
067 public Sandbox() {
068 this(new HashMap<String, Permissions>());
069 }
070
071 /**
072 * Creates a sandbox based on an existing permissions map.
073 * @param map the permissions map
074 */
075 protected Sandbox(Map<String, Permissions> map) {
076 sandbox = map;
077 }
078
079 /**
080 * Gets the read permission value for a given property of a class.
081 * @param clazz the class
082 * @param name the property name
083 * @return null if not allowed, the name of the property to use otherwise
084 */
085 public String read(Class<?> clazz, String name) {
086 return read(clazz.getName(), name);
087 }
088
089 /**
090 * Gets the read permission value for a given property of a class.
091 * @param clazz the class name
092 * @param name the property name
093 * @return null if not allowed, the name of the property to use otherwise
094 */
095 public String read(String clazz, String name) {
096 Permissions permissions = sandbox.get(clazz);
097 if (permissions == null) {
098 return name;
099 } 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 }