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 1703179) +++ 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,112 @@ +/* + * 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; + } + +} 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 1703179) +++ 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,937 @@ +/* + * 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.api.security.authorization.PrivilegeManager; +import org.apache.jackrabbit.api.security.user.UserManager; +import org.apache.jackrabbit.oak.api.ContentRepository; +import org.apache.jackrabbit.oak.api.Root; +import org.apache.jackrabbit.oak.commons.PropertiesUtil; +import org.apache.jackrabbit.oak.namepath.NamePathMapper; +import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard; +import org.apache.jackrabbit.oak.security.user.UserConfigurationImpl; +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.ConfigurationBase; +import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; +import org.apache.jackrabbit.oak.spi.security.Context; +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.LoginContextProvider; +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.permission.PermissionProvider; +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.principal.PrincipalProvider; +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.Registration; +import org.apache.jackrabbit.oak.spi.whiteboard.Tracker; +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.WhiteboardRestrictionProvider; +import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUserAuthenticationFactory; +import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.jcr.security.AccessControlManager; +import java.security.Principal; +import java.util.List; +import java.util.Map; +import java.util.Set; + +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 { + + @Reference + private AuthorizationConfiguration authorizationConfiguration; + + @Reference + private AuthenticationConfiguration authenticationConfiguration; + + @Reference + private PrivilegeConfiguration privilegeConfiguration; + + @Reference + private UserConfiguration userConfiguration; + + private BundleContext context; + + private ServiceRegistration registration; + + 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(); + + private final SecurityProvider securityProvider = createSecurityProvider(); + + @Activate + public void activate(BundleContext context, Map configuration) { + String[] requiredServicePids = getRequiredServicePids(configuration); + + synchronized (this) { + for (String pid : requiredServicePids) { + preconditions.addPrecondition(pid); + } + + this.context = context; + } + + maybeActivate(); + } + + @Modified + public void modified(Map configuration) { + String[] requiredServicePids = getRequiredServicePids(configuration); + + synchronized (this) { + preconditions.clearPreconditions(); + + for (String pid : requiredServicePids) { + preconditions.addPrecondition(pid); + } + } + + maybeDeactivate(); + maybeActivate(); + } + + @Deactivate + public void deactivate() { + ServiceRegistration r; + + synchronized (this) { + r = registration; + + registration = null; + context = null; + + preconditions.clearPreconditions(); + } + + if (r != null) { + r.unregister(); + } + } + + public void bindAuthorizationConfiguration(AuthorizationConfiguration authorizationConfiguration) { + this.authorizationConfiguration = initializeConfiguration(authorizationConfiguration); + } + + public void unbindAuthorizationConfiguration(AuthorizationConfiguration authorizationConfiguration) { + this.authorizationConfiguration = null; + } + + public void bindAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) { + this.authenticationConfiguration = initializeConfiguration(authenticationConfiguration); + } + + public void unbindAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) { + this.authenticationConfiguration = null; + } + + public void bindPrivilegeConfiguration(PrivilegeConfiguration privilegeConfiguration) { + this.privilegeConfiguration = initializeConfiguration(privilegeConfiguration); + } + + public void unbindPrivilegeConfiguration(PrivilegeConfiguration privilegeConfiguration) { + this.privilegeConfiguration = null; + } + + public void bindUserConfiguration(UserConfiguration userConfiguration) { + this.userConfiguration = initializeConfiguration(userConfiguration); + } + + public void unbindUserConfiguration(UserConfiguration userConfiguration) { + this.userConfiguration = null; + } + + public void bindPrincipalConfiguration(PrincipalConfiguration principalConfiguration, Map properties) { + synchronized (this) { + principalConfigurations.add(initializeConfiguration(principalConfiguration)); + addCandidate(properties); + } + + maybeActivate(); + } + + public void unbindPrincipalConfiguration(PrincipalConfiguration principalConfiguration, Map properties) { + synchronized (this) { + principalConfigurations.remove(principalConfiguration); + removeCandidate(properties); + } + + maybeDeactivate(); + } + + public void bindTokenConfiguration(TokenConfiguration tokenConfiguration, Map properties) { + synchronized (this) { + tokenConfigurations.add(initializeConfiguration(tokenConfiguration)); + addCandidate(properties); + } + + maybeActivate(); + } + + public void unbindTokenConfiguration(TokenConfiguration tokenConfiguration, Map properties) { + synchronized (this) { + tokenConfigurations.remove(tokenConfiguration); + removeCandidate(properties); + } + + maybeDeactivate(); + } + + public void bindAuthorizableNodeName(AuthorizableNodeName authorizableNodeName, Map properties) { + synchronized (this) { + authorizableNodeNames.add(authorizableNodeName); + addCandidate(properties); + } + + maybeActivate(); + } + + public void unbindAuthorizableNodeName(AuthorizableNodeName authorizableNodeName, Map properties) { + synchronized (this) { + authorizableNodeNames.remove(authorizableNodeName); + removeCandidate(properties); + } + + maybeDeactivate(); + } + + public void bindAuthorizableActionProvider(AuthorizableActionProvider authorizableActionProvider, Map properties) { + synchronized (this) { + authorizableActionProviders.add(authorizableActionProvider); + addCandidate(properties); + } + + maybeActivate(); + } + + public void unbindAuthorizableActionProvider(AuthorizableActionProvider authorizableActionProvider, Map properties) { + synchronized (this) { + authorizableActionProviders.remove(authorizableActionProvider); + removeCandidate(properties); + } + + maybeDeactivate(); + } + + public void bindRestrictionProvider(RestrictionProvider restrictionProvider, Map properties) { + synchronized (this) { + restrictionProviders.add(restrictionProvider); + addCandidate(properties); + } + + maybeActivate(); + } + + public void unbindRestrictionProvider(RestrictionProvider restrictionProvider, Map properties) { + synchronized (this) { + restrictionProviders.remove(restrictionProvider); + removeCandidate(properties); + } + + maybeDeactivate(); + } + + public void bindUserAuthenticationFactory(UserAuthenticationFactory userAuthenticationFactory, Map properties) { + synchronized (this) { + userAuthenticationFactories.add(userAuthenticationFactory); + addCandidate(properties); + } + + maybeActivate(); + } + + public void unbindUserAuthenticationFactory(UserAuthenticationFactory userAuthenticationFactory, Map properties) { + synchronized (this) { + userAuthenticationFactories.remove(userAuthenticationFactory); + removeCandidate(properties); + } + + maybeDeactivate(); + } + + /** + * Create a new instance of {@code SecurityProvider} and register it as a + * service if some conditions hold. + *

+ * A new service is registered if (1) this component is activated, (2) the + * preconditions specified via OSGi configuration are satisfied, and (3) a + * {@code SecurityProvider} service was not already registered by a previous + * invocation of this method. + */ + private void maybeActivate() { + BundleContext bc; + + 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 (context == null) { + 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()) { + 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) { + return; + } + + bc = this.context; + } + + // Register the SecurityProvider. + + ServiceRegistration r = bc.registerService(SecurityProvider.class.getName(), createSecurityProvider(), null); + + synchronized (this) { + + // Check if another SecurityProvider was registered between the time + // we checked the state of the component and the end of the + // registration process. + + if (registration == null) { + registration = r; + r = null; + } + } + + // Unregister the newly registered SecurityProvider if it is + // out-of-date. + + if (r != null) { + r.unregister(); + } + } + + /** + * Unregister a previously registered {@code SecurityProvider} service if + * the preconditions specified via OSGi configuration are satisfied. + */ + private void maybeDeactivate() { + ServiceRegistration r; + + synchronized (this) { + + // If there is nothing to register, we obviously have nothing to do. + + if (registration == null) { + return; + } + + // The preconditions are not satisfied. This may happen when a + // dependency is unbound from the current component. + + if (preconditions.areSatisfied()) { + return; + } + + r = registration; + registration = null; + } + + if (r != null) { + r.unregister(); + } + } + + /** + * Initialize a referenced {@code AuthorizationConfiguration} service. The + * service will be linked to the {@code SecurityProvider} managed by this + * component and to the {@code RestrictionProvider} services registered into + * the framework. + * + * @param authorizationConfiguration A {@code AuthorizationConfiguration} + * service referenced by this component. + * @return The same {@code AuthorizationConfiguration} service passed as a + * parameter after being initialized. + */ + private AuthorizationConfiguration initializeConfiguration(AuthorizationConfiguration authorizationConfiguration) { + return initializeConfiguration(authorizationConfiguration, ConfigurationParameters.of( + AccessControlConstants.PARAM_RESTRICTION_PROVIDER, createDelegatingRestrictionProvider() + )); + } + + /** + * Initialize a referenced {@code UserConfiguration} service. The service + * will be linked to the {@code SecurityProvider} managed by this component + * and to the {@code AuthorizableActionProvider}, {@code + * AuthorizableNodeName} and {@code UserAuthenticationFactory} registered + * into the framework. + * + * @param userConfiguration A {@code UserConfiguration} service referenced + * by this component. + * @return The same {@code UserConfiguration} service passed as a parameter + * after being initialized. + */ + private UserConfiguration initializeConfiguration(UserConfiguration userConfiguration) { + return initializeConfiguration(userConfiguration, ConfigurationParameters.of( + ConfigurationParameters.of(UserConstants.PARAM_AUTHORIZABLE_ACTION_PROVIDER, createDelegatingAuthorizableActionProvider()), + ConfigurationParameters.of(UserConstants.PARAM_AUTHORIZABLE_NODE_NAME, createDelegatingAuthorizableNodeName()), + ConfigurationParameters.of(UserConstants.PARAM_USER_AUTHENTICATION_FACTORY, createDelegatingUserAuthenticationFactory()) + )); + } + + + private T initializeConfiguration(T configuration) { + return initializeConfiguration(configuration, ConfigurationParameters.EMPTY); + } + + /** + * Initialize a referenced {@code SecurityConfiguration} services. The + * service will be linked to the {@code SecurityProvider} managed by this + * component, and its parameters will be merged with the parameters passed + * to this method. + * + * @param configuration An instance of {@code SecurityConfiguration} to + * initialize. + * @param parameters The parameters to merge with the ones already hold + * by the configuration. + * @param The type of the {@code SecurityConfiguration} + * instance. + * @return The same {@code SecurityConfiguration} after being initialized. + */ + private T initializeConfiguration(T configuration, ConfigurationParameters parameters) { + if (configuration instanceof ConfigurationBase) { + ConfigurationBase base = (ConfigurationBase) configuration; + base.setSecurityProvider(createDelegatingSecurityProvider()); + base.setParameters(ConfigurationParameters.of(parameters, base.getParameters())); + } + + return configuration; + } + + /** + * Create a {@code SecurityProvider} that references its configurations from + * the OSGi framework. + * + * @return An instance of {@code SecurityProvider}. + */ + private SecurityProvider createSecurityProvider() { + SecurityProviderImpl securityProvider = new SecurityProviderImpl(); + + securityProvider.setAuthenticationConfiguration(createDelegatinAuthenticationConfiguration()); + securityProvider.setAuthorizationConfiguration(createDelegatingAuthorizationConfiguration()); + securityProvider.setUserConfiguration(createDelegatingUserConfiguration()); + securityProvider.setPrivilegeConfiguration(createDelegatingPrivilegeConfiguration()); + securityProvider.setPrincipalConfiguration(createDelegatingPrincipalConfiguration()); + securityProvider.setTokenConfiguration(createDelegatingTokenConfiguration()); + securityProvider.setWhiteboard(createDelegatingWhiteboard()); + + return securityProvider; + } + + /** + * Create a {@code PrincipalConfiguration} that aggregates every {@code + * PrincipalConfiguration} service referenced by this component. + * + * @return An instance of {@code PrincipalConfiguration}. + */ + private PrincipalConfiguration createDelegatingPrincipalConfiguration() { + return new CompositePrincipalConfiguration(createDelegatingSecurityProvider()) { + + @Override + protected List getConfigurations() { + return newArrayList(principalConfigurations); + } + + }; + } + + /** + * Create a {@code TokenConfiguration} that aggregates every {@code + * TokenConfiguration} service referenced by this component. + * + * @return An instance of {@code TokenConfiguration}. + */ + private TokenConfiguration createDelegatingTokenConfiguration() { + return new CompositeTokenConfiguration(createDelegatingSecurityProvider()) { + + @Override + protected List getConfigurations() { + return newArrayList(tokenConfigurations); + } + + }; + } + + /** + * Create an instance of {@code SecurityProvider} that delegates its call to + * the {@code SecurityProvider} registered by this component. If no {@code + * SecurityProvider} is registered yet, the returned instance will just + * throw {@code IllegalStateException}. + *

