Developing a Java Authorization Contract for Containers (JACC) Authorization Provider

You can develop a JACC provider to have custom authorization decisions for Java™ Platform, Enterprise Edition (J2EE) applications by implementing the com.ibm.wsspi.security.authorization.jacc.ProviderService interface that is provided in the Liberty server.

Before you begin

By default, the application module loading is deferred until the request to the application is being processed. However, the security constraint of the entire module in the application needs to be processed before the application is ready to be processed. The deferred module loading needs to be disabled. The following shows you how to disable it:
  1. For the WebContainer:
    In the server.xml file, the following element needs to be set:
    <webContainer deferServletLoad="false"/>
  2. For the EJBContainer:
    In the server.xml file, the following property needs to be set:
    <ejbContainer startEJBsAtAppStart="true"/>
    Note: If the previous elements are not set, the complete security constraint information might not be propagated to the third-party JACC provider upon starting the server. As a result, the correct authorization decision might not be enforced by the third-party JACC provider.

About this task

The Java Authorization Contract for Containers specification, JSR 115, defines an interface for authorization providers. In the Liberty server, you must package your JACC provider as a user feature. Your feature must implement the com.ibm.wsspi.security.authorization.jacc.ProviderService interface.

Procedure

  1. Create an OSGi component that provides a service that implements the com.ibm.wsspi.security.authorization.jacc.ProviderService interface.

    The ProviderService interface defines two methods. The getPolicy method, which the Liberty run time invokes to retrieve an instance of your Policy class that implements thejava.security.Policy abstract class. Also, the getPolicyConfigFactory method, which the Liberty run time invokes to retrieve an instance of your PolicyConfigurationFactory class that implements the javax.security.jacc.PolicyConfigurationFactory abstract class.

    The following example uses OSGi declarative services annotations:
    package com.mycompany.jacc;
    
    import com.mycompany.jacc.MyAuthConfigProvider;
    import com.ibm.wsspi.security.authorization.jacc.ProviderService;
    import java.security.Policy;
    import java.util.Map;
    import javax.security.jacc.PolicyConfigurationFactory;
    import org.osgi.service.component.ComponentContext;
    import org.osgi.service.component.annotations.Activate;
    import org.osgi.service.component.annotations.Component;
    import org.osgi.service.component.annotations.Deactivate;
    
    // The property value of javax.security.jacc.policy.provider which defines the implementation class of Policy and
    // javax.security.jacc.PolicyConfigurationFactory.provider which defines the implementation class of PolicyConfigurationFactory, are required for propagating the properties to the Liberty runtime.
    
    @Component(service = ProviderService.class,
      immediate = true,
      property = {
        "javax.security.jacc.policy.provider=com.myco.jacc.MyPolicy",
        "javax.security.jacc.PolicyConfigurationFactory.provider="
        + "com.myco.jacc.MyFactoryImpl"
      }
    )
    
    public class MyJaccProviderService implements ProviderService {
        Map<String, String> configProps;
    
        // This method called by the Liberty runtime
        // to get an instance of Policy class
        @Override
        public Policy getPolicy() {
            return new myPolicy();
        }
    
        // This method called by the Liberty runtime
        // to get an instance of PolicyConfigurationFactory class
        @Override
        public PolicyConfigurationFactory getPolicyConfigurationFactory() {
            ClassLoader cl = null;
            PolicyConfigurationFactory pcf = null;
            System.setProperty(
              "javax.security.jacc.PolicyConfigurationFactory.provider",
              "com.myco.jacc.MyFactoryImpl");
            try {
                cl = Thread.currentThread().getContextClassLoader();
                Thread.currentThread().setContextClassLoader(
                                  this.getClass().getClassLoader());
                pcf = PolicyConfigurationFactory.getPolicyConfigurationFactory();
            } catch (Exception e) {
                return null;
            } finally {
                Thread.currentThread().setContextClassLoader(cl);
            }
            return pcf;
        }
    
        @Activate
        protected void activate(ComponentContext cc) {
            // Read provider config properties here if needed,
            // then pass them to the Provider ctor.
            // This example reads the properties from the OSGi
            // component definition.
            configProps = (Map<String, String>) cc.getProperties();
        }
    
        @Deactivate
        protected void deactivate(ComponentContext cc) {}
    }
  2. Package the component into an OSGi bundle that is part of your user feature, along with your JACC provider.
  3. Ensure that your feature includes the OSGi subsystem content: com.ibm.ws.javaee.jacc.1.5; version="[1,1.0.100)"; location:="dev/api/spec/".
  4. After the feature is installed into the user product extension location, configure the server.xml file with the feature name. For example:
    <featureManager>
       ...
       <feature>usr:myJaccProvider</feature>
    </featureManager>