Index: oak-authorization-cug/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/cug/impl/CugSecurityProvider.java =================================================================== --- oak-authorization-cug/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/cug/impl/CugSecurityProvider.java (revision 1703384) +++ oak-authorization-cug/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/cug/impl/CugSecurityProvider.java (working copy) @@ -33,12 +33,8 @@ composite.setDefaultConfig(authorizationConfiguration); composite.addConfiguration(new CugConfiguration(this)); composite.addConfiguration(authorizationConfiguration); - ((CugSecurityProvider) this).bindAuthorizationConfiguration(composite); + setAuthorizationConfiguration(composite); } } - @Override - protected void bindAuthorizationConfiguration(@Nonnull AuthorizationConfiguration reference) { - super.bindAuthorizationConfiguration(reference); - } } \ No newline at end of file Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/Preconditions.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/Preconditions.java (revision 0) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/Preconditions.java (working copy) @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.oak.security; + +import java.util.Set; + +import static com.google.common.collect.Sets.newHashSet; + +/** + * Represents a preconditions set that may be satisfied by adding the right + * candidates. + *

+ * Initially, a set of preconditions is empty. An empty set of preconditions is + * always satisfied. If candidates are added, but the precondition set is empty, + * the preconditions are considered satisfied. + *

+ * When some preconditions are added, the preconditions set may enter into the + * unsatisfied state. In this case, the preconditions set may be come satisfied + * again only with the addition of the right candidates. + *

+ * This class doesn't admit duplicates for preconditions or candidates. Adding + * the same precondition (or candidate) twice doesn't have any effect on the + * state of the preconditions set. + */ +class Preconditions { + + private final Set preconditions = newHashSet(); + + private final Set candidates = newHashSet(); + + private boolean dirty = false; + + private boolean satisfied = true; + + /** + * Add a precondition to this preconditions set. If the precondition already + * belongs to this set, this operation has no effect. + * + * @param precondition The precondition to be added. + */ + public void addPrecondition(String precondition) { + if (preconditions.add(precondition)) { + dirty = true; + } + } + + /** + * Remove all the preconditions to this set. This makes the set of + * preconditions empty and, as such, satisfied. + */ + public void clearPreconditions() { + preconditions.clear(); + dirty = false; + satisfied = true; + } + + /** + * Add a candidate to this preconditions set. If the candidate already + * belongs to this set, this operation has no effect. + * + * @param candidate The candidate to be added. + */ + public void addCandidate(String candidate) { + if (candidates.add(candidate)) { + dirty = true; + } + } + + /** + * Remove a candidate from this preconditions set. If the candidate doesn't + * belong to this set, this operation has no effect. + * + * @param candidate The candidate to be removed. + */ + public void removeCandidate(String candidate) { + if (candidates.remove(candidate)) { + dirty = true; + } + } + + /** + * Check if the preconditions set are satisfied. + * + * @return {@code true} if the preconditions set is satisfied, {@code false} + * otherwise. + */ + public boolean areSatisfied() { + if (dirty) { + Set unsatisfied = newHashSet(preconditions); + unsatisfied.removeAll(candidates); + satisfied = unsatisfied.isEmpty(); + dirty = false; + } + + return satisfied; + } + + @Override + public String toString() { + return String.format("Preconditions(preconditions = %s, candidates = %s)", preconditions, candidates); + } + +} Property changes on: oak-core/src/main/java/org/apache/jackrabbit/oak/security/Preconditions.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderImpl.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderImpl.java (working copy) @@ -16,118 +16,96 @@ */ package org.apache.jackrabbit.oak.security; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.common.collect.ImmutableMap; -import org.apache.felix.scr.annotations.Activate; -import org.apache.felix.scr.annotations.Component; -import org.apache.felix.scr.annotations.Deactivate; -import org.apache.felix.scr.annotations.Reference; -import org.apache.felix.scr.annotations.ReferenceCardinality; -import org.apache.felix.scr.annotations.ReferencePolicy; -import org.apache.felix.scr.annotations.Service; -import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard; import org.apache.jackrabbit.oak.security.authentication.AuthenticationConfigurationImpl; import org.apache.jackrabbit.oak.security.authentication.token.TokenConfigurationImpl; import org.apache.jackrabbit.oak.security.authorization.AuthorizationConfigurationImpl; import org.apache.jackrabbit.oak.security.principal.PrincipalConfigurationImpl; import org.apache.jackrabbit.oak.security.privilege.PrivilegeConfigurationImpl; import org.apache.jackrabbit.oak.security.user.UserConfigurationImpl; -import org.apache.jackrabbit.oak.spi.security.ConfigurationBase; import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; import org.apache.jackrabbit.oak.spi.security.SecurityConfiguration; import org.apache.jackrabbit.oak.spi.security.SecurityProvider; import org.apache.jackrabbit.oak.spi.security.authentication.AuthenticationConfiguration; -import org.apache.jackrabbit.oak.spi.security.authentication.token.CompositeTokenConfiguration; import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration; import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration; -import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; -import org.apache.jackrabbit.oak.spi.security.principal.CompositePrincipalConfiguration; import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration; import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConfiguration; import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; -import org.apache.jackrabbit.oak.spi.security.user.UserConstants; import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard; -import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardAuthorizableActionProvider; -import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardAuthorizableNodeName; import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardAware; -import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardRestrictionProvider; -import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUserAuthenticationFactory; -import org.osgi.framework.BundleContext; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Sets.newHashSet; -@Component -@Service(value = {SecurityProvider.class}) public class SecurityProviderImpl implements SecurityProvider, WhiteboardAware { - @Reference - private volatile AuthorizationConfiguration authorizationConfiguration; + private AuthenticationConfiguration authenticationConfiguration; - @Reference - private volatile AuthenticationConfiguration authenticationConfiguration; + private AuthorizationConfiguration authorizationConfiguration; - @Reference - private volatile PrivilegeConfiguration privilegeConfiguration; + private UserConfiguration userConfiguration; - @Reference - private volatile UserConfiguration userConfiguration; + private PrincipalConfiguration principalConfiguration; - @Reference(referenceInterface = PrincipalConfiguration.class, - name = "principalConfiguration", - bind = "bindPrincipalConfiguration", - unbind = "unbindPrincipalConfiguration", - policy = ReferencePolicy.DYNAMIC, - cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE) - private final CompositePrincipalConfiguration principalConfiguration = new CompositePrincipalConfiguration(this); + private PrivilegeConfiguration privilegeConfiguration; - @Reference(referenceInterface = TokenConfiguration.class, - name = "tokenConfiguration", - bind = "bindTokenConfiguration", - unbind = "unbindTokenConfiguration", - policy = ReferencePolicy.DYNAMIC, - cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE) - private final CompositeTokenConfiguration tokenConfiguration = new CompositeTokenConfiguration(this); + private TokenConfiguration tokenConfiguration; - private final WhiteboardAuthorizableNodeName authorizableNodeName = new WhiteboardAuthorizableNodeName(); - private final WhiteboardAuthorizableActionProvider authorizableActionProvider = new WhiteboardAuthorizableActionProvider(); - private final WhiteboardRestrictionProvider restrictionProvider = new WhiteboardRestrictionProvider(); - private final WhiteboardUserAuthenticationFactory userAuthenticationFactory = new WhiteboardUserAuthenticationFactory(UserConfigurationImpl.getDefaultAuthenticationFactory()); - private ConfigurationParameters configuration; private Whiteboard whiteboard; /** - * Default constructor used in OSGi environments. + * Default constructor using an empty configuration and default + * implementations for the security configurations. */ public SecurityProviderImpl() { this(ConfigurationParameters.EMPTY); } /** - * Create a new {@code SecurityProvider} instance with the given configuration - * parameters. + * Create a new {@code SecurityProvider} instance with the given + * configuration parameters. * * @param configuration security configuration */ public SecurityProviderImpl(@Nonnull ConfigurationParameters configuration) { - checkNotNull(configuration); - this.configuration = configuration; + this.configuration = checkNotNull(configuration); + this.authenticationConfiguration = new AuthenticationConfigurationImpl(this); + this.authorizationConfiguration = new AuthorizationConfigurationImpl(this); + this.userConfiguration = new UserConfigurationImpl(this); + this.principalConfiguration = new PrincipalConfigurationImpl(this); + this.privilegeConfiguration = new PrivilegeConfigurationImpl(); + this.tokenConfiguration = new TokenConfigurationImpl(this); + } - authenticationConfiguration = new AuthenticationConfigurationImpl(this); - authorizationConfiguration = new AuthorizationConfigurationImpl(this); - userConfiguration = new UserConfigurationImpl(this); - privilegeConfiguration = new PrivilegeConfigurationImpl(); + protected void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) { + this.authenticationConfiguration = checkNotNull(authenticationConfiguration); + } - principalConfiguration.setDefaultConfig(new PrincipalConfigurationImpl(this)); - tokenConfiguration.setDefaultConfig(new TokenConfigurationImpl(this)); + protected void setAuthorizationConfiguration(AuthorizationConfiguration authorizationConfiguration) { + this.authorizationConfiguration = authorizationConfiguration; } + protected void setUserConfiguration(UserConfiguration userConfiguration) { + this.userConfiguration = userConfiguration; + } + + protected void setPrincipalConfiguration(PrincipalConfiguration principalConfiguration) { + this.principalConfiguration = principalConfiguration; + } + + protected void setPrivilegeConfiguration(PrivilegeConfiguration privilegeConfiguration) { + this.privilegeConfiguration = privilegeConfiguration; + } + + protected void setTokenConfiguration(TokenConfiguration tokenConfiguration) { + this.tokenConfiguration = tokenConfiguration; + } + @Override public void setWhiteboard(@Nonnull Whiteboard whiteboard) { this.whiteboard = whiteboard; @@ -144,7 +122,9 @@ if (name == null) { return configuration; } + ConfigurationParameters params = configuration.getConfigValue(name, ConfigurationParameters.EMPTY); + for (SecurityConfiguration sc : getConfigurations()) { if (sc != null && sc.getName().equals(name)) { return ConfigurationParameters.of(params, sc.getParameters()); @@ -156,14 +136,14 @@ @Nonnull @Override public Iterable getConfigurations() { - Set scs = new HashSet(); - scs.add(authenticationConfiguration); - scs.add(authorizationConfiguration); - scs.add(userConfiguration); - scs.add(principalConfiguration); - scs.add(privilegeConfiguration); - scs.add(tokenConfiguration); - return scs; + return newHashSet( + authenticationConfiguration, + authorizationConfiguration, + userConfiguration, + principalConfiguration, + privilegeConfiguration, + tokenConfiguration + ); } @SuppressWarnings("unchecked") @@ -187,89 +167,4 @@ } } - //----------------------------------------------------------------< SCR >--- - @Activate - protected void activate(BundleContext context) { - whiteboard = new OsgiWhiteboard(context); - authorizableActionProvider.start(whiteboard); - authorizableNodeName.start(whiteboard); - restrictionProvider.start(whiteboard); - userAuthenticationFactory.start(whiteboard); - - initializeConfigurations(); - } - - @Deactivate - protected void deactivate() { - authorizableActionProvider.stop(); - authorizableNodeName.stop(); - restrictionProvider.stop(); - userAuthenticationFactory.stop(); - } - - @SuppressWarnings("UnusedDeclaration") - protected void bindPrincipalConfiguration(@Nonnull PrincipalConfiguration reference) { - principalConfiguration.addConfiguration(initConfiguration(reference)); - } - - @SuppressWarnings("UnusedDeclaration") - protected void unbindPrincipalConfiguration(@Nonnull PrincipalConfiguration reference) { - principalConfiguration.removeConfiguration(reference); - } - - @SuppressWarnings("UnusedDeclaration") - protected void bindTokenConfiguration(@Nonnull TokenConfiguration reference) { - tokenConfiguration.addConfiguration(initConfiguration(reference)); - } - - @SuppressWarnings("UnusedDeclaration") - protected void unbindTokenConfiguration(@Nonnull TokenConfiguration reference) { - tokenConfiguration.removeConfiguration(reference); - } - - @SuppressWarnings("UnusedDeclaration") - protected void bindAuthorizationConfiguration(@Nonnull AuthorizationConfiguration reference) { - authorizationConfiguration = initConfiguration(reference); - // TODO (OAK-1268): authorizationConfiguration.addConfiguration(initConfiguration(reference)); - } - - @SuppressWarnings("UnusedDeclaration") - protected void unbindAuthorizationConfiguration(@Nonnull AuthorizationConfiguration reference) { - authorizationConfiguration = new AuthorizationConfigurationImpl(this); - // TODO (OAK-1268): authorizationConfiguration.removeConfiguration(reference); - } - - //------------------------------------------------------------< private >--- - private void initializeConfigurations() { - initConfiguration(authorizationConfiguration, ConfigurationParameters.of( - AccessControlConstants.PARAM_RESTRICTION_PROVIDER, restrictionProvider) - ); - - Map userMap = ImmutableMap.of( - UserConstants.PARAM_AUTHORIZABLE_ACTION_PROVIDER, authorizableActionProvider, - UserConstants.PARAM_AUTHORIZABLE_NODE_NAME, authorizableNodeName, - UserConstants.PARAM_USER_AUTHENTICATION_FACTORY, userAuthenticationFactory); - initConfiguration(userConfiguration, ConfigurationParameters.of(userMap)); - - initConfiguration(authenticationConfiguration); - initConfiguration(privilegeConfiguration); - } - - private T initConfiguration(@Nonnull T config) { - if (config instanceof ConfigurationBase) { - ConfigurationBase cfg = (ConfigurationBase) config; - cfg.setSecurityProvider(this); - cfg.setParameters(ConfigurationParameters.of(ConfigurationParameters.EMPTY, cfg.getParameters())); - } - return config; - } - - private T initConfiguration(@Nonnull T config, @Nonnull ConfigurationParameters params) { - if (config instanceof ConfigurationBase) { - ConfigurationBase cfg = (ConfigurationBase) config; - cfg.setSecurityProvider(this); - cfg.setParameters(ConfigurationParameters.of(params, cfg.getParameters())); - } - return config; - } } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderRegistration.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderRegistration.java (revision 0) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderRegistration.java (working copy) @@ -0,0 +1,622 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.oak.security; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Modified; +import org.apache.felix.scr.annotations.Properties; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.PropertyUnbounded; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.ReferencePolicy; +import org.apache.felix.scr.annotations.References; +import org.apache.jackrabbit.oak.commons.PropertiesUtil; +import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard; +import org.apache.jackrabbit.oak.security.user.UserConfigurationImpl; +import org.apache.jackrabbit.oak.spi.security.ConfigurationBase; +import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; +import org.apache.jackrabbit.oak.spi.security.SecurityConfiguration; +import org.apache.jackrabbit.oak.spi.security.SecurityProvider; +import org.apache.jackrabbit.oak.spi.security.authentication.AuthenticationConfiguration; +import org.apache.jackrabbit.oak.spi.security.authentication.token.CompositeTokenConfiguration; +import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration; +import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration; +import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; +import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider; +import org.apache.jackrabbit.oak.spi.security.principal.CompositePrincipalConfiguration; +import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration; +import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConfiguration; +import org.apache.jackrabbit.oak.spi.security.user.AuthorizableNodeName; +import org.apache.jackrabbit.oak.spi.security.user.UserAuthenticationFactory; +import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; +import org.apache.jackrabbit.oak.spi.security.user.UserConstants; +import org.apache.jackrabbit.oak.spi.security.user.action.AuthorizableActionProvider; +import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardAuthorizableActionProvider; +import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardAuthorizableNodeName; +import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardRestrictionProvider; +import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUserAuthenticationFactory; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Lists.newCopyOnWriteArrayList; + +@Component( + immediate = true, + metatype = true, + label = "Apache Jackrabbit Oak SecurityProvider", + description = "The default SecurityProvider embedded in Apache Jackrabbit Oak" +) +@Properties({ + @Property( + name = "requiredServicePids", + label = "Required service PIDs", + description = "The SecurityProvider will not register itself " + + "unless the services identified by these PIDs are " + + "registered first. Only the PIDs of implementations of " + + "the following interfaces are checked: " + + "PrincipalConfiguration, TokenConfiguration, " + + "AuthorizableNodeName, AuthorizableActionProvider, " + + "RestrictionProvider and UserAuthenticationFactory.", + value = { + "org.apache.jackrabbit.oak.security.principal.PrincipalConfigurationImpl", + "org.apache.jackrabbit.oak.security.authentication.token.TokenConfigurationImpl", + "org.apache.jackrabbit.oak.security.user.RandomAuthorizableNodeName", + "org.apache.jackrabbit.oak.spi.security.user.action.DefaultAuthorizableActionProvider", + "org.apache.jackrabbit.oak.security.authorization.restriction.RestrictionProviderImpl", + "org.apache.jackrabbit.oak.security.user.UserAuthenticationFactoryImpl" + }, + unbounded = PropertyUnbounded.ARRAY + ) +}) +@References({ + @Reference( + name = "principalConfiguration", + referenceInterface = PrincipalConfiguration.class, + cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, + policy = ReferencePolicy.DYNAMIC + ), + @Reference( + name = "tokenConfiguration", + referenceInterface = TokenConfiguration.class, + cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, + policy = ReferencePolicy.DYNAMIC + ), + @Reference( + name = "authorizableNodeName", + referenceInterface = AuthorizableNodeName.class, + cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, + policy = ReferencePolicy.DYNAMIC + ), + @Reference( + name = "authorizableActionProvider", + referenceInterface = AuthorizableActionProvider.class, + cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, + policy = ReferencePolicy.DYNAMIC + ), + @Reference( + name = "restrictionProvider", + referenceInterface = RestrictionProvider.class, + cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, + policy = ReferencePolicy.DYNAMIC + ), + @Reference( + name = "userAuthenticationFactory", + referenceInterface = UserAuthenticationFactory.class, + cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, + policy = ReferencePolicy.DYNAMIC + ) +}) +public class SecurityProviderRegistration { + + private static final Logger log = LoggerFactory.getLogger(SecurityProviderRegistration.class); + + @Reference + private AuthorizationConfiguration authorizationConfiguration; + + @Reference + private AuthenticationConfiguration authenticationConfiguration; + + @Reference + private PrivilegeConfiguration privilegeConfiguration; + + @Reference + private UserConfiguration userConfiguration; + + private BundleContext context; + + private ServiceRegistration registration; + + private boolean registering; + + private final Preconditions preconditions = new Preconditions(); + + private final List principalConfigurations = newCopyOnWriteArrayList(); + + private final List tokenConfigurations = newCopyOnWriteArrayList(); + + private final List authorizableNodeNames = newCopyOnWriteArrayList(); + + private final List authorizableActionProviders = newCopyOnWriteArrayList(); + + private final List restrictionProviders = newCopyOnWriteArrayList(); + + private final List userAuthenticationFactories = newCopyOnWriteArrayList(); + + @Activate + public void activate(BundleContext context, Map configuration) { + String[] requiredServicePids = getRequiredServicePids(configuration); + + synchronized (this) { + for (String pid : requiredServicePids) { + preconditions.addPrecondition(pid); + } + + this.context = context; + } + + maybeRegister(); + } + + @Modified + public void modified(Map configuration) { + String[] requiredServicePids = getRequiredServicePids(configuration); + + synchronized (this) { + preconditions.clearPreconditions(); + + for (String pid : requiredServicePids) { + preconditions.addPrecondition(pid); + } + } + + maybeUnregister(); + maybeRegister(); + } + + @Deactivate + public void deactivate() { + ServiceRegistration registration; + + synchronized (this) { + registration = this.registration; + + this.registration = null; + this.registering = false; + this.context = null; + + this.preconditions.clearPreconditions(); + } + + if (registration != null) { + registration.unregister(); + } + } + + public void bindAuthorizationConfiguration(AuthorizationConfiguration authorizationConfiguration) { + this.authorizationConfiguration = authorizationConfiguration; + } + + public void unbindAuthorizationConfiguration(AuthorizationConfiguration authorizationConfiguration) { + this.authorizationConfiguration = null; + } + + public void bindAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) { + this.authenticationConfiguration = authenticationConfiguration; + } + + public void unbindAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) { + this.authenticationConfiguration = null; + } + + public void bindPrivilegeConfiguration(PrivilegeConfiguration privilegeConfiguration) { + this.privilegeConfiguration = privilegeConfiguration; + } + + public void unbindPrivilegeConfiguration(PrivilegeConfiguration privilegeConfiguration) { + this.privilegeConfiguration = null; + } + + public void bindUserConfiguration(UserConfiguration userConfiguration) { + this.userConfiguration = userConfiguration; + } + + public void unbindUserConfiguration(UserConfiguration userConfiguration) { + this.userConfiguration = null; + } + + public void bindPrincipalConfiguration(PrincipalConfiguration principalConfiguration, Map properties) { + synchronized (this) { + principalConfigurations.add(principalConfiguration); + addCandidate(properties); + } + + maybeRegister(); + } + + public void unbindPrincipalConfiguration(PrincipalConfiguration principalConfiguration, Map properties) { + synchronized (this) { + principalConfigurations.remove(principalConfiguration); + removeCandidate(properties); + } + + maybeUnregister(); + } + + public void bindTokenConfiguration(TokenConfiguration tokenConfiguration, Map properties) { + synchronized (this) { + tokenConfigurations.add(tokenConfiguration); + addCandidate(properties); + } + + maybeRegister(); + } + + public void unbindTokenConfiguration(TokenConfiguration tokenConfiguration, Map properties) { + synchronized (this) { + tokenConfigurations.remove(tokenConfiguration); + removeCandidate(properties); + } + + maybeUnregister(); + } + + public void bindAuthorizableNodeName(AuthorizableNodeName authorizableNodeName, Map properties) { + synchronized (this) { + authorizableNodeNames.add(authorizableNodeName); + addCandidate(properties); + } + + maybeRegister(); + } + + public void unbindAuthorizableNodeName(AuthorizableNodeName authorizableNodeName, Map properties) { + synchronized (this) { + authorizableNodeNames.remove(authorizableNodeName); + removeCandidate(properties); + } + + maybeUnregister(); + } + + public void bindAuthorizableActionProvider(AuthorizableActionProvider authorizableActionProvider, Map properties) { + synchronized (this) { + authorizableActionProviders.add(authorizableActionProvider); + addCandidate(properties); + } + + maybeRegister(); + } + + public void unbindAuthorizableActionProvider(AuthorizableActionProvider authorizableActionProvider, Map properties) { + synchronized (this) { + authorizableActionProviders.remove(authorizableActionProvider); + removeCandidate(properties); + } + + maybeUnregister(); + } + + public void bindRestrictionProvider(RestrictionProvider restrictionProvider, Map properties) { + synchronized (this) { + restrictionProviders.add(restrictionProvider); + addCandidate(properties); + } + + maybeRegister(); + } + + public void unbindRestrictionProvider(RestrictionProvider restrictionProvider, Map properties) { + synchronized (this) { + restrictionProviders.remove(restrictionProvider); + removeCandidate(properties); + } + + maybeUnregister(); + } + + public void bindUserAuthenticationFactory(UserAuthenticationFactory userAuthenticationFactory, Map properties) { + synchronized (this) { + userAuthenticationFactories.add(userAuthenticationFactory); + addCandidate(properties); + } + + maybeRegister(); + } + + public void unbindUserAuthenticationFactory(UserAuthenticationFactory userAuthenticationFactory, Map properties) { + synchronized (this) { + userAuthenticationFactories.remove(userAuthenticationFactory); + removeCandidate(properties); + } + + maybeUnregister(); + } + + private void maybeRegister() { + BundleContext context; + + log.info("Trying to register a SecurityProvider..."); + + synchronized (this) { + + // The component is not activated, yet. We have no means of registering + // the SecurityProvider. This method will be called again after + // activation completes. + + if (this.context == null) { + log.info("Aborting: no BundleContext is available"); + return; + } + + // The preconditions are not satisifed. This may happen when this + // component is activated but not enough mandatory services are bound + // to it. + + if (!preconditions.areSatisfied()) { + log.info("Aborting: preconditions are not satisfied: {}", preconditions); + return; + } + + // The SecurityProvider is already registered. This may happen when a + // new dependency is added to this component, but the requirements are + // already satisfied. + + if (registration != null) { + log.info("Aborting: a SecurityProvider is already registered"); + return; + } + + // If the component is in the process of registering an instance of + // SecurityProvider, return. This check is necessary because we don't + // want to call createSecurityProvider() more than once. That method, + // in fact, changes the state of the bound dependencies (it sets a + // back-reference from the security configurations to the new + // SecurityProvider). We want those dependencies to change state only + // when we are sure that we will register the SecurityProvider we + // are creating. + + if (registering) { + log.info("Aborting: a SecurityProvider is already being registered"); + return; + } + + // Mark the start of a registration process. + + registering = true; + + // Save the BundleContext for local usage. + + context = this.context; + } + + // Register the SecurityProvider. + + ServiceRegistration registration = context.registerService( + SecurityProvider.class.getName(), + createSecurityProvider(context), + null + ); + + synchronized (this) { + this.registration = registration; + this.registering = false; + } + + log.info("SecurityProvider instance registered"); + } + + private void maybeUnregister() { + ServiceRegistration registration; + + log.info("Trying to unregister the SecurityProvider..."); + + synchronized (this) { + + // If there is nothing to register, we obviously have nothing to do. + + if (this.registration == null) { + log.info("Aborting: no SecurityProvider is registered"); + return; + } + + // The preconditions are not satisfied. This may happen when a + // dependency is unbound from the current component. + + if (preconditions.areSatisfied()) { + log.info("Aborting: preconditions are satisfied"); + return; + } + + // Save the ServiceRegistration for local use. + + registration = this.registration; + this.registration = null; + } + + registration.unregister(); + + log.info("SecurityProvider instance unregistered"); + } + + private SecurityProvider createSecurityProvider(BundleContext context) { + SecurityProviderImpl securityProvider = new SecurityProviderImpl(); + + // Static, mandatory references + + securityProvider.setAuthenticationConfiguration(initializeConfiguration(securityProvider, authenticationConfiguration)); + securityProvider.setAuthorizationConfiguration(initializeConfiguration(securityProvider, authorizationConfiguration)); + securityProvider.setUserConfiguration(initializeConfiguration(securityProvider, userConfiguration)); + securityProvider.setPrivilegeConfiguration(initializeConfiguration(securityProvider, privilegeConfiguration)); + + // Multiple, dynamic references + + securityProvider.setPrincipalConfiguration(createCompositePrincipalConfiguration(securityProvider)); + securityProvider.setTokenConfiguration(createCompositeTokenConfiguration(securityProvider)); + + // Whiteboard + + securityProvider.setWhiteboard(new OsgiWhiteboard(context)); + + return securityProvider; + } + + private PrincipalConfiguration createCompositePrincipalConfiguration(SecurityProvider securityProvider) { + return new CompositePrincipalConfiguration(securityProvider) { + + @Override + protected List getConfigurations() { + ArrayList configurations = newArrayList(newArrayList(principalConfigurations)); + + for (PrincipalConfiguration configuration : configurations) { + initializeConfiguration(getSecurityProvider(), configuration); + } + + return configurations; + } + + }; + } + + private TokenConfiguration createCompositeTokenConfiguration(SecurityProvider securityProvider) { + return new CompositeTokenConfiguration(securityProvider) { + + @Override + protected List getConfigurations() { + List configurations = newArrayList(tokenConfigurations); + + for (TokenConfiguration configuration : configurations) { + initializeConfiguration(getSecurityProvider(), configuration); + } + + return configurations; + } + + }; + } + + private AuthorizationConfiguration initializeConfiguration(SecurityProvider securityProvider, AuthorizationConfiguration authorizationConfiguration) { + return initializeConfiguration(securityProvider, authorizationConfiguration, ConfigurationParameters.of( + AccessControlConstants.PARAM_RESTRICTION_PROVIDER, createCompositeRestrictionProvider() + )); + } + + private UserConfiguration initializeConfiguration(SecurityProvider securityProvider, UserConfiguration userConfiguration) { + return initializeConfiguration(securityProvider, userConfiguration, ConfigurationParameters.of( + ConfigurationParameters.of(UserConstants.PARAM_AUTHORIZABLE_ACTION_PROVIDER, createCompositeAuthorizableActionProvider()), + ConfigurationParameters.of(UserConstants.PARAM_AUTHORIZABLE_NODE_NAME, createCompositeAuthorizableNodeName()), + ConfigurationParameters.of(UserConstants.PARAM_USER_AUTHENTICATION_FACTORY, createCompositeUserAuthenticationFactory()) + )); + } + + private T initializeConfiguration(SecurityProvider securityProvider, T configuration) { + return initializeConfiguration(securityProvider, configuration, ConfigurationParameters.EMPTY); + } + + private T initializeConfiguration(SecurityProvider securityProvider, T configuration, ConfigurationParameters parameters) { + if (configuration instanceof ConfigurationBase) { + ConfigurationBase base = (ConfigurationBase) configuration; + base.setSecurityProvider(securityProvider); + base.setParameters(ConfigurationParameters.of(parameters, base.getParameters())); + } + + return configuration; + } + + private RestrictionProvider createCompositeRestrictionProvider() { + return new WhiteboardRestrictionProvider() { + + @Override + protected List getServices() { + return newArrayList(restrictionProviders); + } + + }; + } + + private AuthorizableActionProvider createCompositeAuthorizableActionProvider() { + return new WhiteboardAuthorizableActionProvider() { + + @Override + protected List getServices() { + return newArrayList(authorizableActionProviders); + } + + }; + } + + private AuthorizableNodeName createCompositeAuthorizableNodeName() { + return new WhiteboardAuthorizableNodeName() { + + @Override + protected List getServices() { + return newArrayList(authorizableNodeNames); + } + + }; + } + + private UserAuthenticationFactory createCompositeUserAuthenticationFactory() { + return new WhiteboardUserAuthenticationFactory(UserConfigurationImpl.getDefaultAuthenticationFactory()) { + + @Override + protected List getServices() { + return newArrayList(userAuthenticationFactories); + } + + }; + } + + private void addCandidate(Map properties) { + String pid = getServicePid(properties); + + if (pid == null) { + return; + } + + preconditions.addCandidate(pid); + } + + private void removeCandidate(Map properties) { + String pid = getServicePid(properties); + + if (pid == null) { + return; + } + + preconditions.removeCandidate(pid); + } + + private String getServicePid(Map properties) { + return PropertiesUtil.toString(properties.get(Constants.SERVICE_PID), null); + } + + private String[] getRequiredServicePids(Map configuration) { + return PropertiesUtil.toStringArray(configuration.get("requiredServicePids"), new String[]{}); + } + +} Property changes on: oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderRegistration.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java (working copy) @@ -44,7 +44,7 @@ /** * Default implementation for the {@code TokenConfiguration} interface. */ -@Component(metatype = true, label = "Apache Jackrabbit Oak TokenConfiguration") +@Component(metatype = true, label = "Apache Jackrabbit Oak TokenConfiguration", immediate = true) @Service({TokenConfiguration.class, SecurityConfiguration.class}) @Properties({ @Property(name = TokenProvider.PARAM_TOKEN_EXPIRATION, Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/restriction/RestrictionProviderImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/restriction/RestrictionProviderImpl.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/restriction/RestrictionProviderImpl.java (working copy) @@ -56,7 +56,7 @@ * is {@link org.apache.jackrabbit.oak.api.Type#STRINGS}. * */ -@Component +@Component(immediate = true) @Service(RestrictionProvider.class) public class RestrictionProviderImpl extends AbstractRestrictionProvider { Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/package-info.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/package-info.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/package-info.java (working copy) @@ -20,7 +20,7 @@ * * See README.md for more details. */ -@Version("1.0.1") +@Version("2.0.0") @Export(optional = "provide:=true") package org.apache.jackrabbit.oak.security; Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/principal/PrincipalConfigurationImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/principal/PrincipalConfigurationImpl.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/principal/PrincipalConfigurationImpl.java (working copy) @@ -38,7 +38,7 @@ /** * Default implementation of the {@code PrincipalConfiguration} */ -@Component() +@Component(immediate = true) @Service({PrincipalConfiguration.class, SecurityConfiguration.class}) public class PrincipalConfigurationImpl extends ConfigurationBase implements PrincipalConfiguration { Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/RandomAuthorizableNodeName.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/RandomAuthorizableNodeName.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/RandomAuthorizableNodeName.java (working copy) @@ -33,7 +33,7 @@ * Implementation of the {@code AuthorizableNodeName} that generates a random * node name that doesn't reveal the ID of the authorizable. */ -@Component(metatype = true, label = "Apache Jackrabbit Oak Random Authorizable Node Name", description = "Generates a random name for the authorizable node.", policy = ConfigurationPolicy.REQUIRE) +@Component(metatype = true, label = "Apache Jackrabbit Oak Random Authorizable Node Name", description = "Generates a random name for the authorizable node.", immediate = true) @Service(AuthorizableNodeName.class) public class RandomAuthorizableNodeName implements AuthorizableNodeName { Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthenticationFactoryImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthenticationFactoryImpl.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthenticationFactoryImpl.java (working copy) @@ -26,7 +26,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -@Component +@Component(immediate = true) @Service public class UserAuthenticationFactoryImpl implements UserAuthenticationFactory { Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/token/CompositeTokenConfiguration.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/token/CompositeTokenConfiguration.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/token/CompositeTokenConfiguration.java (working copy) @@ -28,7 +28,7 @@ /** * {@link TokenConfiguration} that combines different token provider implementations. */ -public final class CompositeTokenConfiguration extends CompositeConfiguration implements TokenConfiguration { +public class CompositeTokenConfiguration extends CompositeConfiguration implements TokenConfiguration { public CompositeTokenConfiguration(@Nonnull SecurityProvider securityProvider) { super(TokenConfiguration.NAME, securityProvider); Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/token/package-info.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/token/package-info.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/token/package-info.java (working copy) @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@Version("1.1.0") +@Version("1.2.0") @Export(optional = "provide:=true") package org.apache.jackrabbit.oak.spi.security.authentication.token; Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/principal/CompositePrincipalConfiguration.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/principal/CompositePrincipalConfiguration.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/principal/CompositePrincipalConfiguration.java (working copy) @@ -30,7 +30,7 @@ * {@link PrincipalConfiguration} that combines different principal provider * implementations that share a common principal manager implementation. */ -public final class CompositePrincipalConfiguration extends CompositeConfiguration implements PrincipalConfiguration { +public class CompositePrincipalConfiguration extends CompositeConfiguration implements PrincipalConfiguration { public CompositePrincipalConfiguration(@Nonnull SecurityProvider securityProvider) { super(PrincipalConfiguration.NAME, securityProvider); Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/principal/package-info.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/principal/package-info.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/principal/package-info.java (working copy) @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@Version("1.0") +@Version("1.1.0") @Export(optional = "provide:=true") package org.apache.jackrabbit.oak.spi.security.principal; Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/DefaultAuthorizableActionProvider.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/DefaultAuthorizableActionProvider.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/DefaultAuthorizableActionProvider.java (working copy) @@ -38,7 +38,7 @@ * Default implementation of the {@link AuthorizableActionProvider} interface * that allows to config all actions provided by the OAK. */ -@Component(metatype = true, label = "Apache Jackrabbit Oak AuthorizableActionProvider") +@Component(metatype = true, label = "Apache Jackrabbit Oak AuthorizableActionProvider", immediate = true) @Service(AuthorizableActionProvider.class) @Properties({ @Property(name = DefaultAuthorizableActionProvider.ENABLED_ACTIONS, Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/package-info.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/package-info.java (revision 1703384) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/package-info.java (working copy) @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@Version("1.0.2") +@Version("1.0.3") @Export(optional = "provide:=true") package org.apache.jackrabbit.oak.spi.security.user.action; Index: oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/SecurityProviderRegistrationTest.groovy =================================================================== --- oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/SecurityProviderRegistrationTest.groovy (revision 0) +++ oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/SecurityProviderRegistrationTest.groovy (working copy) @@ -0,0 +1,424 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.oak.run.osgi + +import org.apache.felix.connect.launch.PojoServiceRegistry +import org.apache.jackrabbit.api.security.principal.PrincipalManager +import org.apache.jackrabbit.oak.api.Root +import org.apache.jackrabbit.oak.api.Tree +import org.apache.jackrabbit.oak.namepath.NamePathMapper +import org.apache.jackrabbit.oak.spi.commit.CommitHook +import org.apache.jackrabbit.oak.spi.commit.MoveTracker +import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider +import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer +import org.apache.jackrabbit.oak.spi.lifecycle.WorkspaceInitializer +import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters +import org.apache.jackrabbit.oak.spi.security.Context +import org.apache.jackrabbit.oak.spi.security.SecurityProvider +import org.apache.jackrabbit.oak.spi.security.authentication.Authentication +import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration +import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenProvider +import org.apache.jackrabbit.oak.spi.security.authorization.restriction.Restriction +import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionDefinition +import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionPattern +import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider +import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration +import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider +import org.apache.jackrabbit.oak.spi.security.user.AuthorizableNodeName +import org.apache.jackrabbit.oak.spi.security.user.UserAuthenticationFactory +import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration +import org.apache.jackrabbit.oak.spi.security.user.action.AuthorizableAction +import org.apache.jackrabbit.oak.spi.security.user.action.AuthorizableActionProvider +import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter +import org.junit.Before +import org.junit.Test +import org.osgi.framework.ServiceReference +import org.osgi.service.cm.ConfigurationAdmin + +import javax.annotation.Nonnull +import javax.annotation.Nullable +import javax.jcr.RepositoryException +import javax.jcr.Value +import javax.jcr.security.AccessControlException +import java.security.Principal +import java.util.concurrent.TimeUnit + +class SecurityProviderRegistrationTest extends AbstractRepositoryFactoryTest { + + private PojoServiceRegistry registry; + + @Before + public void initializeRegistry() { + registry = repositoryFactory.initializeServiceRegistry(config) + } + + /** + * Test that, without any additional configuration, a SecurityProvider + * service is registered by default. + */ + @Test + public void testDefaultSetup() { + assert securityProviderServiceReferences != null + } + + /** + * A SecurityProvider shouldn't start without a required + * PrincipalConfiguration service. + */ + @Test + public void testRequiredPrincipalConfigurationNotAvailable() { + testRequiredService(PrincipalConfiguration, newPrincipalConfiguration()) + } + + /** + * A SecurityProvider shouldn't start without a required TokenConfiguration + * service. + */ + @Test + public void testRequiredTokenConfigurationNotAvailable() { + testRequiredService(TokenConfiguration, newTokenConfiguration()) + } + + /** + * A SecurityProvider shouldn't start without a required + * AuthorizableNodeName service. + */ + @Test + public void testRequiredAuthorizableNodeNameNotAvailable() { + testRequiredService(AuthorizableNodeName, newAuthorizableNodeName()) + } + + /** + * A SecurityProvider shouldn't start without a required + * AuthorizableActionProvider service. + */ + @Test + public void testRequiredAuthorizableActionProviderNotAvailable() { + testRequiredService(AuthorizableActionProvider, newAuthorizableActionProvider()) + } + + /** + * A SecurityProvider shouldn't start without a required RestrictionProvider + * service. + */ + @Test + public void testRequiredRestrictionProviderNotAvailable() { + testRequiredService(RestrictionProvider, newRestrictionProvider()) + } + + /** + * A SecurityProvider shouldn't start without a required + * UserAuthenticationFactory service. + */ + @Test + public void testRequiredUserAuthenticationFactoryNotAvailable() { + testRequiredService(UserAuthenticationFactory, newUserAuthenticationFactory()) + } + + /** + * A SecurityProvider should be registered only if every every prerequisite + * is satisfied. + */ + @Test + public void testMultipleRequiredServices() { + + // Set up the SecurityProvider to require three services + + setRequiredServicePids("test.RequiredPrincipalConfiguration", "test.RequiredTokenConfiguration", "test.AuthorizableNodeName") + TimeUnit.MILLISECONDS.sleep(500) + assert securityProviderServiceReferences == null + + // Start the services and verify that only at the end the + // SecurityProvider registers itself + + registry.registerService(PrincipalConfiguration.class.name, newPrincipalConfiguration(), dict("service.pid": "test.RequiredPrincipalConfiguration")) + assert securityProviderServiceReferences == null + + registry.registerService(TokenConfiguration.class.name, newTokenConfiguration(), dict("service.pid": "test.RequiredTokenConfiguration")) + assert securityProviderServiceReferences == null + + registry.registerService(TokenConfiguration.class.name, newTokenConfiguration(), dict("service.pid": "test.AuthorizableNodeName")) + assert securityProviderServiceReferences != null + } + + private void testRequiredService(Class serviceClass, T service) { + + // Adding a new precondition on a missing service PID forces the + // SecurityProvider to unregister. + + setRequiredServicePids("test.Required" + serviceClass.simpleName) + TimeUnit.MILLISECONDS.sleep(500) + assert securityProviderServiceReferences == null + + // If a service is registered, and if the PID of the service matches the + // precondition, the SecurityProvider is registered again. + + def registration = registry.registerService(serviceClass.name, service, dict("service.pid": "test.Required" + serviceClass.simpleName)) + assert securityProviderServiceReferences != null + + // If the service is unregistered, but the precondition is still in + // place, the SecurityProvider unregisters again. + + registration.unregister() + assert securityProviderServiceReferences == null + + // Removing the precondition allows the SecurityProvider to register. + + setRequiredServicePids() + TimeUnit.MILLISECONDS.sleep(500) + assert securityProviderServiceReferences != null + } + + private ServiceReference[] getSecurityProviderServiceReferences() { + return registry.getServiceReferences(SecurityProvider.class.name, null) + } + + private void setRequiredServicePids(String... pids) { + setConfiguration([ + "org.apache.jackrabbit.oak.security.SecurityProviderRegistration": [ + "requiredServicePids": pids + ] + ]) + } + + private void setConfiguration(Map> configuration) { + getConfigurationInstaller().installConfigs(configuration) + } + + private ConfigInstaller getConfigurationInstaller() { + return new ConfigInstaller(getConfigurationAdmin(), registry.bundleContext) + } + + private ConfigurationAdmin getConfigurationAdmin() { + return registry.getService(registry.getServiceReference(ConfigurationAdmin.class.name)) as ConfigurationAdmin + } + + private static Dictionary dict(Map map) { + return new Hashtable(map); + } + + private static PrincipalConfiguration newPrincipalConfiguration() { + return new PrincipalConfiguration() { + + @Override + PrincipalManager getPrincipalManager(Root root, NamePathMapper namePathMapper) { + return null + } + + @Override + PrincipalProvider getPrincipalProvider(Root root, NamePathMapper namePathMapper) { + return null + } + + @Override + String getName() { + return null + } + + @Override + ConfigurationParameters getParameters() { + return null + } + + @Override + WorkspaceInitializer getWorkspaceInitializer() { + return null + } + + @Override + RepositoryInitializer getRepositoryInitializer() { + return null + } + + @Override + List getCommitHooks( + @Nonnull String workspaceName) { + return null + } + + @Override + List getValidators( + @Nonnull String workspaceName, + @Nonnull Set principals, + @Nonnull MoveTracker moveTracker) { + return null + } + + @Override + List getProtectedItemImporters() { + return null + } + + @Override + Context getContext() { + return null + } + + } + } + + private static TokenConfiguration newTokenConfiguration() { + return new TokenConfiguration() { + + @Override + TokenProvider getTokenProvider(Root root) { + return null + } + + @Override + String getName() { + return null + } + + @Override + ConfigurationParameters getParameters() { + return null + } + + @Override + WorkspaceInitializer getWorkspaceInitializer() { + return null + } + + @Override + RepositoryInitializer getRepositoryInitializer() { + return null + } + + @Override + List getCommitHooks( + @Nonnull String workspaceName) { + return null + } + + @Override + List getValidators( + @Nonnull String workspaceName, + @Nonnull Set principals, + @Nonnull MoveTracker moveTracker) { + return null + } + + @Override + List getProtectedItemImporters() { + return null + } + + @Override + Context getContext() { + return null + } + + } + } + + private static AuthorizableNodeName newAuthorizableNodeName() { + return new AuthorizableNodeName() { + + @Override + String generateNodeName(@Nonnull String authorizableId) { + return null + } + + } + } + + private static AuthorizableActionProvider newAuthorizableActionProvider() { + return new AuthorizableActionProvider() { + + @Override + List getAuthorizableActions( + @Nonnull SecurityProvider securityProvider) { + return null + } + + } + } + + private static RestrictionProvider newRestrictionProvider() { + new RestrictionProvider() { + + @Override + Set getSupportedRestrictions( + @Nullable String oakPath) { + return null + } + + @Override + Restriction createRestriction( + @Nullable String oakPath, + @Nonnull String oakName, + @Nonnull Value value) throws AccessControlException, RepositoryException { + return null + } + + @Override + Restriction createRestriction( + @Nullable String oakPath, + @Nonnull String oakName, + @Nonnull Value... values) throws AccessControlException, RepositoryException { + return null + } + + @Override + Set readRestrictions( + @Nullable String oakPath, @Nonnull Tree aceTree) { + return null + } + + @Override + void writeRestrictions(String oakPath, Tree aceTree, Set restrictions) throws RepositoryException { + + } + + @Override + void validateRestrictions( + @Nullable String oakPath, + @Nonnull Tree aceTree) throws AccessControlException, RepositoryException { + + } + + @Override + RestrictionPattern getPattern( + @Nullable String oakPath, @Nonnull Tree tree) { + return null + } + + @Override + RestrictionPattern getPattern( + @Nullable String oakPath, + @Nonnull Set restrictions) { + return null + } + + } + } + + private static UserAuthenticationFactory newUserAuthenticationFactory() { + return new UserAuthenticationFactory() { + + @Override + Authentication getAuthentication( + @Nonnull UserConfiguration configuration, + @Nonnull Root root, @Nullable String userId) { + return null + } + + } + } + +}