+ * This delegate is necessary because some configurations require a {@code + * SecurityProvider} at construction time. The delegate can be considered a + * "promise" that a {@code SecurityProvider} will be registered when the + * right conditions are met. + * + * @return An instance of {@code SecurityProvider} + */ + private SecurityProvider createDelegatingSecurityProvider() { + return new SecurityProvider() { + + @Nonnull + @Override + public ConfigurationParameters getParameters(@Nullable String name) { + return securityProvider.getParameters(name); + } + + @Nonnull + @Override + public Iterable getConfigurations() { + return securityProvider.getConfigurations(); + } + + @Nonnull + @Override + public T getConfiguration(@Nonnull Class configClass) { + return securityProvider.getConfiguration(configClass); + } + + }; + } + + /** + * Create a {@code RestrictionProvider} that aggregates every {@code + * RestrictionProvider} service referenced by this component. + * + * @return An instance of {@code RestrictionProvider}. + */ + private RestrictionProvider createDelegatingRestrictionProvider() { + return new WhiteboardRestrictionProvider() { + + @Override + protected List getServices() { + return newArrayList(restrictionProviders); + } + + }; + } + + /** + * Create a {@code AuthorizableActionProvider} that aggregates every {@code + * AuthorizableActionProvider} service referenced by this component. + * + * @return An instance of {@code RestrictionProvider}. + */ + private AuthorizableActionProvider createDelegatingAuthorizableActionProvider() { + return new WhiteboardAuthorizableActionProvider() { + + @Override + protected List getServices() { + return newArrayList(authorizableActionProviders); + } + + }; + } + + /** + * Create a {@code AuthorizableNodeName} that aggregates every {@code + * AuthorizableNodeName} service referenced by this component. + * + * @return An instance of {@code AuthorizableNodeName}. + */ + private AuthorizableNodeName createDelegatingAuthorizableNodeName() { + return new WhiteboardAuthorizableNodeName() { + + @Override + protected List getServices() { + return newArrayList(authorizableNodeNames); + } + + }; + } + + /** + * Create a {@code UserAuthenticationFactory} that aggregates every {@code + * UserAuthenticationFactory} service referenced by this component. + * + * @return An instance of {@code UserAuthenticationFactory}. + */ + private UserAuthenticationFactory createDelegatingUserAuthenticationFactory() { + return new WhiteboardUserAuthenticationFactory(UserConfigurationImpl.getDefaultAuthenticationFactory()) { + + @Override + protected List getServices() { + return newArrayList(userAuthenticationFactories); + } + + }; + } + + private AuthenticationConfiguration createDelegatinAuthenticationConfiguration() { + return new DelegatingAuthenticationConfiguration() { + + @Override + protected AuthenticationConfiguration getAuthenticationConfiguration() { + return authenticationConfiguration; + } + + }; + } + + private AuthorizationConfiguration createDelegatingAuthorizationConfiguration() { + return new DelegatinAuthorizationConfiguration() { + + @Override + protected AuthorizationConfiguration getAuthorizationConfiguration() { + return authorizationConfiguration; + } + + }; + } + + private PrivilegeConfiguration createDelegatingPrivilegeConfiguration() { + return new DelegatingPrivilegeConfiguration() { + + @Override + protected PrivilegeConfiguration getPrivilegeConfiguration() { + return privilegeConfiguration; + } + + }; + } + + private UserConfiguration createDelegatingUserConfiguration() { + return new DelegatingUserConfiguration() { + + @Override + protected UserConfiguration getUserConfiguration() { + return userConfiguration; + } + + }; + } + + private Whiteboard createDelegatingWhiteboard() { + return new DelegatingWhiteboard() { + + @Override + protected Whiteboard getWhiteboard() { + return new OsgiWhiteboard(context); + } + + }; + } + + 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[]{}); + } + + private static abstract class DelegatingSecurityConfiguration implements SecurityConfiguration { + + protected abstract SecurityConfiguration getSecurityConfiguration(); + + @Nonnull + @Override + public String getName() { + return getSecurityConfiguration().getName(); + } + + @Nonnull + @Override + public ConfigurationParameters getParameters() { + return getSecurityConfiguration().getParameters(); + } + + @Nonnull + @Override + public WorkspaceInitializer getWorkspaceInitializer() { + return getSecurityConfiguration().getWorkspaceInitializer(); + } + + @Nonnull + @Override + public RepositoryInitializer getRepositoryInitializer() { + return getSecurityConfiguration().getRepositoryInitializer(); + } + + @Nonnull + @Override + public List getCommitHooks(@Nonnull String workspaceName) { + return getSecurityConfiguration().getCommitHooks(workspaceName); + } + + @Nonnull + @Override + public List getValidators(@Nonnull String workspaceName, @Nonnull Set principals, @Nonnull MoveTracker moveTracker) { + return getSecurityConfiguration().getValidators(workspaceName, principals, moveTracker); + } + + @Nonnull + @Override + public List getProtectedItemImporters() { + return getSecurityConfiguration().getProtectedItemImporters(); + } + + @Nonnull + @Override + public Context getContext() { + return getSecurityConfiguration().getContext(); + } + + } + + private static abstract class DelegatinAuthorizationConfiguration extends DelegatingSecurityConfiguration implements AuthorizationConfiguration { + + protected abstract AuthorizationConfiguration getAuthorizationConfiguration(); + + @Nonnull + @Override + public AccessControlManager getAccessControlManager(@Nonnull Root root, @Nonnull NamePathMapper namePathMapper) { + return getAuthorizationConfiguration().getAccessControlManager(root, namePathMapper); + } + + @Nonnull + @Override + public RestrictionProvider getRestrictionProvider() { + return getAuthorizationConfiguration().getRestrictionProvider(); + } + + @Nonnull + @Override + public PermissionProvider getPermissionProvider(@Nonnull Root root, @Nonnull String workspaceName, @Nonnull Set principals) { + return getAuthorizationConfiguration().getPermissionProvider(root, workspaceName, principals); + } + + @Override + protected SecurityConfiguration getSecurityConfiguration() { + return getAuthorizationConfiguration(); + } + + } + + private static abstract class DelegatingAuthenticationConfiguration extends DelegatingSecurityConfiguration implements AuthenticationConfiguration { + + protected abstract AuthenticationConfiguration getAuthenticationConfiguration(); + + @Nonnull + @Override + public LoginContextProvider getLoginContextProvider(ContentRepository contentRepository) { + return getAuthenticationConfiguration().getLoginContextProvider(contentRepository); + } + + @Override + protected SecurityConfiguration getSecurityConfiguration() { + return getAuthenticationConfiguration(); + } + + } + + private static abstract class DelegatingUserConfiguration extends DelegatingSecurityConfiguration implements UserConfiguration { + + protected abstract UserConfiguration getUserConfiguration(); + + @Override + protected SecurityConfiguration getSecurityConfiguration() { + return getUserConfiguration(); + } + + @Nonnull + @Override + public UserManager getUserManager(Root root, NamePathMapper namePathMapper) { + return getUserConfiguration().getUserManager(root, namePathMapper); + } + + @Nullable + @Override + public PrincipalProvider getUserPrincipalProvider(@Nonnull Root root, @Nonnull NamePathMapper namePathMapper) { + return getUserConfiguration().getUserPrincipalProvider(root, namePathMapper); + } + + } + + private static abstract class DelegatingPrivilegeConfiguration extends DelegatingSecurityConfiguration implements PrivilegeConfiguration { + + protected abstract PrivilegeConfiguration getPrivilegeConfiguration(); + + @Override + protected SecurityConfiguration getSecurityConfiguration() { + return getPrivilegeConfiguration(); + } + + @Nonnull + @Override + public PrivilegeManager getPrivilegeManager(Root root, NamePathMapper namePathMapper) { + return getPrivilegeConfiguration().getPrivilegeManager(root, namePathMapper); + } + + } + + private static abstract class DelegatingWhiteboard implements Whiteboard { + + protected abstract Whiteboard getWhiteboard(); + + @Override + public Registration register(Class type, T service, Map properties) { + return getWhiteboard().register(type, service, properties); + } + + @Override + public Tracker track(Class type) { + return getWhiteboard().track(type); + } + + } + +} 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 1703179) +++ 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 1703179) +++ 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 1703179) +++ 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 1703179) +++ 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 1703179) +++ 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 1703179) +++ 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 1703179) +++ 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 1703179) +++ 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 1703179) +++ 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 1703179) +++ 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 1703179) +++ 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 1703179) +++ 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 + } + + } + } + +}