Skip to main content

Extend JAAS for class instance-level authorization

Build more dynamic, flexible, and scalable enterprise applications

Carlos Fonseca (cafonseca@us.ibm.com), Software Engineer, IBM, Software Group
Currently a software engineer for IBM Research, Carlos Fonseca has been developing software professionally for 10 years. He started programming in C and Visual Basic, then moved on to C++ programming. For the last six years Carlos has focused on object-oriented design and development using the Java platform, lending his expertise to projects that range from Swing GUI stand-alone applications to server-side applications using J2EE. When not developing software professionally, Carlos also enjoys programming as a hobby. You can contact Carlos at cafonseca@us.ibm.com.

Summary:  The Java Authentication and Authorization Service (JAAS) is an extension to the Java 2 SDK. Under JAAS, a user or service may be given specific permissions to execute code in a Java class. In this article, software engineer Carlos Fonseca shows you how to extend the JAAS framework for the enterprise. Adding class instance-level authorization and special relationships to the JAAS framework lets you build more dynamic, flexible, and scalable enterprise applications.

Date:  01 Apr 2002
Level:  Intermediate
Activity:  3615 views

Most Java applications require some sort of class instance-level access control. For example, the specification for a Web-based, self-service auction application might have the following requirement:

Any registered (authenticated) user can create an auction but only the user who created the auction may modify it.

This means that any user can execute the code written to create an Auction class instance, but only the user who owns that instance may execute code designed to modify it. Usually, the user who created the Auction instance will be the owner. This is referred to as the class instance owner relationship.

Another requirement for the application could be:

Any user can create a bid for an auction and the owner of the auction may accept or reject any bid.

Again, any user can execute the code written to create an instance of a Bid class, but only a user who owns that instance will be given permission to modify it. Furthermore, the owner of the Auction class instance must be able to modify the acceptance flag in the related Bid class instances. This means there is a relationship, known as a special relationship, between the Auction instance and the corresponding Bid instances.

Unfortunately, the Java Authentication and Authorization Service (JAAS), which is part of the Java 2 platform, does not allow for class instance-level access control or special relationships. In this article, we will extend the JAAS framework to include both. The driving motivation behind this extension is to allow us to separate access control into a generalized framework that uses policies based on ownership and special relationships. These policies may then be changed by an administrator over the lifespan of an application.

Before we dive into extending the JAAS framework, we'll review the Java 2 platform's access control mechanisms. We'll discuss the use of policy files and permissions, and also talk about the relationship between the SecurityManager and the AccessController.

Access control in the Java 2 platform

Under the Java 2 platform, all code, whether it is local or remote, can be controlled by a policy. A policy is defined by a set of permissions for code in various locations, by various signers, or by both. A permission permits access to a resource; it is defined by a name and may be associated with certain actions.

The abstract class java.security.Policy is used to represent a security policy for an application. The default implementation is provided by the sun.security.provider.PolicyFile, in which the policies are defined in a file. Listing 1 is an example of a typical policy file:


Listing 1. A typical policy file
// Grant these permissions to code loaded from a sample.jar file
// in the C drive and if it is signed by XYZ
grant codebase "file:/C:/sample.jar", signedby "XYZ" {
	// Allow socket actions to any host using port 8080
	permission java.net.SocketPermission "*:8080", "accept, connect, 
	  listen, resolve";
	// Allows file access (read, write, execute, delete) in
	// the user�s home directory.
	Permission java.io.FilePermission "${user.home}/-", "read, write, 
	  execute, delete";
};

SecurityManager versus AccessController

In the standard JDK distribution, the mechanism to control access to code source is turned off by default. Prior to the Java 2 platform, access to code source was managed by the SecurityManager class. The SecurityManager is enabled by the java.security.manager system property, as shown here:

java -Djava.security.manager

Under the Java 2 platform, an application can be set to use the java.lang.SecurityManager class or the java.security.AccessController class to manage sensitive operations. The AccessController is new in the Java 2 platform. For backward compatibility, the SecurityManager class still exists but defers its decisions to the AccessController class. Both the SecurityManager and the AccessController use an application's policy file to determine whether a requested operation will be allowed. Listing 2 shows how the AccessController processes a SocketPermission request:


