All Permissions
5.1 The CodeSource Class
5.2.4 Permission Collections
The access controller depends upon the ability to aggregate permissions so that it can easily call the implies( ) method on all of them. For example, a particular user might be given permission to read
several directories: perhaps the user's home directory (/home/sdo/−) and the system's temporary directory (/tmp/−). When the access controller needs to see if the user can access a particular file, it must test both of these permissions to see if either one matches. This can be done easily by aggregating all the file permissions into a single permission collection.
Every permission class is required to implement a permission collection, then, which is a mechanism where objects of the same permission class may be grouped together and operated upon as a single unit. This requirement is enforced by the newPermissionCollection( ) method of the Permission class.
The PermissionCollection class (java.security.PermissionCollection) is defined as follows:
public abstract class PermissionCollection
Implement an aggregate set of permissions. While permission collections can handle heterogeneous sets of permissions, a permission collection typically should be used to group together a homogeneous group of permissions (e.g., all file permissions or all socket permissions, etc.).
There are three basic operations that you can perform on a permission collection:
public abstract void add(Permission p)
Add the given permission to the permission collection.
public abstract boolean implies(Permission p)
Check to see if any permission in the collection implies the given permission. This can be done by enumerating all the permission objects that have been added to the collection and calling the implies( ) method on each of those objects in turn, but it is typically implemented in a more efficient manner.
public abstract Enumeration elements( )
Return an enumeration of all the permissions in the collection.
The definition of this class seems to imply that permission collections can contain any set of arbitrary permissions (and some versions of the Java documentation state that explicitly). Forget that idea; introducing that notion into permission collections vastly complicates matters, and the issue of a heterogeneous collection of permission objects is better handled elsewhere (we'll see how a little bit later). As far as we're concerned, the purpose of a permission collection is to aggregate only permission objects of a particular type.
Permission collections are typically implemented as inner classes, or at least as classes that are private to the package in which they are defined. There is, for example, a corresponding permission collection class for the FilePermission class, one for the SocketPermission class, and so on.
None of these collections is available as a public class that we can use in our own program. Hence, in order to support the newPermissionCollection( ) method in our XYZPayrollPermission class, we'd need to do something like this:
package javasec.samples.ch05;
import java.util.*;
import java.security.*;
import java.util.*;
public class XYZPayrollPermissionCollection
extends PermissionCollection { private Hashtable permissions;
// We keep track of whether the * name has been added to make // the implies method simpler.
private boolean addedAdmin;
private int adminMask;
XYZPayrollPermissionCollection( ) { permissions = new Hashtable( );
throw new IllegalArgumentException("Read only collection");
// This is a homogenous collection, as are all // PermissionCollections that you'll implement.
if (!(p instanceof XYZPayrollPermission))
throw new IllegalArgumentException("Wrong type");
XYZPayrollPermission xyz = (XYZPayrollPermission) p;
String name = xyz.getName( );
XYZPayrollPermission other =
(XYZPayrollPermission) permissions.get(name);
if (other != null)
xyz = merge(xyz, other);
// An administrative permission. The administrative permission // may have only view or only update or both, and multiple
public boolean implies(Permission p) {
if (!(p instanceof XYZPayrollPermission)) return false;
XYZPayrollPermission xyz = (XYZPayrollPermission) p;
// If the admin name is present, then all names are implied; // permissions; we merge the action lists and only store the
// name once.
private XYZPayrollPermission merge(XYZPayrollPermission a, XYZPayrollPermission b) { String aAction = a.getActions( );
if (aAction.equals("")) return b;
String bAction = b.getActions( );
if (bAction.equals("")) return a;
return new XYZPayrollPermission(a.getName( ), aAction + "," + bAction);
} }
Note the logic within the implies( ) method −− it's the important part of this example. The implies(
) method must test each permission in the hashtable (or whatever other container you've used to store the added permissions), but it should do so efficiently. We could always call the implies( ) method of each entry in the hashtable, but that would clearly not be efficient −− it's better to call only the implies( ) method on a permission in the table that has a matching name.
The only trick is that we won't find a matching name if we're doing wildcard pattern matching −− if we've added the name "*" to the table, we'll always want to return true, even though looking up the name "John Smith" in the table will not return the administrative entry. Implementing this wildcard pattern matching efficiently is the key to writing a good permission collection.
When you use (or subclass) one of the concrete permission classes that we listed in Chapter 2, there is no need to provide a permission collection class −− all concrete implementations provide their own collection. In addition, there are two other cases when you do not need to implement a permission collection:
When you extend the Permission class, but do not do wildcard pattern matching.
Hidden internally within the Java API is a PermissionsHash class, which is the default permission collection class for permission objects. The PermissionsHash class stores the aggregated permissions in a hashtable, so the implementations of its add( ) and elements( ) methods are straightforward. The implementation of its implies( ) method is based on looking up the name of the permission parameter in the hashtable collection: if an entry is found, then the
implies( ) method is called on that entry.
•
When you extend the BasicPermission class and do not provide support for actions.
The newPermissionClass( ) method of the BasicPermission class will provide a permission collection that handles wildcard pattern matching correctly (and efficiently).
•
If you implement your own PermissionCollection class, you must keep track of whether it has been marked as read−only. There are two methods involved in this:
public boolean isReadOnly( )
Return an indication of whether the collection has been marked as read−only.
public void setReadOnly( )
Set the collection to be read−only. Once the read−only flag has been set, it cannot be cleared: the collection will remain read−only forever.
A permission collection is expected to throw a security exception from its add( ) method if it has been marked as read−only. Note the read−only instance variable is private to the PermissionCollection class, so subclasses will have to rely on the isReadOnly( ) method to test its value.