For a long time, the Java EE security system has had a quite cool, but woefully underused , feature; pluggable authorization modules. The authorization modules are provided by a specification called Java Authorization Contract for Containers, aka JACC

What Are Authorization Modules?

Simply put, authorization modules are a repository of permissions and overridable methods to check these permissions. These methods can be used by both the container (server) for authorization decisions as well as by application code.

Although extremely powerful in concept, the specification and API design left a few things to be desired. Arguably one of the worst problems is that an authorization module (officially called a "JACC provider") has to be installed for the entire Java EE server. Normally, it cannot be registered from or for a single application (authentication modules, on the contrary, can be installed per application). Another practical problem is that while Java EE servers are supposed to use JACC for all of their authorization decisions, actually few do by default. JACC can be activated on these servers, but without this explicit activation, the server will use a proprietary mechanism.

JACC in Payara Server

In Payara Server, however, JACC is the core of the authorization system. There's no parallel proprietary system, and therefore no need to switch between two systems. By extension, this means that security vulnerabilities and other bugs don't have to be fixed in multiple systems either, because the code surface for one such system is typically simply smaller than for multiple ones. This is even worse for vendors who swapped out their proprietary authorization system for another proprietary one, and are effectively maintaining three systems now: the Java EE standard one, their new proprietary one, and their legacy proprietary one.

Additionally, Payara Server does allow authorization modules to be installed from within an application and then only apply them for that single application. This does make use of a proprietary Payara API, so applications using this are not directly portable to other Java EE servers anymore. It's relatively easy to remove the usage of that API and then install the same authorization module on any other server using that server's proprietary method or tooling, though.

An example of installing an authorization module this way is shown below:

@WebListener public class JaccInstaller implements ServletContextListener { @Override public void contextInitialized ( ServletContextEvent sce ) { JaccConfigurationFactory . getJaccConfigurationFactory (). registerContextProvider ( getAppContextId ( sce . getServletContext ()), new TestPolicyConfigurationFactory (), new LoggingTestPolicy ()); } private String getAppContextId ( ServletContext servletContext ) { return servletContext . getVirtualServerName () + " " + servletContext . getContextPath (); } }

We use a simple WebListener here to install a custom authorization module. Such a listener can be easily removed if really needed even without recompiling the application. In it we obtain an instance of the "JaccConfigurationFactory", which we use to register our module for the current context (hence the method name "registerContextProvider").

The method takes three parameters; the application context Id, a regular JACC "PolicyConfigurationFactory" and a regular JACC "Policy".

The "application context Id" is an Id that uniquely identifies the application from which the call is being made. The PolicyConfigurationFactory is an object that is used to create another object that receives all permissions the container finds in the application. The Policy is the object that can use these collected permissions to make the actual authorization decisions.

Benefits of Custom Authorization Rules

So what can we actually do with custom authorization rules?

The answer to that is; quite a lot actually! For instance, we can have time-based roles. Say we have a role "Employee", which is only valid between 9 and 5. With an authorization rule we can add logic to programmatically check for the time, and if it's outside these hours cause false to be returned from "isCallerInRole" or "@RolesAllowed" checks all over the application.

We can also query (external) systems on the spot whether a user is in a specific group or role. This may sometimes be necessary when connecting to other security systems that only answer to "is user in role" queries, and can't return a list of all roles that a given user has (which would be needed for an authentication mechanism).

Although not readily available yet in either Java EE or Payara Server, the following high-level code is a conceptual sketch of what can be done when building on JACC:

@ApplicationScoped public class CustomAuthorizationMechanism implements AuthorizationMechanism { @Inject SomeService someService ; // Service doing role checks, may contact external system @Inject SecurityConstraints securityConstraints ; // All security constraints found in application @Override public Boolean preAuthorizeByRole ( Caller caller , Permission requestedPermission ) { return getRequiredRoles ( securityConstraints . getPerRolePermissions (), requestedPermission ) . stream () . anyMatch ( role -> someService . isInRole ( caller , role )); } }

Given the collection of required roles, we then check if the caller is in at least one of those roles by querying the injected service for each role. As we're expression the rule in plain Java code, we can basically do whatever we want here. For example, it would be trivial to change this rule to check that the caller is in all roles instead of any role.

The full source of installing an actual working authorization module can be found on GitHub, along with an automated test that demonstrates how the test application is supposed to be called: