As part of the software I’m working on, I need user-based permissions. It’s a servlet based app, and each HTTP request represents an operation that occurs under the context of a particular user. I found learning how to use Java’s security support somewhat confusing, and the documentation I found with Google wasn’t entirely sufficient.
Part of the confusion stems from the legacy aspect of Java’s design. At one point, there was no concept of permissions for the purpose of a single operation, only the security permissions for the entire VM. e.g. whether the VM was permitted to write to a particular file. I went down a few dead ends reading the wrong documentation or learning about the wrong class. I also feel the current design is somewhat awkward due to the need to support the old way of doing things.
In the end, I determined the relevant classes were AccessControlContext and ProtectionDomain. Reading up about JAAS, which handles user authentication and association of permissions with a user, was useful, too. I didn’t use JAAS, as my user management system is custom and very simple; it was easier to do a custom work rather than implement the necessary interfaces to represent my user management system.
For each operation that needs to occur under a special context, I need to create an AccessControlContext. There are a few ways to get one: through AccessController to get the current thread’s context, and two different AccessControlContext constructors. One of the constructors involves the use of a DomainCombiner, which allows you to “merge” AccessControlContexts. The other constructor lets you specify the ProtectionDomains specifically.
ProtectionDomain gave me a bit of a headache, as I couldn’t figure out how to create one that had just the permissions I wanted. What’s not clear for the documentation for the ProtectionDomain constructors is that the CodeSource can be null. My epiphany was realizing that ProtectionDomain was the same as the “grant” sections of a java policy file. Like the “grant” sections, ProtectionDomains apply to those classes that come from a particular code source (e.g. a JAR or URL).
For awhile, I thought I had to obtain the current thread’s context and somehow copy it or merge it (with a combiner) to create a new AccessControlContext. This wasn’t the case, and as far as I can figure out, the combiners are present mainly so that JAAS can merge the user-specific ProtectionDomain with the “base” system ProtectionDomain once the user authenticates.
Putting it all together, an AccessControlContext allows us to execute code under a special context. An AccessControlContext itself does not have permissions associated with it. Instead, it is composed of ProtectionDomains which have permissions that only apply to security checks originating from certain classes, based on where the classes was loaded from (i.e. security check requests from a particular codebase). So to achieve what I want , I needed to create a ProtectionDomain that applied to all classes (null CodeSource). I’ll have to figure out which permissions the VM needs (e.g. file access), in addition to creating my own custom set of Permission implementations to represent my app-specific operations.
I do wonder if I’m missing an easier way of accomplishing this, but it took awhile to figure out even this much, so I’m not inclined to keep looking now that I have a reasonable solution.
The code I came up with looks like this:
Permissions permissions = new Permissions(); // TODO populate the permissions collection. // TODO shouldn't be giving all permissions permissions.add(new AllPermission()); ProtectionDomain userDomain = new ProtectionDomain(null, permissions); AccessControlContext userContext = new AccessControlContext(new ProtectionDomain[] {userDomain} ); try { opDocument = ( Document ) AccessController.doPrivileged( new PrivilegedExceptionAction () { public Document run() throws Exception { return command.execute(); } }, userContext); } catch (PrivilegedActionException pae) { throw pae.getException(); }