Listing 2. Protecting sensitive operations
Public void someMethod() {
	Permission permission = 
	  new java.net.SocketPermission("localhost:8080", "connect");
	AccessController.checkPermission(permission);
	// Sensitive code starts here
	Socket s = new Socket("localhost", 8080);
}

In this example, we see the AccessController checking the application's current policy implementation. If any permission defined in the policy file implies the requested permission, the method will simply return; otherwise an AccessControlException will be thrown. The check is actually redundant in this example, because the constructor for the default socket implementation performs the same check.

In the next section, we'll look more closely at how the AccessController works with the java.security.Policy implementation to securely process application requests.


The AccessController at work

A typical checkPermission(Permission p) method call on the AccessController class might result in the following sequence:

  1. The AccessController invokes the getPermissions(CodeSource codeSource) method of the java.security.Policy class implementation.

  2. The getPermissions(CodeSource codeSource) method returns a PermissionCollection class instance, which represents a collection of the same type of permissions.

  3. The AccessController calls the implies(Permission p) method of the PermissionCollection class.

  4. In turn, the PermissionCollection calls the implies(Permission p) method of the individual Permission objects contained in the collection. These methods return true if the current permission object in the collection implies the specified permission, false if it does not.

Now, let's look at the important elements of this control-access sequence in more detail.

The PermissionCollection class

Most permission class types have a corresponding PermissionCollection class. An instance of such a collection may be created by calling the newPermissionCollection() method defined by the Permission subclass implementation. The getPermissions() method of the java.security.Policy class implementation may also return a Permissions class instance, a subclass of PermissionCollection. This class represents a collection of different types of permission objects organized by PermissionCollection. The implies(Permission p) method of the Permissions class may call the implies(Permission p) method of the individual PermissionCollection classes.

CodeSource and the ProtectionDomain class

The combination of permissions and CodeSource (code location and the certificates that were used to verify the signed code) are encapsulated in the ProtectionDomain class. Class instances that have the same permissions and the same CodeSource are placed in the same domain. Classes with the same permissions but different CodeSources are placed in different domains. A class can only belong to one ProtectionDomain. To obtain the ProtectionDomain for an object, use the getProtectionDomain() method defined in the java.lang.Class class.

Permissions

Assigning permissions to CodeSources does not necessarily mean the implied operations will be allowed. Each class in the calling stack must have the required permissions for the operations to complete successfully. In other words, if you assign a java.io.FilePermission to class B, and class B is called by class A, then class A must also have the same permission or a permission that implies java.io.FilePermission.

On the other hand, a calling class may need temporary permissions to complete an operation in another class that has those permissions. For example, a class load from another location may not be trusted for access to the local filesystem. Classes loaded locally, however, are given read permissions for a certain directory. These classes may implement the PrivilegedAction interface to give the calling classes permission to complete the specified operations. The check of the calling stack stops when it encounters a PrivilegedAction instance, effectively granting all subsequent class calls the required permission to perform the specified operation.


Working with JAAS

As suggested by its name, JAAS consists of two primary components: authentication and authorization. We're concerned mostly with extending the authorization component of JAAS, but we'll start with a brief overview of JAAS authentication, followed by a look at a simple JAAS authorization operation.

User authentication in JAAS

JAAS augments the access control security model defined in Java 2 by adding subject-based policies. Permission is granted not only based on the CodeSource but also on the user executing the code. Obviously, for this model to work each user must be authenticated.

JAAS's authentication mechanism is built on a set of pluggable login modules. The JAAS distribution contains several LoginModule implementations. LoginModules may be used to prompt the user for a userid and password. The LoginContext class uses a configuration file to determine which LoginModule to use to authenticate the user. This configuration may be specified through the system property java.security.auth.login.config. An example configuration is:

java -Djava.security.auth.login.config=login.conf

And here's how a login configuration file might look:

Example {
  com.ibm.resource.security.auth.LoginModuleExample required 
    debug=true userFile="users.xml" groupFile="groups.xml";
};

Know your principles

The Subject class is used to encapsulate the credentials of an authenticated entity such as a user. A Subject may have a grouping of identities called principals. For example, if the Subject is a user, the user's name and social security number may be some of the Subject's identities, or principals. Principals are associated with the identity names.

