1 module hunt.security.BasicPermission; 2 3 import hunt.security.Permission; 4 import hunt.security.PermissionCollection; 5 6 import hunt.collection; 7 8 import hunt.Exceptions; 9 import hunt.text.Common; 10 11 import std.algorithm; 12 13 /** 14 * The BasicPermission class : the Permission class, and 15 * can be used as the base class for permissions that want to 16 * follow the same naming convention as BasicPermission. 17 * <P> 18 * The name for a BasicPermission is the name of the given permission 19 * (for example, "exit", 20 * "setFactory", "print.queueJob", etc). The naming 21 * convention follows the hierarchical property naming convention. 22 * An asterisk may appear by itself, or if immediately preceded by a "." 23 * may appear at the end of the name, to signify a wildcard match. 24 * For example, "*" and "java.*" signify a wildcard match, while "*java", "a*b", 25 * and "java*" do not. 26 * <P> 27 * The action string (inherited from Permission) is unused. 28 * Thus, BasicPermission is commonly used as the base class for 29 * "named" permissions 30 * (ones that contain a name but no actions list; you either have the 31 * named permission or you don't.) 32 * Subclasses may implement actions on top of BasicPermission, 33 * if desired. 34 * <p> 35 * @see java.security.Permission 36 * @see java.security.Permissions 37 * @see java.security.PermissionCollection 38 * @see java.lang.SecurityManager 39 * 40 * @author Marianne Mueller 41 * @author Roland Schemers 42 */ 43 44 abstract class BasicPermission : Permission 45 { 46 private enum long serialVersionUID = 6279438298436773498L; 47 48 // does this permission have a wildcard at the end? 49 private bool wildcard; 50 51 // the name without the wildcard on the end 52 private string path; 53 54 // is this permission the old-style exitVM permission (pre JDK 1.6)? 55 private bool exitVM; 56 57 /** 58 * initialize a BasicPermission object. Common to all constructors. 59 */ 60 private void init(string name) { 61 if (name is null) 62 throw new NullPointerException("name can't be null"); 63 64 int len = cast(int)name.length; 65 66 if (len == 0) { 67 throw new IllegalArgumentException("name can't be empty"); 68 } 69 70 char last = name.charAt(len - 1); 71 72 // Is wildcard or ends with ".*"? 73 if (last == '*' && (len == 1 || name.charAt(len - 2) == '.')) { 74 wildcard = true; 75 if (len == 1) { 76 path = ""; 77 } else { 78 path = name.substring(0, len - 1); 79 } 80 } else { 81 if (name.equals("exitVM")) { 82 wildcard = true; 83 path = "exitVM."; 84 exitVM = true; 85 } else { 86 path = name; 87 } 88 } 89 } 90 91 /** 92 * Creates a new BasicPermission with the specified name. 93 * Name is the symbolic name of the permission, such as 94 * "setFactory", 95 * "print.queueJob", or "topLevelWindow", etc. 96 * 97 * @param name the name of the BasicPermission. 98 * 99 * @throws NullPointerException if {@code name} is {@code null}. 100 * @throws IllegalArgumentException if {@code name} is empty. 101 */ 102 this(string name) { 103 super(name); 104 init(name); 105 } 106 107 108 /** 109 * Creates a new BasicPermission object with the specified name. 110 * The name is the symbolic name of the BasicPermission, and the 111 * actions string is currently unused. 112 * 113 * @param name the name of the BasicPermission. 114 * @param actions ignored. 115 * 116 * @throws NullPointerException if {@code name} is {@code null}. 117 * @throws IllegalArgumentException if {@code name} is empty. 118 */ 119 this(string name, string actions) { 120 super(name); 121 init(name); 122 } 123 124 /** 125 * Checks if the specified permission is "implied" by 126 * this object. 127 * <P> 128 * More specifically, this method returns true if: 129 * <ul> 130 * <li> <i>p</i>'s class is the same as this object's class, and 131 * <li> <i>p</i>'s name equals or (in the case of wildcards) 132 * is implied by this object's 133 * name. For example, "a.b.*" implies "a.b.c". 134 * </ul> 135 * 136 * @param p the permission to check against. 137 * 138 * @return true if the passed permission is equal to or 139 * implied by this permission, false otherwise. 140 */ 141 override bool implies(Permission p) { 142 if ((p is null) || (typeid(p) != typeid(this))) 143 return false; 144 145 BasicPermission that = cast(BasicPermission) p; 146 147 if (this.wildcard) { 148 if (that.wildcard) { 149 // one wildcard can imply another 150 return that.path.startsWith(path); 151 } else { 152 // make sure ap.path is longer so a.b.* doesn't imply a.b 153 return (that.path.length > this.path.length) && 154 that.path.startsWith(this.path); 155 } 156 } else { 157 if (that.wildcard) { 158 // a non-wildcard can't imply a wildcard 159 return false; 160 } 161 else { 162 return this.path.equals(that.path); 163 } 164 } 165 } 166 167 /** 168 * Checks two BasicPermission objects for equality. 169 * Checks that <i>obj</i>'s class is the same as this object's class 170 * and has the same name as this object. 171 * <P> 172 * @param obj the object we are testing for equality with this object. 173 * @return true if <i>obj</i>'s class is the same as this object's class 174 * and has the same name as this BasicPermission object, false otherwise. 175 */ 176 override 177 bool opEquals(Object obj) { 178 if (obj == this) 179 return true; 180 181 if ((obj is null) || (typeid(obj) != typeid(this))) 182 return false; 183 184 BasicPermission bp = cast(BasicPermission) obj; 185 186 return getName().equals(bp.getName()); 187 } 188 189 190 /** 191 * Returns the hash code value for this object. 192 * The hash code used is the hash code of the name, that is, 193 * {@code getName().hashCode()}, where {@code getName} is 194 * from the Permission superclass. 195 * 196 * @return a hash code value for this object. 197 */ 198 override size_t toHash() @trusted nothrow { 199 return hashOf(this.getName()); 200 } 201 202 /** 203 * Returns the canonical string representation of the actions, 204 * which currently is the empty string "", since there are no actions for 205 * a BasicPermission. 206 * 207 * @return the empty string "". 208 */ 209 override string getActions() { 210 return ""; 211 } 212 213 /** 214 * Returns a new PermissionCollection object for storing BasicPermission 215 * objects. 216 * 217 * <p>BasicPermission objects must be stored in a manner that allows them 218 * to be inserted in any order, but that also enables the 219 * PermissionCollection {@code implies} method 220 * to be implemented in an efficient (and consistent) manner. 221 * 222 * @return a new PermissionCollection object suitable for 223 * storing BasicPermissions. 224 */ 225 override PermissionCollection newPermissionCollection() { 226 // return new BasicPermissionCollection(this.getClass()); 227 return new BasicPermissionCollection(this); 228 } 229 230 /** 231 * readObject is called to restore the state of the BasicPermission from 232 * a stream. 233 */ 234 // private void readObject(ObjectInputStream s) 235 // { 236 // s.defaultReadObject(); 237 // // init is called to initialize the rest of the values. 238 // init(getName()); 239 // } 240 241 /** 242 * Returns the canonical name of this BasicPermission. 243 * All internal invocations of getName should invoke this method, so 244 * that the pre-JDK 1.6 "exitVM" and current "exitVM.*" permission are 245 * equivalent in equals/hashCode methods. 246 * 247 * @return the canonical name of this BasicPermission. 248 */ 249 final string getCanonicalName() { 250 return exitVM ? "exitVM.*" : getName(); 251 } 252 } 253 254 /** 255 * A BasicPermissionCollection stores a collection 256 * of BasicPermission permissions. BasicPermission objects 257 * must be stored in a manner that allows them to be inserted in any 258 * order, but enable the implies function to evaluate the implies 259 * method in an efficient (and consistent) manner. 260 * 261 * A BasicPermissionCollection handles comparing a permission like "a.b.c.d.e" 262 * with a Permission such as "a.b.*", or "*". 263 * 264 * @see java.security.Permission 265 * @see java.security.Permissions 266 * 267 * 268 * @author Roland Schemers 269 * 270 * @serial include 271 */ 272 273 final class BasicPermissionCollection : PermissionCollection 274 { 275 276 private enum long serialVersionUID = 739301742472979399L; 277 278 /** 279 * Key is name, value is permission. All permission objects in 280 * collection must be of the same type. 281 * Not serialized; see serialization section at end of class. 282 */ 283 private Map!(string, Permission) perms; 284 285 /** 286 * This is set to {@code true} if this BasicPermissionCollection 287 * contains a BasicPermission with '*' as its permission name. 288 * 289 * @see #serialPersistentFields 290 */ 291 private bool all_allowed; 292 293 /** 294 * The class to which all BasicPermissions in this 295 * BasicPermissionCollection belongs. 296 * 297 * @see #serialPersistentFields 298 */ 299 // private Class<?> permClass; 300 301 /** 302 * Create an empty BasicPermissionCollection object. 303 * 304 */ 305 306 // this(Class<?> clazz) { 307 this(Object clazz) { 308 perms = new HashMap!(string, Permission)(11); 309 all_allowed = false; 310 // permClass = clazz; 311 } 312 313 /** 314 * Adds a permission to the BasicPermissions. The key for the hash is 315 * permission.path. 316 * 317 * @param permission the Permission object to add. 318 * 319 * @exception IllegalArgumentException - if the permission is not a 320 * BasicPermission, or if 321 * the permission is not of the 322 * same Class as the other 323 * permissions in this collection. 324 * 325 * @exception SecurityException - if this BasicPermissionCollection object 326 * has been marked readonly 327 */ 328 override void add(Permission permission) { 329 implementationMissing(); 330 // if (! (permission instanceof BasicPermission)) 331 // throw new IllegalArgumentException("invalid permission: "~ 332 // permission.toString()); 333 // if (isReadOnly()) 334 // throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection"); 335 336 // // BasicPermission bp = (BasicPermission) permission; 337 338 // // make sure we only add new BasicPermissions of the same class 339 // // Also check null for compatibility with deserialized form from 340 // // previous versions. 341 // // if (permClass is null) { 342 // // // adding first permission 343 // // permClass = bp.getClass(); 344 // // } else { 345 // // if (bp.getClass() != permClass) 346 // // throw new IllegalArgumentException("invalid permission: " ~ 347 // // permission); 348 // } 349 350 // synchronized (this) { 351 // perms.put(bp.getCanonicalName(), permission); 352 // } 353 354 // // No sync on all_allowed; staleness OK 355 // if (!all_allowed) { 356 // if (bp.getCanonicalName().equals("*")) 357 // all_allowed = true; 358 // } 359 } 360 361 /** 362 * Check and see if this set of permissions implies the permissions 363 * expressed in "permission". 364 * 365 * @param permission the Permission object to compare 366 * 367 * @return true if "permission" is a proper subset of a permission in 368 * the set, false if not. 369 */ 370 override bool implies(Permission permission) { 371 if (typeid(permission) != typeid(BasicPermission)) 372 return false; 373 374 implementationMissing(); 375 376 // BasicPermission bp = cast(BasicPermission) permission; 377 378 // // random subclasses of BasicPermission do not imply each other 379 // if (bp.getClass() != permClass) 380 // return false; 381 382 // // short circuit if the "*" Permission was added 383 // if (all_allowed) 384 // return true; 385 386 // // strategy: 387 // // Check for full match first. Then work our way up the 388 // // path looking for matches on a.b..* 389 390 // string path = bp.getCanonicalName(); 391 // //System.out.println("check "+path); 392 393 // Permission x; 394 395 // synchronized (this) { 396 // x = perms.get(path); 397 // } 398 399 // if (x !is null) { 400 // // we have a direct hit! 401 // return x.implies(permission); 402 // } 403 404 // // work our way up the tree... 405 // int last, offset; 406 407 // offset = path.length-1; 408 409 // while ((last = path.lastIndexOf(".", offset)) != -1) { 410 411 // path = path.substring(0, last+1) ~ "*"; 412 // //System.out.println("check "+path); 413 414 // synchronized (this) { 415 // x = perms.get(path); 416 // } 417 418 // if (x !is null) { 419 // return x.implies(permission); 420 // } 421 // offset = last -1; 422 // } 423 424 // we don't have to check for "*" as it was already checked 425 // at the top (all_allowed), so we just return false 426 return false; 427 } 428 429 /** 430 * Returns an enumeration of all the BasicPermission objects in the 431 * container. 432 * 433 * @return an enumeration of all the BasicPermission objects. 434 */ 435 override Enumeration!Permission elements() { 436 // Convert Iterator of Map values into an Enumeration 437 synchronized (this) { 438 return Collections.enumeration(perms.values()); 439 } 440 } 441 442 // Need to maintain serialization interoperability with earlier releases, 443 // which had the serializable field: 444 // 445 // @serial the Hashtable is indexed by the BasicPermission name 446 // 447 // private Hashtable permissions; 448 /** 449 * @serialField permissions java.util.Hashtable 450 * The BasicPermissions in this BasicPermissionCollection. 451 * All BasicPermissions in the collection must belong to the same class. 452 * The Hashtable is indexed by the BasicPermission name; the value 453 * of the Hashtable entry is the permission. 454 * @serialField all_allowed bool 455 * This is set to {@code true} if this BasicPermissionCollection 456 * contains a BasicPermission with '*' as its permission name. 457 * @serialField permClass java.lang.Class 458 * The class to which all BasicPermissions in this 459 * BasicPermissionCollection belongs. 460 */ 461 // private static final ObjectStreamField[] serialPersistentFields = { 462 // new ObjectStreamField("permissions", Hashtable.class), 463 // new ObjectStreamField("all_allowed", Boolean.TYPE), 464 // new ObjectStreamField("permClass", Class.class), 465 // }; 466 467 // /** 468 // * @serialData Default fields. 469 // */ 470 // /* 471 // * Writes the contents of the perms field out as a Hashtable for 472 // * serialization compatibility with earlier releases. all_allowed 473 // * and permClass unchanged. 474 // */ 475 // private void writeObject(ObjectOutputStream out) { 476 // // Don't call out.defaultWriteObject() 477 478 // // Copy perms into a Hashtable 479 // Hashtable!(string, Permission) permissions = 480 // new Hashtable<>(perms.size()*2); 481 482 // synchronized (this) { 483 // permissions.putAll(perms); 484 // } 485 486 // // Write out serializable fields 487 // ObjectOutputStream.PutField pfields = out.putFields(); 488 // pfields.put("all_allowed", all_allowed); 489 // pfields.put("permissions", permissions); 490 // pfields.put("permClass", permClass); 491 // out.writeFields(); 492 // } 493 494 // /** 495 // * readObject is called to restore the state of the 496 // * BasicPermissionCollection from a stream. 497 // */ 498 // private void readObject(java.io.ObjectInputStream in) 499 // , ClassNotFoundException 500 // { 501 // // Don't call defaultReadObject() 502 503 // // Read in serialized fields 504 // ObjectInputStream.GetField gfields = in.readFields(); 505 506 // // Get permissions 507 // // writeObject writes a Hashtable!(string, Permission) for the 508 // // permissions key, so this cast is safe, unless the data is corrupt. 509 // @SuppressWarnings("unchecked") 510 // Hashtable!(string, Permission) permissions = 511 // (Hashtable!(string, Permission))gfields.get("permissions", null); 512 // perms = new HashMap!(string, Permission)(permissions.size()*2); 513 // perms.putAll(permissions); 514 515 // // Get all_allowed 516 // all_allowed = gfields.get("all_allowed", false); 517 518 // // Get permClass 519 // permClass = (Class<?>) gfields.get("permClass", null); 520 521 // if (permClass is null) { 522 // // set permClass 523 // Enumeration<Permission> e = permissions.elements(); 524 // if (e.hasMoreElements()) { 525 // Permission p = e.nextElement(); 526 // permClass = p.getClass(); 527 // } 528 // } 529 // } 530 }