All Permissions
5.1 The CodeSource Class
5.2.2 Using the Permission Class
We'll now look into the classes upon which all these permissions are based: the Permission class. This class abstracts the notion of a permission and a name. From a programmatic standpoint, the Permission class is really used only to create your own types of permissions. It has some interesting methods, but the operations that are implemented on a permission object are not generally used in code that we write −− they are used instead by the access controller. Hence, we'll examine this class primarily with an eye towards understanding how it can be used to implement our own permissions.
Permission is an abstract class that contains these public methods:
public Permission(String name)
Construct a permission object that represents the desired permission.
public abstract boolean equals(Object o)
Subclasses of the Permission class are required to implement their own test for equality. Often this is simply done by comparing the name (and actions, if applicable) of the permission.
public abstract int hashCode( )
Subclasses of the Permission class are required to implement their own hash code. In order for the access controller to function correctly, the hash code for a given permission object must never change during execution of the virtual machine. In addition, permissions that compare as equal must return
the same hash code.
public final String getName( )
Return the name that was used to construct this permission.
public abstract String getActions( )
Return the canonical form of the actions (if any) that were used to construct this permission.
public String toString( )
The convention for printing a permission is to print in parentheses the class name, the name of the permission, and the actions. For example, a file permission might return:
("java.io.FilePermission","/myclasses/xyz/HRApplet.class","read")
public abstract boolean implies(Permission p)
This method is one of the keys of the Permission class: it is responsible for determining whether a class that is granted one permission is granted another. This method is normally responsible for performing wildcard matching so that, for example, the file permission /myclasses/− implies the file permission /myclasses/xyz/HRApplet.class. But this method need not rely on wildcards; permission to write a particular object in a database would probably imply permission to read that object as well.
public PermissionCollection newPermissionCollection( )
Return a permission collection suitable for holding instances of this type of permission. We'll discuss the topic of permission collections in the next section. This method returns null by default.
public void checkGuard(Object o)
Call the security manager to see if the permission (i.e., the this variable) has been granted,
generating a SecurityException if the permission has not been granted. The object parameter of this method is unused. We'll give more details about this method later in this chapter.
Implementing your own permission means providing a class with concrete implementations of these abstract methods. Note that the notions of wildcard matching and actions are not generally present in this class −− if you want your class to support either of these features, you're responsible for implementing all of the necessary logic to do so (although the BasicPermission class that we'll look at next can help us with that).
Say that you are implementing a program to administer payroll information. You'll want to create permissions to allow users to view their payroll history. You'll also want to allow the HR department to update the pay rate for employees. We'll need to implement a permission class to encapsulate all of that:
package javasec.samples.ch05;
import java.security.*;
import java.util.*;
public class XYZPayrollPermission extends Permission { protected int mask;
static private int VIEW = 0x01;
static private int UPDATE = 0x02;
public XYZPayrollPermission(String name) {
// Our permission must always have an action, so we // choose a default one here.
this(name, "view");
}
public XYZPayrollPermission(String name, String action) { // Our superclass, however, does not support actions // so we don't provide one to that.
super(name);
parse(action);
}
private void parse(String action) {
// Look in the action string for the words view and // update, separated by white space or by a comma
StringTokenizer st = new StringTokenizer(action, ",\t ");
mask = 0;
public boolean implies(Permission permission) {
if (!(permission instanceof XYZPayrollPermission)) return false;
XYZPayrollPermission p = (XYZPayrollPermission) permission;
String name = getName( );
XYZPayrollPermission p = (XYZPayrollPermission) o;
return ((p.getName().equals(getName( ))) && (p.mask == mask));
}
public int hashCode( ) {
else throw new IllegalArgumentException("Unknown mask");
}
public PermissionCollection newPermissionsCollection( ) { // More about this in a later example.
return new XYZPayrollPermissionCollection( );
}
public static void main(String[] args) { XYZPayrollPermission p1 =
The instance variables in this class are required to hold the information about the actions −− even though our superclass makes references to actions, it doesn't 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; we've 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 we've stored the actual actions as bits in a single integer −− this simplifies some of the later logic.
As required, we've 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: we're 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 we're 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 object's 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 −− you're not limited only to wildcard matching in the implies( ) method.