The Principal implementation class along with its name is specified in the JAAS policy file. The default JAAS implementation uses a policy file similar to that of the Java 2 implementation, except that each grant statement must be associated with at least one principal. The javax.security.auth.Policy abstract class is used to represent the JAAS security policy. The default implementation is provided by the com.sun.security.auth.PolicyFile in which the policies are defined in a file. Listing 3 is an example of a JAAS policy file:


Listing 3. Example JAAS policy file
// Example grant entry
grant codeBase "file:/C:/sample.jar", signedby "XYZ",
  principal com.ibm.resource.security.auth.PrincipalExample "admin" {
    // Allow socket actions to any host using port 8080
    permission java.net.SocketPermission 
      "*:8080", "accept, connect, listen, resolve";
    // Allows file access (read, write, execute, delete) in
    // the user's home directory.
    Permission java.io.FilePermission 
      "${user.home}/-", "read, write, execute, delete";
};

This example is similar to the standard Java 2 policy file shown in Listing 1. In fact, the only difference is the principal statement, which states that only subjects (users) that have the specified principal and principal name are granted the specified permissions.

Again, a system property, java.security.auth.policy, is used to indicate where the JAAS policy file resides, as shown below:

java -Djava.security.auth.policy=policy.jaas

The Subject class contains several methods to perform work as a particular subject; these methods are as follows:

public static Object 
  doAs(Subject subject, java.security.PrivilegedAction action)
public static Object 
  doAs(Subject subject, java.security.PrivilegedAction action)
  throws java.security.PrivilegedActionException	

Note that sensitive code is protected in the same way described in the Java 2 CodeSource Access Control overview. See the Resources section to learn more about code source access control and authentication in JAAS.


Authorization in JAAS

Listing 4 shows the results of an authorization request using the JAAS policy file shown in Listing 3. Assume that a SecurityManager has been installed and a Subject with a com.ibm.resource.security.auth.PrincipalExample principal named "admin" has been authenticated by the loginContext.


Listing 4. A simple authorization request
public class JaasExample {
	public static void main(String[] args) {
		...
		// where authenticatedUser is a Subject with
		// a PrincipalExample named admin.
		Subject.doAs(authenticatedUser, new JaasExampleAction());
		...
	}
}

public class JaasExampleAction implements PrivilegedAction {
	public Object run() {
		FileWriter fw = new FileWriter("hi.txt");
		fw.write("Hello, World!");
		fw.close();
	}
}	

Here the sensitive code is encapsulated in the JaasExampleAction class. Also note that the calling classes do not require the permissions granted to the JaasExampleAction class code source, because it implements a PrivilegedAction.


Extending JAAS

Most applications have custom logic that authorizes users to perform an operation not only on a class, but also on an instance of that class. Such authorization is usually based on a relationship between the user and the instance. This is where JAAS falls a little short. Fortunately, however, JAAS was designed so that it can be extended. With a little bit of work, we can extend JAAS to include a generalized class instance-level authorization framework.

As I stated at the beginning of this article, the abstract class javax.security.auth.Policy is used to represent the JAAS security policy. Its default implementation is provided by the com.sun.security.auth.PolicyFile class. The PolicyFile class reads the policies from a JAAS-formatted file like the one shown in Listing 3.

We need to add one thing to this file to extend the policy definition for class instance-level authorization: an optional relationship parameter associated with a permission statement.

The default JAAS permission statement has the following format:

permission <permission implementation class> [name], [actions];	

We add the optional relationship parameter to the end of this permission statement to complete the policy definition. Here's the format for the new permission statement:

permission <permission implementation class> 
  [name], [actions], [relationship];


The most important thing to note when extending JAAS for class instance-level authorization is that the permission implementation class must have a three-parameter constructor. The first parameter is for the name, the second for the actions, and the last for the relationship.

Parsing the new file format

Since the format of the file has changed, a new javax.security.auth.Policy subclass is needed to parse the file.

For simplicity, our example employs the new javax.security.auth.Policy subclass, com.ibm.resource.security.auth.XMLPolicyFile, to read the policies from an XML file. In a real-world enterprise application, a relational database would be better suited to handle this task.

