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 }