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.jexl3.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 * 057 * @since 3.0 058 */ 059 public final class Sandbox { 060 /** 061 * The map from class names to permissions. 062 */ 063 private final Map<String, Permissions> sandbox; 064 065 /** 066 * Creates a new default sandbox. 067 */ 068 public Sandbox() { 069 this(new HashMap<String, Permissions>()); 070 } 071 072 /** 073 * Creates a sandbox based on an existing permissions map. 074 * @param map the permissions map 075 */ 076 protected Sandbox(Map<String, Permissions> map) { 077 sandbox = map; 078 } 079 080 /** 081 * Gets the read permission value for a given property of a class. 082 * @param clazz the class 083 * @param name the property name 084 * @return null if not allowed, the name of the property to use otherwise 085 */ 086 public String read(Class<?> clazz, String name) { 087 return read(clazz.getName(), name); 088 } 089 090 /** 091 * Gets the read permission value for a given property of a class. 092 * @param clazz the class name 093 * @param name the property name 094 * @return null if not allowed, the name of the property to use otherwise 095 */ 096 public String read(String clazz, String name) { 097 Permissions permissions = sandbox.get(clazz); 098 if (permissions == null) { 099 return name; 100 } else { 101 return permissions.read().get(name); 102 } 103 } 104 105 /** 106 * Gets the write permission value for a given property of a class. 107 * @param clazz the class 108 * @param name the property name 109 * @return null if not allowed, the name of the property to use otherwise 110 */ 111 public String write(Class<?> clazz, String name) { 112 return write(clazz.getName(), name); 113 } 114 115 /** 116 * Gets the write permission value for a given property of a class. 117 * @param clazz the class name 118 * @param name the property name 119 * @return null if not allowed, the name of the property to use otherwise 120 */ 121 public String write(String clazz, String name) { 122 Permissions permissions = sandbox.get(clazz); 123 if (permissions == null) { 124 return name; 125 } else { 126 return permissions.write().get(name); 127 } 128 } 129 130 /** 131 * Gets the execute permission value for a given method of a class. 132 * @param clazz the class 133 * @param name the method name 134 * @return null if not allowed, the name of the method to use otherwise 135 */ 136 public String execute(Class<?> clazz, String name) { 137 return execute(clazz.getName(), name); 138 } 139 140 /** 141 * Gets the execute permission value for a given method of a class. 142 * @param clazz the class name 143 * @param name the method name 144 * @return null if not allowed, the name of the method to use otherwise 145 */ 146 public String execute(String clazz, String name) { 147 Permissions permissions = sandbox.get(clazz); 148 if (permissions == null) { 149 return name; 150 } else { 151 return permissions.execute().get(name); 152 } 153 } 154 155 /** 156 * A base set of names. 157 */ 158 public abstract static class Names { 159 /** 160 * Adds a name to this set. 161 * @param name the name to add 162 * @return true if the name was really added, false if not 163 */ 164 public abstract boolean add(String name); 165 166 /** 167 * Adds an alias to a name to this set. 168 * <p>This only has an effect on white lists.</p> 169 * @param name the name to alias 170 * @param alias the alias 171 * @return true if the alias was added, false if it was already present 172 */ 173 public boolean alias(String name, String alias) { 174 return false; 175 } 176 177 /** 178 * Whether a given name is allowed or not. 179 * @param name the method/property name to check 180 * @return null if not allowed, the actual name to use otherwise 181 */ 182 public String get(String name) { 183 return name; 184 } 185 } 186 /** 187 * The pass-thru name set. 188 */ 189 private static final Names WHITE_NAMES = new Names() { 190 @Override 191 public boolean add(String name) { 192 return false; 193 } 194 }; 195 196 /** 197 * A white set of names. 198 */ 199 public static final class WhiteSet extends Names { 200 /** The map of controlled names and aliases. */ 201 protected Map<String, String> names = null; 202 203 @Override 204 public boolean add(String name) { 205 if (names == null) { 206 names = new HashMap<String, String>(); 207 } 208 return names.put(name, name) == null; 209 } 210 211 @Override 212 public boolean alias(String name, String alias) { 213 if (names == null) { 214 names = new HashMap<String, String>(); 215 } 216 return names.put(alias, name) == null; 217 } 218 219 @Override 220 public String get(String name) { 221 if (names == null) { 222 return name; 223 } else { 224 return names.get(name); 225 } 226 } 227 } 228 229 /** 230 * A black set of names. 231 */ 232 public static final class BlackSet extends Names { 233 /** The set of controlled names. */ 234 protected Set<String> names = null; 235 236 @Override 237 public boolean add(String name) { 238 if (names == null) { 239 names = new HashSet<String>(); 240 } 241 return names.add(name); 242 } 243 244 @Override 245 public String get(String name) { 246 return names != null && !names.contains(name) ? name : null; 247 } 248 } 249 250 /** 251 * Contains the white or black lists for properties and methods for a given class. 252 */ 253 public static final class Permissions { 254 /** The controlled readable properties. */ 255 private final Names read; 256 /** The controlled writeable properties. */ 257 private final Names write; 258 /** The controlled methods. */ 259 private final Names execute; 260 261 /** 262 * Creates a new permissions instance. 263 * @param readFlag whether the read property list is white or black 264 * @param writeFlag whether the write property list is white or black 265 * @param executeFlag whether the method list is white of black 266 */ 267 Permissions(boolean readFlag, boolean writeFlag, boolean executeFlag) { 268 this(readFlag ? new WhiteSet() : new BlackSet(), 269 writeFlag ? new WhiteSet() : new BlackSet(), 270 executeFlag ? new WhiteSet() : new BlackSet()); 271 } 272 273 /** 274 * Creates a new permissions instance. 275 * @param nread the read set 276 * @param nwrite the write set 277 * @param nexecute the method set 278 */ 279 Permissions(Names nread, Names nwrite, Names nexecute) { 280 this.read = nread != null ? nread : WHITE_NAMES; 281 this.write = nwrite != null ? nwrite : WHITE_NAMES; 282 this.execute = nexecute != null ? nexecute : WHITE_NAMES; 283 } 284 285 /** 286 * Adds a list of readable property names to these permissions. 287 * @param pnames the property names 288 * @return this instance of permissions 289 */ 290 public Permissions read(String... pnames) { 291 for (String pname : pnames) { 292 read.add(pname); 293 } 294 return this; 295 } 296 297 /** 298 * Adds a list of writeable property names to these permissions. 299 * @param pnames the property names 300 * @return this instance of permissions 301 */ 302 public Permissions write(String... pnames) { 303 for (String pname : pnames) { 304 write.add(pname); 305 } 306 return this; 307 } 308 309 /** 310 * Adds a list of executable methods names to these permissions. 311 * <p>The constructor is denoted as the empty-string, all other methods by their names.</p> 312 * @param mnames the method names 313 * @return this instance of permissions 314 */ 315 public Permissions execute(String... mnames) { 316 for (String mname : mnames) { 317 execute.add(mname); 318 } 319 return this; 320 } 321 322 /** 323 * Gets the set of readable property names in these permissions. 324 * @return the set of property names 325 */ 326 public Names read() { 327 return read; 328 } 329 330 /** 331 * Gets the set of writeable property names in these permissions. 332 * @return the set of property names 333 */ 334 public Names write() { 335 return write; 336 } 337 338 /** 339 * Gets the set of method names in these permissions. 340 * @return the set of method names 341 */ 342 public Names execute() { 343 return execute; 344 } 345 } 346 347 /** 348 * The pass-thru permissions. 349 */ 350 private static final Permissions ALL_WHITE = new Permissions(WHITE_NAMES, WHITE_NAMES, WHITE_NAMES); 351 352 /** 353 * Creates the set of permissions for a given class. 354 * @param clazz the class for which these permissions apply 355 * @param readFlag whether the readable property list is white - true - or black - false - 356 * @param writeFlag whether the writeable property list is white - true - or black - false - 357 * @param executeFlag whether the executable method list is white white - true - or black - false - 358 * @return the set of permissions 359 */ 360 public Permissions permissions(String clazz, boolean readFlag, boolean writeFlag, boolean executeFlag) { 361 Permissions box = new Permissions(readFlag, writeFlag, executeFlag); 362 sandbox.put(clazz, box); 363 return box; 364 } 365 366 /** 367 * Creates a new set of permissions based on white lists for methods and properties for a given class. 368 * @param clazz the whitened class name 369 * @return the permissions instance 370 */ 371 public Permissions white(String clazz) { 372 return permissions(clazz, true, true, true); 373 } 374 375 /** 376 * Creates a new set of permissions based on black lists for methods and properties for a given class. 377 * @param clazz the blackened class name 378 * @return the permissions instance 379 */ 380 public Permissions black(String clazz) { 381 return permissions(clazz, false, false, false); 382 } 383 384 /** 385 * Gets the set of permissions associated to a class. 386 * @param clazz the class name 387 * @return the defined permissions or an all-white permission instance if none were defined 388 */ 389 public Permissions get(String clazz) { 390 Permissions permissions = sandbox.get(clazz); 391 if (permissions == null) { 392 return ALL_WHITE; 393 } else { 394 return permissions; 395 } 396 } 397 }