The easiest way to replace the default JAAS access control policy implementation with the XMLPolicyFile class is by adding the auth.policy.provider=com.ibm.resource.security.auth.XMLPolicyFile entry to the java.security properties file. The java.security properties file is located in the lib/security directory of the Java 2 platform runtime. Listing 5 is a sample XML policy file used with the XMLPolicyFile class:


Listing 5. An XML policy file
<?xml version="1.0"?>
<policy>
    <grant codebase="file:/D:/sample_actions.jar">
      <principal classname=
        "com.ibm.resource.security.auth.PrincipalExample" name="users">
        <permission classname=
          "com.ibm.resource.security.auth.ResourcePermission"
          name="com.ibm.security.sample.Auction"
          actions="create" />
        <permission classname=
         "com.ibm.resource.security.auth.ResourcePermission"
          name="com.ibm.security.sample.Auction"
          actions="read" />
        <permission classname=
         "com.ibm.resource.security.auth.ResourcePermission"
          name="com.ibm.security.sample.Auction"
          actions="write"
          relationship="owner" />
        <permission classname=
         "com.ibm.resource.security.auth.ResourcePermission"
          name="com.ibm.security.sample.Bid"
          actions="create" />
        <permission classname=
         "com.ibm.resource.security.auth.ResourcePermission"
          name="com.ibm.security.sample.Bid"
          actions="read" />
        <permission classname=
         "com.ibm.resource.security.auth.ResourcePermission"
          name="com.ibm.security.sample.Bid"
          actions="write"
          relationship="owner" />
        <permission classname=
         "com.ibm.resource.security.auth.ResourcePermission"
          name="com.ibm.security.sample.Bid"
          actions="accept"
          relationship="actionOwner" />
    </principal>
  </grant>
</policy>	

In this example policy file, any user (Subject) associated with the PrincipalExample named users can create and read an Action.class instance. However, only the user that created the instance can update (write) it. This is defined by the third permission element, which contains the relationship attribute value of owner. The same holds true for the Bid.class instances, except that the owner of the corresponding Auction.class instance can change the bid acceptance flag.

The Resource interface

Classes that require class instance-level access control must implement the Resource interface. The getOwner() method of the interface returns the owner of the class instance. The fulfills(Subject subject, String relationship) method is used for dealing with special relationships. In addition, these classes use the com.ibm.resource.security.auth.ResourcePermission class to protect sensitive code. For example, the Auction class has the following constructor:

public Auction() {
  Permission permission = 
    new ResourcePermission("com.ibm.security.sample.Auction", "create");
  AccessController.checkPermission(permission);
	}	

The owner relationship

The implies(Permission p) method of the ResourcePermission class is the key to this framework. The implies() method compares the name and actions properties for equality. If a relationship is defined, then the class instance (Resource) being protected must be passed into the ResourcePermission constructor. The ResourcePermission class understands the owner relationship. It compares the owner of the class instance with the subject (user) executing the code. Special relationships are delegated to the fulfills() method of the class being protected.

For example, in the XML policy file shown in Listing 5, only the owner of the Auction class instance can update (write) the file. The setter methods of the class use the protection code shown in Listing 6:


Listing 6. The implies(Permission) method at work
public void setName(String newName) {
  Permission permission = 
new ResourcePermission("com.ibm.security.sample.Auction", "write", this);
  AccessController.checkPermission(permission);
  // sensitive code
  this.name = newName;
}

The this reference that is passed into the ResourcePermission constructor represents the Resource interface that the Auction class implements. Because the relationship listed in the policy file is owner, the ResourcePermission class uses this reference to check if the current Subject (user) has a principal that matches the owner of the instance. If another relationship is specified, then the ResourcePermission class calls the fulfills(Subject subject, String relationship) method of the Auction class. It is up to the Resource implementing class to provide the logic in the fulfills() method.

The Bid class listed in the XML policy file has the methods shown in Listing 7 (assume the Bid class instance has a reference, auction, to the corresponding Auction class instance).


Listing 7. Handling special relationships
public void setAccepted(boolean flag) {
  Permission permission = 
new ResourcePermission("com.ibm.security.sample.Auction", "accept", this);
  AccessController.checkPermission(permission);
  // sensitive code
  this.accepted = flag;
  }
	
