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 }