Oak 1.0 comes with a extension to the Jackrabbit user management API that allows to perform additional actions or validations upon common user management tasks such as
Similar functionality has been present in Jackrabbit 2.x as internal interface. Compared to the Jackrabbit interface the new AuthorizableAction
has been slightly adjusted to match Oak requirements operate directly on the Oak API, which eases the handling of implementation specific tasks such as writing protected items.
The following public interfaces are provided by Oak in the package org.apache.jackrabbit.oak.spi.security.user.action
:
The AuthorizableAction
interface itself allows to perform validations or write additional application specific content while executing user management related write operations. Therefore these actions are executed as part of the transient user management modifications. This contrasts to org.apache.jackrabbit.oak.spi.commit.CommitHook
s which in turn are only triggered once modifications are persisted.
Consequently, implementations of the AuthorizableAction
interface are expected to adhere to this rule and perform transient repository operation or validation. They must not force changes to be persisted by calling org.apache.jackrabbit.oak.api.Root.commit()
.
See section Group Actions for a related extension to monitor group specific operations and User Actions for user specific operations.
Oak 1.0 provides the following base implementations:
AbstractAuthorizableAction
: abstract base implementation that doesn't perform any action.DefaultAuthorizableActionProvider
: default action provider service that allows to enable the built-in actions provided with oak.CompositeActionProvider
: Allows to aggregate multiple provider implementations.The following implementations of the AuthorizableAction
interface are provided:
AccessControlAction
: set up permission for new authorizablesPasswordValidationAction
: simplistic password verification upon user creation and password modificationPasswordChangeAction
: verifies that the new password is different from the old oneClearMembershipAction
: clear group membership upon removal of an authorizable.As in Jackrabbit 2.x the actions are executed with the editing session and the target operation will fail if any of the configured actions fails (e.g. due to insufficient permissions by the editing Oak ContentSession).
The default security setup as present with Oak 1.0 is able to provide custom AuthorizableActionProvider
implementations and will automatically combine the different implementations using the CompositeActionProvider
.
In an OSGi setup the following steps are required in order to add an action provider implementation:
AuthorizableActionProvider
interface exposing your custom action(s).AuthorizableActionProvider
is listed as required service with the SecurityProvider
(see also Introduction)@Component() @Service(AuthorizableActionProvider.class) public class MyAuthorizableActionProvider implements AuthorizableActionProvider { private static final String PUBLIC_PROFILE_NAME = "publicProfileName"; private static final String PRIVATE_PROFILE_NAME = "privateProfileName"; private static final String FRIENDS_PROFILE_NAME = "friendsProfileName"; @Property(name = PUBLIC_PROFILE_NAME, value = "publicProfile") private String publicName; @Property(name = PRIVATE_PROFILE_NAME, value = "privateProfile") private String privateName; @Property(name = FRIENDS_PROFILE_NAME, value = "friendsProfile") private String friendsName; private ConfigurationParameters config = ConfigurationParameters.EMPTY; public MyAuthorizableActionProvider() {} public MyAuthorizableActionProvider(ConfigurationParameters config) { this.config = config; } //-----------------------------------------< AuthorizableActionProvider >--- @Override public List<? extends AuthorizableAction> getAuthorizableActions(SecurityProvider securityProvider) { AuthorizableAction action = new ProfileAction(publicName, privateName, friendsName); action.init(securityProvider, config); return Collections.singletonList(action); } //----------------------------------------------------< SCR Integration >--- @Activate private void activate(Map<String, Object> properties) { config = ConfigurationParameters.of(properties); } }
This example action generates additional child nodes upon user/group creation that will later be used to store various target-specific profile information:
class ProfileAction extends AbstractAuthorizableAction { private final String publicName; private final String privateName; private final String friendsName; ProfileAction(@Nullable String publicName, @Nullable String privateName, @Nullable String friendsName) { this.publicName = publicName; this.privateName = privateName; this.friendsName = friendsName; } @Override public void onCreate(Group group, Root root, NamePathMapper namePathMapper) throws RepositoryException { createProfileNodes(group.getPath(), root); } @Override public void onCreate(User user, String password, Root root, NamePathMapper namePathMapper) throws RepositoryException { createProfileNodes(user.getPath(), root); } private void createProfileNodes(@Nonnull String authorizablePath, @Nonnull Root root) throws AccessDeniedException { Tree tree = root.getTree(authorizablePath); if (tree.exists()) { NodeUtil authorizableNode = new NodeUtil(tree); if (publicName != null) { authorizableNode.addChild(publicName, NodeTypeConstants.NT_OAK_UNSTRUCTURED); } if (privateName != null) { authorizableNode.addChild(privateName, NodeTypeConstants.NT_OAK_UNSTRUCTURED); } if (friendsName != null) { authorizableNode.addChild(friendsName, NodeTypeConstants.NT_OAK_UNSTRUCTURED); } } }
Map<String, Object> userParams = new HashMap<String, Object>(); userParams.put(UserConstants.PARAM_AUTHORIZABLE_ACTION_PROVIDER, new MyAuthorizableActionProvider()); ConfigurationParameters config = ConfigurationParameters.of(ImmutableMap.of(UserConfiguration.NAME, ConfigurationParameters.of(userParams))); SecurityProvider securityProvider = SecurityProviderBuilder.newBuilder().with(config).build(); Repository repo = new Jcr(new Oak()).with(securityProvider).createRepository();