public boolean fulfills(Subject user, String relationship) {
  if( relationship.equalsIgnoreCase("auctionOwner") ) {
    String auctionOwner = auction.getOwner();
    Iterator principalIterator = user.getPrincipals().iterator();
    while(principalIterator.hasNext()) {
      Principal principal = (Principal) principalIterator.next();
      if( principal.getName().equals(auctionOwner) )
        return true;
    }
  }
  return false;
}

The relationship string passed in to the fulfills() method is the relationship listed in the policy file. In this case, the "auctionOwner" string was used.

By default, the XMLPolicyFile class looks for a file named ResourcePolicy.xml in the current working directory. The system property, com.ibm.resource.security.auth.policy, may be used to specify a different file name and location.

WebSphere Application Server example

In addition to the command-line example, you may want to run this simple program, which is optimized for the IBM WebSphere Application Server, version 4.0.2.


A working example

To pull all of this information together, we'll run a simple command-line example. The example program contains three jar files:

  • resourceSecurity.jar
  • example.jar
  • exampleActions.jar

The resourceSecurity.jar file contains the JAAS extension framework that allows instance-level access control. It also contains a LoginModuleExample class that reads user authentication information from an XML file. Userids and passwords are stored in the users.xml file. User groups are stored in the groups.xml file. See the Resources section for more information on LoginModuleExample.

The example contains four additional files:

  • login.conf
  • policy
  • resourcePolicy.xml
  • run.bat

Be sure you update the file paths in the run.bat, policy, and resourcePolicy.xml files before attempting to run the example. By default all passwords are "passw0rd".

How the example works

The example program prompts for a userid and password. It checks the supplied userid and password with the entries in the users.xml file. After the user is authenticated, the program tries to create a UserProfile class instance, modify it, and read from it. By default the owner of the UserProfile class is Jane (jane). When Jane logs in, all three operations are successful. When John (john) logs in, only the create operation is successful. When Lou (lou), Jane's manager, logs in, only the first and last operations are successful. When the system administrator (admin) logs in, all operations are successful. Of course, all this will only be true if the supplied ResourcePolicy.xml file has not been modified.

Example setup
The following setup instructions assume you are using JDK 1.3 and have extracted the files to the d:\JaasExample directory. You will save some work by extracting the files to this directory; otherwise you will have to modify the policy and the ResourceSecurity.xml policy files with the correct path names.

Here's what you need to do to run the example:

  1. Download the source files for this example.

  2. Copy the jaas.jar and the jaasmod.jar to your JDK jre\lib\ext directory (i.e., D:\JDK1.3\jre\lib\ext).

  3. Add the following string to the end of the java.security file located in JDK's jre\lib\security directory (i.e., D:\JDK1.3\jre\lib\security): auth.policy.provider=com.ibm.resource.security.auth.XMLPolicyFile.

  4. Execute the run.bat file.

Conclusion

Class instance-level authorization separates access control into a generalized framework that uses policies based on ownership and special relationships. These policies may then be changed by an administrator over the lifespan of an application. Extending the JAAS in this way decreases the chances that you or another programmer will have to rewrite the code as the business rules change over the lifespan of the application.

The special-relationship concept can be further extended by abstracting the relationship string to a class. Instead of calling the fulfills(Subject user, String relationship) method of the Resource implementation class, you would simply call a new fulfills(Subject user, Resource resource) method defined in the Relationship implementation class. This would allow many Resource implementation classes to use the same relationship logic.



Download

NameSizeDownload method
j-jaas/standaloneExample.zip2MB FTP

Information about download methods


Resources

About the author

Currently a software engineer for IBM Research, Carlos Fonseca has been developing software professionally for 10 years. He started programming in C and Visual Basic, then moved on to C++ programming. For the last six years Carlos has focused on object-oriented design and development using the Java platform, lending his expertise to projects that range from Swing GUI stand-alone applications to server-side applications using J2EE. When not developing software professionally, Carlos also enjoys programming as a hobby. You can contact Carlos at cafonseca@us.ibm.com.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=10648
ArticleTitle=Extend JAAS for class instance-level authorization
publish-date=04012002
author1-email=cafonseca@us.ibm.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers