Fork me on GitHub

Introduction to Oak Security

General

The main entry point to Oak security is the SecurityProvider, which is registered to the Oak repository upon creation. The provider is in charge of collecting and exposing all security related modules present in a given Oak repository.

Each security module comes with one or multiple SecurityConfiguration(s) that are registered with the provider, identified (and possibly aggregated) by their name.

Modules

Currently Oak comes with the following built-in security modules, each defined by a dedicated sub-interfaces of SecurityConfiguration:

### API Extensions

The package org.apache.jackrabbit.oak.spi.security defines the following interfaces and base implementations:

  • SecurityProvider: Main entry point for Oak security.
    • OpenSecurityProvider: Rudimentary implementation for test purpose with the minimal required setup.
  • SecurityConfiguration: Base interface for all security related configurations.
    • SecurityConfiguration.Default: Default base implementation return default implementations for initializers, context as well as empty parameters, hooks, validators etc.
    • ConfigurationBase: Abstract base implementation of the SecurityConfiguration interface.
    • CompositeConfiguration: Abstract base implementation for all composite configurations that allow for aggregation of multiple modules.
  • ConfigurationParameters: Utility used to pass around parameters and options.
  • Context: Context information that allows to identify items defined and maintained by a give security module implementation.
  • RegistrationConstants: Utility used to define well-known registration properties

SecurityProvider

The SecurityProvider is the key to Oak security by providing access to the individual security modules and the configurations associated. Since version 1.3.7 Oak provides two implementations of the SecurityProvider suited for OSGi and non-OSGi setup, respectively.

OSGi Setup

Since Oak 1.3.7 the core bundle will install a dedicated OSGi component (SecurityProviderRegistration, labeled “Apache Jackrabbit Oak SecurityProvider”), which registers the SecurityProvider once all mandatory references have successfully been resolved. This new approach addresses issues present with the initial security provider implementation and has been backported to existing branches (see OAK-3201 and OAK-3441).

While optional configuration setting can be changed or extended at runtime, modules and extensions considered required for a functional security setup, need to be listed in the “Required Service PIDs” property. This asserts both reliable security setup and proper initialization of the individual modules. See also sections Configuration and Pluggability below.

Non-OSGi Setup

In a non-OSGi setup the SecurityProvider (be it the default or a custom implementation) gets passed to the repository constructor. See section pluggability for details wrt module initialization.

The following example has been extracted from the basic test setup:

NodeStore nodeStore = ...

ConfigurationParameters params = ... // TODO: provide config options
// Optional: set additional/custom implementations of the supported `SecurityConfiguration`s via the params
SecurityProvider sp = SecurityProviderBuilder.newBuilder().with(params).build();

Repository repository = new Jcr(nodeStore).with(sp).createRepository();

SecurityConfiguration

The SecurityConfiguration interface defines functionality common to all security related modules. Apart from simple configuration parameters the basic interface defines the means to properly initialize a given security module and provide Oak internal mechanism to asserts proper validation and data consistency for all security relevant operations.

Please note, that RepositoryInitializer and WorkspaceInitializer as exposed by the base interface are only invoked upon repository|workspace initialization and consequently impact the pluggability of a given security module. Examples includes modules that require modifications to the global repository content such as node types, namespaces and privileges or require the installation of initial content or index definitions for proper execution.

The following subinterfaces of SecurityConfiguration are currently defined by Oak:

Mandatory and Optional Modules

While Oak ships default implementation for all security configurations listed above, only authentication and authorization are mandatory for a functional Oak repository.

This is compliant with the security requirements defined by JSR 283 which defines API to login into the repository and mandates minimal permission evaluation, be it implementation specific of imposed by the optional access control management.

The minimal security setup may consequently be reduced to a setup as defined by the following imaginary, custom SecurityProvider (see also OpenSecurityProvider)

 public class MySecurityProvider implements SecurityProvider {
 
     [...]
 
     public <T> T getConfiguration(Class<T> configClass) {
         if (AuthenticationConfiguration.class == configClass) {
             return (T) new MyAuthentication();
         } else if (AuthorizationConfiguration.class == configClass) {
             return (T) new MyAuthorization();
         } else {
             throw new IllegalArgumentException();
         }
     }
     
     private final class MyAuthentication extends SecurityConfiguration.Default implements AuthenticationConfiguration {   
         [...]
     }
     
     private final class MyAuthorization extends SecurityConfiguration.Default implements AuthorizationConfiguration { 
         public AccessControlManager getAccessControlManager(Root root, NamePathMapper namePathMapper) {
             throw new UnsupportedOperationException();
         }
         public RestrictionProvider getRestrictionProvider() {
             throw new UnsupportedOperationException();
         }
         public PermissionProvider getPermissionProvider(Root root, String workspaceName, Set<Principal> principals) {
             return MyPermissionProvider.getInstance(principals);
         }
     }
 }

All other security modules can be considered optional from an Oak repository point of view. Please note the following dependencies and special cases:

  1. Authentication is mandatory and expected to bind a set of Principals to the Subject. This may happen before or during the repository login.
  2. Permission Evaluation is mandatory and associated with the set of Principals bound to to the Subject during the authentication step.
  3. Principals represent the link between authentication and authorization and MAY be exposed by Principal Management module as described above.
  4. Access Control Management is optional and usually goes along with Principal and Privilege Management
  5. Principal Management is optional and is NOT tied to User Management. However, supporting User Management in a given repository setup usually goes along with exposing the corresponding principals as part of the Principal Management.
  6. User Management is optional and MAY be used for credentials validation during the authentication step. If present it is usually used as a source for principals exposed by Principal Management.
### Configuration

