Using the Permission Class

public int hashCode { We must always provide a hash code for permissions, because the hashes must match if the permissions compare as equals. The default implementation of this method wouldnt provide that. return getName.hashCode mask; } public String getActions { This method must return the same string, no matter how the action list was passed to the constructor. if mask == 0 return ; else if mask == VIEW return view; else if mask == UPDATE return update; else if mask == VIEW | UPDATE return view, update; else throw new IllegalArgumentExceptionUnknown mask; } public PermissionCollection newPermissionsCollection { More about this in a later example. return new XYZPayrollPermissionCollection ; } public static void mainString[] args { XYZPayrollPermission p1 = new XYZPayrollPermissionsdo, view; XYZPayrollPermission p2 = new XYZPayrollPermissionargs[0], args[1]; System.out.printlnP1 is + p1; System.out.printlnP2 is + p2; System.out.printlnP1 − P2 is + p1.impliesp2; System.out.printlnP2 − P1 is + p2.impliesp1; } } The instance variables in this class are required to hold the information about the actions −− even though our superclass makes references to actions, it doesnt provide a manner in which to store them or process them, so we have to provide that logic. That logic is provided in the parse method; weve chosen the common convention of having the action string treated as a list of actions that are separated by commas and whitespace. Note also that weve stored the actual actions as bits in a single integer −− this simplifies some of the later logic. As required, weve implemented the equals and hashCode methods −− and we have done so rather simply. We consider objects equal if their names are equal and their masks that is, their actions are equal, and construct a hash code accordingly. Our implementation of the getActions method is typical: were required to return the same action string for a permission object that was constructed with an action list of view, update as for one that was constructed with an action list of update, view . This requirement is one of the prime reasons why the actions are stored as a mask −− because it allows us to construct this action string in the proper format. Finally, the implies method is responsible for determining how wildcard and other implied permissions are handled. If the name passed to construct our object is an asterisk, then we match any other name; hence, an object to represent the permissions of the HR department might be constructed as: new XYZPayrollPermission, view, update When the implies method is called on this wildcard object, the name will always match, and because the action mask has the complete list of actions, the mask comparison will always yield the mask that were testing against. If the implies method is called with a different object, however, it will only return true if the names are equal and the objects mask is a subset of the target mask. Note that we also might have implemented the logic in such a way that permission to perform an update implies permission to perform a view simply by changing the logic of testing the mask −− youre not limited only to wildcard matching in the implies method.

5.2.3 The BasicPermission Class

If you need to implement your own permission class, the BasicPermission class java.security.BasicPermission provides some useful semantics. This class implements a basic permission −− that is, a permission that doesnt have actions. Basic permissions can be thought of as binary permissions −− you either have them or you dont. However, this restriction does not prevent you from implementing actions in your subclasses of the BasicPermission class as the PropertyPermission class does. The prime benefit of this class is the manner in which it implements wildcards. Names in basic permissions are considered to be hierarchical, following a dot−separated convention. For example, if the XYZ corporation wanted to create a set of basic permissions, they might use the convention that the first word of the permission always be xyz : xyz.readDatabase , xyz.writeDatabase , xyz.runPayrollProgram , xyz.HRDepartment.accessCheck , and so on. These permissions can then be specified by their full name, or they can be specified with an asterisk wildcard: xyz . would match each of these no matter what depth, and would match every possible basic permission. The wildcard matching of this class does not match partial names: xyz.read wouldnt match any of the permissions we just listed. Further, the wildcard must be in the rightmost position: .readDatabase wouldnt match any basic permission. The BasicPermission class is abstract, although it does not contain any abstract methods, and it completely implements all the abstract methods of the Permission class. Hence, a concrete implementation of the BasicPermission need only contain a constructor to call the correct constructor of the superclass since there is no default constructor in the BasicPermission class. Subclasses must call one of these constructors: public BasicPermissionString name Construct a permission with the given name. This is the usual constructor for this class, as basic permissions do not normally have actions. public BasicPermissionString name, String action Construct a permission with the given name and action. Even though basic permissions do not usually have actions associated with them, you must provide a constructor with this signature in all implementations of the BasicPermission class due to the mechanism that is used to construct permission objects from the policy file which we will see later in this chapter.

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 Chapter 5. The Access Controller several directories: perhaps the users home directory homesdo− and the systems 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 addPermission p Add the given permission to the permission collection. public abstract boolean impliesPermission 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 well see how a little bit later. As far as were 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, wed 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 ; addedAdmin = false; } public void addPermission p { Required test if isReadOnly throw new IllegalArgumentExceptionRead only collection; This is a homogenous collection, as are all PermissionCollections that youll implement. if p instanceof XYZPayrollPermission throw new IllegalArgumentExceptionWrong type; XYZPayrollPermission xyz = XYZPayrollPermission p; String name = xyz.getName ; XYZPayrollPermission other = XYZPayrollPermission permissions.getname; if other = null xyz = mergexyz, other; An administrative permission. The administrative permission may have only view or only update or both, and multiple admin permissions may be added, so the masks are OR−ed together. if name.equals { addedAdmin = true; adminMask = xyz.mask | adminMask; } permissions.putname, xyz; } public Enumeration elements { return permissions.elements ; } public boolean impliesPermission p { if p instanceof XYZPayrollPermission return false; XYZPayrollPermission xyz = XYZPayrollPermission p; If the admin name is present, then all names are implied; we need check only the permissions. if addedAdmin adminMask xyz.mask == xyz.mask return true; Otherwise, we can just see if the given individual is in our table and if so, see if the permissions match. Permission inTable = Permission permissions.getxyz.getName ; if inTable == null return false; return inTable.impliesxyz; } This is called when the same name is added twice to the permissions; we merge the action lists and only store the name once. private XYZPayrollPermission mergeXYZPayrollPermission a, XYZPayrollPermission b { String aAction = a.getActions ; if aAction.equals return b; String bAction = b.getActions ; if bAction.equals return a; return new XYZPayrollPermissiona.getName , aAction + , + bAction; } } Note the logic within the implies method −− its the important part of this example. The implies method must test each permission in the hashtable or whatever other container youve 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 −− its better to call only the implies method on a permission in the table that has a matching name. The only trick is that we wont find a matching name if were doing wildcard pattern matching −− if weve added the name to the table, well 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. 86