The configuration parameters of individual security modules are described in the corresponding sections. The following paragraphs describe the configuration of SecurityProvider and CompositeConfiguration in an OSGi-base setup.

SecurityProviderRegistration

Parameter Type Default Description
Required Services String[] see below Service references mandatory for the SecurityProvider registration.

The value of the individual configuration entries can be one of:

  • the value of the service.pid registration property
  • the value of the oak.security.name registration property

By default the SecurityProviderRegistration defines the following mandatory services. As long as these required references are not resolved the SecurityProviderRegistration will not register the SecurityProvider service and ultimately prevent premature initialization of the Oak repository instance.

  • “org.apache.jackrabbit.oak.security.authorization.AuthorizationConfigurationImpl”
  • “org.apache.jackrabbit.oak.security.principal.PrincipalConfigurationImpl”,
  • “org.apache.jackrabbit.oak.security.authentication.token.TokenConfigurationImpl”,
  • “org.apache.jackrabbit.oak.spi.security.user.action.DefaultAuthorizableActionProvider”,
  • “org.apache.jackrabbit.oak.security.authorization.restriction.RestrictionProviderImpl”,
  • “org.apache.jackrabbit.oak.security.user.UserAuthenticationFactoryImpl”

The value of this configuration parameter needs to be adjusted for any additional module or functionality that is considered required for a successful security setup. See section pluggability below.

Parameter Type Default Description
Authorization Composition Type String (AND|OR) AND The Composite Authorization model uses this flag to determine what type of logic to apply to the existing providers

Given a set of permission providers, the composite model can aggregate the results by applying an AND logic (for example all providers must allow a specific privilege in order to be granted), or an OR (for example any provider can allow a privilege). By default the AND version is used.

CompositeConfiguration

Parameter Type Default Description
PARAM_RANKING int NO_RANKING (Integer.MIN_VALUE) Optional configuration parameter to define the ranking within the aggregation.

Note: Security modules that allow for multiple configurations may choose to expose the PARAM_RANKING option in order to allow for explicit ordering of the individual implementations. If the ranking parameter is omitted the CompositeConfiguration will try to use the SERVICE_RANKING to define the order. If neither is available (or set to NO_RANKING) the new entry will be appended to the list.

### Pluggability

In a default setup Oak allows to plug custom or additional implementations of the various SecurityConfiguration described before. Similarly it would be possible to provide a custom SecurityProvider.

Please note: this is only recommended for experts having in-depth understanding of Oak internals and which understand the security risk associated with custom replacements or extensions.

SecurityProvider

The default SecurityProvider service could be replaced by deploying a custom service to the OSGi container. In a non-OSGi setup the JCR|Oak repository needs to be created with the custom implementation:

SecurityProvider sp = new MySecurityProvider();  
Repository repository = new Jcr().with(sp).createRepository();

SecurityConfiguration

The default Oak security setup distinguishes between the following types of modules:

  • Unary modules: AuthenticationConfiguration, PrivilegeConfiguration, UserConfiguration
  • Multiple modules: AuthorizationConfiguration (since Oak 1.4), PrincipalConfiguration, TokenConfiguration

Plugging an implementation of an unary module will replace the default provided by Oak. As far as the multiple modules are concerned a custom implementation plugged into the repository will result in the creation of a CompositeConfiguration. The aggregated modules are kept in a list while the insertion order is defined by the PARAM_RANKING or by the OSGi service ranking in case the explicit ranking parameter is missing.

OSGi setup

The following steps are required to replace an existing unary security component or add an additional implementation (for multiple configurations only):

  • Deploy the bundle containing your custom implementation
  • Configure the component according to your needs
  • For multiple configurations make sure you have set the desired ranking
  • Find the SecurityProviderRegistration (“Apache Jackrabbit Oak SecurityProvider”) config and enter the PID of your custom security module as additional value to the requiredServicePids property.
  • In case of a replacement remove the PID of the module to be replaced and make sure your implementation gets a higher SERVICE_RANKING or deactivate the obsolete module altogether.
Non-OSGi setup

In this case the individual security modules get “manually” bound/unbound to the SecurityProvider instance. The provider itself might be the default or a custom implementation of the interface. If and how security modules can be added to the provider remains an implementation detail and is not part of the SecurityProvider interface definition.

Example

Extend the default SecurityProvider with a custom PrincipalConfiguration. See also oak-exercise module for an example.

MyPrincipalConfiguration pc = new MyPrincipalConfiguration();

ConfigurationParameters params = ConfigurationParameters.EMPTY;
pc.setParameters(params);
SecurityProvider securityProvider = SecurityProviderBuilder.newBuilder().with(params).build();

CompositeConfiguration<PrincipalConfiguration> composite = (CompositeConfiguration) securityProvider
    .getConfiguration(PrincipalConfiguration.class);
PrincipalConfiguration defConfig = composite.getDefaultConfig();

pc.setSecurityProvider(securityProvider);
pc.setRootProvider(((ConfigurationBase) defConfig).getRootProvider());
pc.setTreeProvider(((ConfigurationBase) defConfig).getTreeProvider());
composite.addConfiguration(pc);
composite.addConfiguration(defConfig);

Repository repo = new Jcr(new Oak()).with(securityProvider).createRepository();
Initialization of SecurityConfiguration(s)

If a given security modules mandates repository and|or workspace initialization steps such as e.g. node type registration or persisting new index definitions, the deployment of the module requires a reload of the SecurityProvider.

In the default OSGi-based setup this is achieved by adding the PID of corresponding service to the Required Service PIDs property mentioned above ultimately forcing the re-registration of the SecurityProvider service with the bundle context.

Other setup scenarios would need to recreate the ContentRepository object or adjust the repository ‘initial’ content before binding the new configuration to the SecurityProvider in order to avoid inconsistencies.