You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by an...@apache.org on 2016/06/01 07:11:59 UTC
svn commit: r1746408 - in /jackrabbit/oak/trunk:
oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/
oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/pr...
Author: angela
Date: Wed Jun 1 07:11:59 2016
New Revision: 1746408
URL: http://svn.apache.org/viewvc?rev=1746408&view=rev
Log:
OAK-4087 : Replace Sync of configured AutoMembership by Dynamic Principal Generation
Added:
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/SyncHandlerMapping.java (with props)
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleAutoMembershipTest.java (with props)
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/PrincipalProviderAutoMembershipTest.java (with props)
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/external/
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/external/defaultusersync.md
- copied, changed from r1746238, jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/defaultusersync.md
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/external/dynamic.md
Removed:
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/defaultusersync.md
Modified:
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContext.java
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleFactory.java
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalProvider.java
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfiguration.java
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTestBase.java
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AbstractPrincipalTest.java
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalProviderTest.java
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/externalloginmodule.md
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/usersync.md
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/principal/principalprovider.md
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/authentication/external/AbstractExternalTest.java
Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContext.java?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContext.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContext.java Wed Jun 1 07:11:59 2016
@@ -135,6 +135,11 @@ public class DynamicSyncContext extends
}
}
+ @Override
+ protected void applyMembership(@Nonnull Authorizable member, @Nonnull Set<String> groups) throws RepositoryException {
+ log.debug("Dynamic membership sync enabled => omit setting auto-membership for {} ", member.getID());
+ }
+
/**
* Recursively collect the principal names of the given declared group
* references up to the given depth.
Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java Wed Jun 1 07:11:59 2016
@@ -70,12 +70,12 @@ public class ExternalLoginModule extends
/**
* Name of the parameter that configures the name of the external identity provider.
*/
- public static final String PARAM_IDP_NAME = "idp.name";
+ public static final String PARAM_IDP_NAME = SyncHandlerMapping.PARAM_IDP_NAME;
/**
* Name of the parameter that configures the name of the synchronization handler.
*/
- public static final String PARAM_SYNC_HANDLER_NAME = "sync.handlerName";
+ public static final String PARAM_SYNC_HANDLER_NAME = SyncHandlerMapping.PARAM_SYNC_HANDLER_NAME;
private ExternalIdentityProviderManager idpManager;
Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleFactory.java?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleFactory.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleFactory.java Wed Jun 1 07:11:59 2016
@@ -17,12 +17,12 @@
package org.apache.jackrabbit.oak.spi.security.authentication.external.impl;
import java.util.Hashtable;
-
import javax.jcr.Repository;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.security.auth.spi.LoginModule;
+import com.google.common.collect.ImmutableMap;
import org.apache.felix.jaas.LoginModuleFactory;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
@@ -43,8 +43,6 @@ import org.osgi.service.component.Compon
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.collect.ImmutableMap;
-
/**
* Implements a LoginModuleFactory that creates {@link ExternalLoginModule}s and allows to configure login modules
* via OSGi config.
@@ -56,7 +54,7 @@ import com.google.common.collect.Immutab
configurationFactory = true
)
@Service
-public class ExternalLoginModuleFactory implements LoginModuleFactory {
+public class ExternalLoginModuleFactory implements LoginModuleFactory, SyncHandlerMapping {
private static final Logger log = LoggerFactory.getLogger(ExternalLoginModuleFactory.class);
@@ -92,14 +90,14 @@ public class ExternalLoginModuleFactory
label = "Identity Provider Name",
description = "Name of the identity provider (for example: 'ldap')."
)
- public static final String PARAM_IDP_NAME = ExternalLoginModule.PARAM_IDP_NAME;
+ public static final String PARAM_IDP_NAME = SyncHandlerMapping.PARAM_IDP_NAME;
@Property(
value = "default",
label = "Sync Handler Name",
description = "Name of the sync handler."
)
- public static final String PARAM_SYNC_HANDLER_NAME = ExternalLoginModule.PARAM_SYNC_HANDLER_NAME;
+ public static final String PARAM_SYNC_HANDLER_NAME = SyncHandlerMapping.PARAM_SYNC_HANDLER_NAME;
@Reference
private SyncManager syncManager;
@@ -120,6 +118,7 @@ public class ExternalLoginModuleFactory
*/
private Registration mbeanRegistration;
+ //----------------------------------------------------< SCR integration >---
/**
* Activates the LoginModuleFactory service
* @param context the component context
Added: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/SyncHandlerMapping.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/SyncHandlerMapping.java?rev=1746408&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/SyncHandlerMapping.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/SyncHandlerMapping.java Wed Jun 1 07:11:59 2016
@@ -0,0 +1,43 @@
+/*
+ * 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.spi.security.authentication.external.impl;
+
+/**
+ * Marker interface identifying classes that map a given
+ * {@link org.apache.jackrabbit.oak.spi.security.authentication.external.SyncHandler SyncHandler}
+ * to an {@link org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProvider ExternalIdentityProvider}
+ * where both are identified by their name.
+ *
+ * @see org.apache.jackrabbit.oak.spi.security.authentication.external.SyncManager#getSyncHandler(String)
+ * @see org.apache.jackrabbit.oak.spi.security.authentication.external.SyncHandler#getName()
+ * @see org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProviderManager#getProvider(String)
+ * @see org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProvider#getName()
+ * @see ExternalLoginModuleFactory
+ */
+public interface SyncHandlerMapping {
+
+ /**
+ * Name of the parameter that configures the name of the external identity provider.
+ */
+ String PARAM_IDP_NAME = "idp.name";
+
+ /**
+ * Name of the parameter that configures the name of the synchronization handler.
+ */
+ String PARAM_SYNC_HANDLER_NAME = "sync.handlerName";
+
+}
\ No newline at end of file
Propchange: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/SyncHandlerMapping.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalProvider.java?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalProvider.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalProvider.java Wed Jun 1 07:11:59 2016
@@ -19,12 +19,14 @@ package org.apache.jackrabbit.oak.spi.se
import java.security.Principal;
import java.security.acl.Group;
import java.text.ParseException;
+import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -54,6 +56,7 @@ import org.apache.jackrabbit.oak.api.Tre
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.namepath.NamePathMapper;
import org.apache.jackrabbit.oak.spi.query.PropertyValues;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityRef;
import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncConfig;
import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalIdentityConstants;
import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
@@ -95,12 +98,18 @@ class ExternalGroupPrincipalProvider imp
private final Root root;
private final NamePathMapper namePathMapper;
+
private final UserManager userManager;
+ private final AutoMembershipPrincipals autoMembershipPrincipals;
- ExternalGroupPrincipalProvider(Root root, UserConfiguration uc, NamePathMapper namePathMapper) {
+ ExternalGroupPrincipalProvider(@Nonnull Root root, @Nonnull UserConfiguration uc,
+ @Nonnull NamePathMapper namePathMapper,
+ @Nonnull Map<String, String[]> autoMembershipMapping) {
this.root = root;
this.namePathMapper = namePathMapper;
+
userManager = uc.getUserManager(root, namePathMapper);
+ autoMembershipPrincipals = new AutoMembershipPrincipals(autoMembershipMapping);
}
//--------------------------------------------------< PrincipalProvider >---
@@ -164,6 +173,15 @@ class ExternalGroupPrincipalProvider imp
}
//------------------------------------------------------------< private >---
+ @CheckForNull
+ private String getIdpName(@Nonnull Tree userTree) {
+ PropertyState ps = userTree.getProperty(REP_EXTERNAL_ID);
+ if (ps != null) {
+ return ExternalIdentityRef.fromString(ps.getValue(Type.STRING)).getProviderName();
+ } else {
+ return null;
+ }
+ }
private Set<Group> getGroupPrincipals(@CheckForNull Authorizable authorizable) throws RepositoryException {
if (authorizable != null && !authorizable.isGroup()) {
@@ -178,10 +196,14 @@ class ExternalGroupPrincipalProvider imp
if (userTree.exists() && UserUtil.isType(userTree, AuthorizableType.USER) && userTree.hasProperty(REP_EXTERNAL_PRINCIPAL_NAMES)) {
PropertyState ps = userTree.getProperty(REP_EXTERNAL_PRINCIPAL_NAMES);
if (ps != null) {
+ // we have an 'external' user that has been synchronized with the dynamic-membership option
Set<Group> groupPrincipals = Sets.newHashSet();
for (String principalName : ps.getValue(Type.STRINGS)) {
groupPrincipals.add(new ExternalGroupPrincipal(principalName));
}
+
+ // add existing group principals as defined with the _autoMembership_ option.
+ groupPrincipals.addAll(autoMembershipPrincipals.get(getIdpName(userTree)));
return groupPrincipals;
}
}
@@ -194,6 +216,10 @@ class ExternalGroupPrincipalProvider imp
* {@link #REP_EXTERNAL_PRINCIPAL_NAMES} properties that match the given
* name or name hint.
*
+ * NOTE: ignore any principals listed in the {@link DefaultSyncConfig.User#autoMembership}
+ * because they are expected to exist in the system and thus will be found
+ * by another principal provider instance.
+ *
* @param nameHint The principal name or name hint to be searched for.
* @param exactMatch boolean flag indicating if the query should search for
* exact matching.
@@ -409,4 +435,54 @@ class ExternalGroupPrincipalProvider imp
return null;
}
}
+
+ private final class AutoMembershipPrincipals {
+
+ private final Map<String, String[]> autoMembershipMapping;
+ private final Map<String, Set<Group>> principalMap;
+
+ private AutoMembershipPrincipals(@Nonnull Map<String, String[]> autoMembershipMapping) {
+ this.autoMembershipMapping = autoMembershipMapping;
+ this.principalMap = new ConcurrentHashMap<String, Set<Group>>(autoMembershipMapping.size());
+ }
+
+ @Nonnull
+ private Collection<Group> get(@CheckForNull String idpName) {
+ if (idpName == null) {
+ return ImmutableSet.of();
+ }
+
+ Set<Group> principals;
+ if (!principalMap.containsKey(idpName)) {
+ String[] vs = autoMembershipMapping.get(idpName);
+ if (vs == null) {
+ principals = ImmutableSet.of();
+ } else {
+ ImmutableSet.Builder<Group> builder = ImmutableSet.builder();
+ for (String groupId : autoMembershipMapping.get(idpName)) {
+ try {
+ Authorizable gr = userManager.getAuthorizable(groupId);
+ if (gr != null && gr.isGroup()) {
+ Principal grPrincipal = gr.getPrincipal();
+ if (grPrincipal instanceof Group) {
+ builder.add((Group) grPrincipal);
+ } else {
+ log.warn("Principal of group {} is not of type java.security.acl.Group -> Ignoring", groupId);
+ }
+ } else {
+ log.warn("Configured auto-membership group {} does not exist -> Ignoring", groupId);
+ }
+ } catch (RepositoryException e) {
+ log.debug("Failed to retrieved 'auto-membership' group with id {}", groupId, e);
+ }
+ }
+ principals = builder.build();
+ }
+ principalMap.put(idpName, principals);
+ } else {
+ principals = principalMap.get(idpName);
+ }
+ return principals;
+ }
+ }
}
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfiguration.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfiguration.java?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfiguration.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfiguration.java Wed Jun 1 07:11:59 2016
@@ -18,6 +18,8 @@ package org.apache.jackrabbit.oak.spi.se
import java.security.Principal;
import java.security.acl.Group;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -26,8 +28,11 @@ import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
@@ -46,6 +51,8 @@ import org.apache.jackrabbit.oak.spi.sec
import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncHandler;
import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalLoginModuleFactory;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.SyncHandlerMapping;
import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration;
import org.apache.jackrabbit.oak.spi.security.principal.PrincipalManagerImpl;
import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
@@ -54,6 +61,8 @@ import org.apache.jackrabbit.oak.spi.xml
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Implementation of the {@code PrincipalConfiguration} interface that provides
@@ -73,7 +82,10 @@ import org.osgi.util.tracker.ServiceTrac
@Service({PrincipalConfiguration.class, SecurityConfiguration.class})
public class ExternalPrincipalConfiguration extends ConfigurationBase implements PrincipalConfiguration {
+ private static final Logger log = LoggerFactory.getLogger(ExternalPrincipalConfiguration.class);
+
private SyncConfigTracker syncConfigTracker;
+ private SyncHandlerMappingTracker syncHandlerMappingTracker;
@SuppressWarnings("UnusedDeclaration")
public ExternalPrincipalConfiguration() {
@@ -96,7 +108,7 @@ public class ExternalPrincipalConfigurat
public PrincipalProvider getPrincipalProvider(Root root, NamePathMapper namePathMapper) {
if (dynamicMembershipEnabled()) {
UserConfiguration uc = getSecurityProvider().getConfiguration(UserConfiguration.class);
- return new ExternalGroupPrincipalProvider(root, uc, namePathMapper);
+ return new ExternalGroupPrincipalProvider(root, uc, namePathMapper, syncConfigTracker.getAutoMembership());
} else {
return EmptyPrincipalProvider.INSTANCE;
}
@@ -132,8 +144,13 @@ public class ExternalPrincipalConfigurat
@Activate
private void activate(BundleContext bundleContext, Map<String, Object> properties) {
setParameters(ConfigurationParameters.of(properties));
- syncConfigTracker = new SyncConfigTracker(bundleContext);
+ syncHandlerMappingTracker = new SyncHandlerMappingTracker(bundleContext);
+ syncHandlerMappingTracker.open();
+
+ syncConfigTracker = new SyncConfigTracker(bundleContext, syncHandlerMappingTracker);
syncConfigTracker.open();
+
+
}
@SuppressWarnings("UnusedDeclaration")
@@ -142,6 +159,9 @@ public class ExternalPrincipalConfigurat
if (syncConfigTracker != null) {
syncConfigTracker.close();
}
+ if (syncHandlerMappingTracker != null) {
+ syncHandlerMappingTracker.close();
+ }
}
//------------------------------------------------------------< private >---
@@ -196,11 +216,14 @@ public class ExternalPrincipalConfigurat
*/
private static final class SyncConfigTracker extends ServiceTracker {
+ private final SyncHandlerMappingTracker mappingTracker;
+
private Set<ServiceReference> enablingRefs = new HashSet<ServiceReference>();
private boolean isEnabled = false;
- public SyncConfigTracker(BundleContext context) {
+ public SyncConfigTracker(@Nonnull BundleContext context, @Nonnull SyncHandlerMappingTracker mappingTracker) {
super(context, SyncHandler.class.getName(), null);
+ this.mappingTracker = mappingTracker;
}
@Override
@@ -234,5 +257,83 @@ public class ExternalPrincipalConfigurat
private static boolean hasDynamicMembership(ServiceReference reference) {
return PropertiesUtil.toBoolean(reference.getProperty(DefaultSyncConfigImpl.PARAM_USER_DYNAMIC_MEMBERSHIP), DefaultSyncConfigImpl.PARAM_USER_DYNAMIC_MEMBERSHIP_DEFAULT);
}
+
+ private Map<String, String[]> getAutoMembership() {
+ Map<String, String[]> autoMembership = new HashMap<String, String[]>();
+ for (ServiceReference ref : enablingRefs) {
+ String syncHandlerName = PropertiesUtil.toString(ref.getProperty(DefaultSyncConfigImpl.PARAM_NAME), DefaultSyncConfigImpl.PARAM_NAME_DEFAULT);
+ String[] membership = PropertiesUtil.toStringArray(ref.getProperty(DefaultSyncConfigImpl.PARAM_GROUP_AUTO_MEMBERSHIP), new String[0]);
+
+ for (String idpName : mappingTracker.getIdpNames(syncHandlerName)) {
+ String[] previous = autoMembership.put(idpName, membership);
+ if (previous != null) {
+ String msg = (Arrays.equals(previous, membership)) ? "Duplicate" : "Colliding";
+ log.debug(msg + " auto-membership configuration for IDP '{}'; replacing previous values {} by {} defined by SyncHandler '{}'",
+ idpName, Arrays.toString(previous), Arrays.toString(membership), syncHandlerName);
+ }
+ }
+ }
+ return autoMembership;
+ }
+ }
+
+ /**
+ * {@code ServiceTracker} to detect any {@link SyncHandler} that has
+ * dynamic membership enabled.
+ */
+ private static final class SyncHandlerMappingTracker extends ServiceTracker {
+
+ private Map<ServiceReference, String[]> referenceMap = new HashMap<ServiceReference, String[]>();
+
+ public SyncHandlerMappingTracker(@Nonnull BundleContext context) {
+ super(context, SyncHandlerMapping.class.getName(), null);
+ }
+
+ @Override
+ public Object addingService(ServiceReference reference) {
+ addMapping(reference);
+ return super.addingService(reference);
+ }
+
+ @Override
+ public void modifiedService(ServiceReference reference, Object service) {
+ addMapping(reference);
+ super.modifiedService(reference, service);
+ }
+
+ @Override
+ public void removedService(ServiceReference reference, Object service) {
+ referenceMap.remove(reference);
+ super.removedService(reference, service);
+ }
+
+ private void addMapping(ServiceReference reference) {
+ String idpName = PropertiesUtil.toString(reference.getProperty(ExternalLoginModuleFactory.PARAM_IDP_NAME), null);
+ String syncHandlerName = PropertiesUtil.toString(reference.getProperty(ExternalLoginModuleFactory.PARAM_SYNC_HANDLER_NAME), null);
+
+ if (idpName != null && syncHandlerName != null) {
+ referenceMap.put(reference, new String[]{syncHandlerName, idpName});
+ } else {
+ log.warn("Ignoring SyncHandlerMapping with incomplete mapping of IDP '{}' and SyncHandler '{}'", idpName, syncHandlerName);
+ }
+ }
+
+ private Iterable<String> getIdpNames(@Nonnull final String syncHandlerName) {
+ return Iterables.filter(Iterables.transform(referenceMap.values(), new Function<String[], String>() {
+ @Nullable
+ @Override
+ public String apply(@Nullable String[] input) {
+ if (input != null && input.length == 2) {
+ if (syncHandlerName.equals(input[0])) {
+ return input[1];
+ } // else: different sync-handler
+ } else {
+ log.warn("Unexpected value of reference map. Expected String[] with length = 2");
+ }
+ return null;
+ }
+ }
+ ), Predicates.notNull());
+ }
}
}
\ No newline at end of file
Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleAutoMembershipTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleAutoMembershipTest.java?rev=1746408&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleAutoMembershipTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleAutoMembershipTest.java Wed Jun 1 07:11:59 2016
@@ -0,0 +1,438 @@
+/*
+ * 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.spi.security.authentication.external;
+
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.ValueFactory;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncConfig;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalLoginModule;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.SyncHandlerMapping;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class ExternalLoginModuleAutoMembershipTest extends ExternalLoginModuleTestBase {
+
+ private static final String NON_EXISTING_NAME = "nonExisting";
+
+ @Rule
+ public final OsgiContext context = new OsgiContext();
+
+ private Root r;
+ private UserManager userManager;
+ private ValueFactory valueFactory;
+
+ private ExternalSetup setup1;
+ private ExternalSetup setup2;
+ private ExternalSetup setup3;
+ private ExternalSetup setup4;
+ private ExternalSetup setup5;
+
+ @Override
+ public void before() throws Exception {
+ super.before();
+
+ r = getSystemRoot();
+ userManager = getUserManager(r);
+ valueFactory = getValueFactory(r);
+
+ syncConfig.user().setDynamicMembership(true);
+
+ // register the ExternalPrincipal configuration in order to have it's
+ // activate method invoked.
+ context.registerInjectActivateService(externalPrincipalConfiguration);
+
+ // first configuration based on test base-setup with
+ // - dynamic membership = true
+ // - auto-membership = 'gr_default' and 'nonExisting'
+ syncConfig.user().setDynamicMembership(true);
+ setup1 = new ExternalSetup(idp, syncConfig, WhiteboardUtils.getService(whiteboard, SyncHandler.class), "gr" + UUID.randomUUID());
+
+ // second configuration with different IDP ('idp2') and
+ // - dynamic membership = true
+ // - auto-membership = 'gr_name2' and 'nonExisting'
+ DefaultSyncConfig sc2 = new DefaultSyncConfig();
+ sc2.setName("name2").user().setDynamicMembership(true);
+ setup2 = new ExternalSetup(new TestIdentityProvider("idp2"), sc2);
+
+ // third configuration with different IDP ('idp3') and
+ // - dynamic membership = false
+ // - auto-membership = 'gr_name3' and 'nonExisting'
+ DefaultSyncConfig sc3 = new DefaultSyncConfig();
+ sc3.setName("name3");
+ setup3 = new ExternalSetup(new TestIdentityProvider("idp3"), sc3);
+
+ // forth configuration based on different IDP ('idp4') but re-using
+ // sync-handler configuration (sc2)
+ setup4 = new ExternalSetup(new TestIdentityProvider("idp4"), sc2);
+
+ // fifth configuration with different IDP ('idp5') and
+ // - dynamic membership = true
+ // - auto-membership => nothing configured
+ DefaultSyncConfig sc5 = new DefaultSyncConfig();
+ sc5.setName("name5").user().setDynamicMembership(true);
+ setup5 = new ExternalSetup(new TestIdentityProvider("idp5"), sc5, new DefaultSyncHandler(sc5), null);
+ }
+
+ @Override
+ public void after() throws Exception {
+ try {
+ syncConfig.user().setAutoMembership().setExpirationTime(0);
+
+ setup1.close();
+ setup2.close();
+ setup3.close();
+ setup4.close();
+ } finally {
+ super.after();
+ }
+ }
+
+ @Override
+ protected Configuration getConfiguration() {
+ return new Configuration() {
+ @Override
+ public AppConfigurationEntry[] getAppConfigurationEntry(String s) {
+ AppConfigurationEntry[] entries = new AppConfigurationEntry[5];
+ int i = 0;
+ for (ExternalSetup setup : new ExternalSetup[] {setup1, setup2, setup3, setup4, setup5}) {
+ entries[i++] = setup.asConfigurationEntry();
+ }
+ return entries;
+ }
+ };
+ }
+
+ private static void registerSyncHandlerMapping(@Nonnull OsgiContext ctx, @Nonnull ExternalSetup setup) {
+ String syncHandlerName = setup.sc.getName();
+ Map props = ImmutableMap.of(
+ DefaultSyncConfigImpl.PARAM_NAME, syncHandlerName,
+ DefaultSyncConfigImpl.PARAM_USER_DYNAMIC_MEMBERSHIP, setup.sc.user().getDynamicMembership(),
+ DefaultSyncConfigImpl.PARAM_GROUP_AUTO_MEMBERSHIP, setup.sc.user().getAutoMembership());
+ ctx.registerService(SyncHandler.class, setup.sh, props);
+
+ Map mappingProps = ImmutableMap.of(
+ SyncHandlerMapping.PARAM_IDP_NAME, setup.idp.getName(),
+ SyncHandlerMapping.PARAM_SYNC_HANDLER_NAME, syncHandlerName);
+ ctx.registerService(SyncHandlerMapping.class, new SyncHandlerMapping() {}, mappingProps);
+ }
+
+ @Test
+ public void testLoginSyncAutoMembershipSetup1() throws Exception {
+ ContentSession cs = null;
+ try {
+ cs = login(new SimpleCredentials(USER_ID, new char[0]));
+
+ // the login must set the existing auto-membership principals to the subject
+ Set<Principal> principals = cs.getAuthInfo().getPrincipals();
+ assertTrue(principals.contains(setup1.gr.getPrincipal()));
+
+ assertFalse(principals.contains(new PrincipalImpl(NON_EXISTING_NAME)));
+ assertFalse(principals.contains(setup2.gr.getPrincipal()));
+ assertFalse(principals.contains(setup3.gr.getPrincipal()));
+
+ // however, the existing auto-membership group must _not_ have changed
+ // and the test user must not be a stored member of this group.
+ root.refresh();
+ UserManager uMgr = getUserManager(root);
+
+ User user = uMgr.getAuthorizable(USER_ID, User.class);
+ Group gr = uMgr.getAuthorizable(setup1.gr.getID(), Group.class);
+
+ assertFalse(gr.isDeclaredMember(user));
+ assertFalse(gr.isMember(user));
+ } finally {
+ options.clear();
+ if (cs != null) {
+ cs.close();
+ }
+ }
+ }
+
+ @Test
+ public void testLoginAfterSyncSetup1() throws Exception {
+ setup1.sync(USER_ID, false);
+
+ ContentSession cs = null;
+ try {
+ cs = login(new SimpleCredentials(USER_ID, new char[0]));
+
+ // the login must set the configured + existing auto-membership principals
+ // to the subject; non-existing auto-membership entries must be ignored.
+ Set<Principal> principals = cs.getAuthInfo().getPrincipals();
+ assertTrue(principals.contains(setup1.gr.getPrincipal()));
+
+ assertFalse(principals.contains(new PrincipalImpl(NON_EXISTING_NAME)));
+ assertFalse(principals.contains(setup2.gr.getPrincipal()));
+ assertFalse(principals.contains(setup3.gr.getPrincipal()));
+
+ // however, the existing auto-membership group must _not_ have changed
+ // and the test user must not be a stored member of this group.
+ root.refresh();
+ UserManager uMgr = getUserManager(root);
+
+ User user = uMgr.getAuthorizable(USER_ID, User.class);
+ Group gr = uMgr.getAuthorizable(setup1.gr.getID(), Group.class);
+
+ assertFalse(gr.isDeclaredMember(user));
+ assertFalse(gr.isMember(user));
+ } finally {
+ options.clear();
+ if (cs != null) {
+ cs.close();
+ }
+ }
+ }
+
+ @Test
+ public void testLoginAfterSyncSetup2() throws Exception {
+ setup2.sync(USER_ID, false);
+
+ ContentSession cs = null;
+ try {
+ cs = login(new SimpleCredentials(USER_ID, new char[0]));
+
+ // the login must set the existing auto-membership principals to the subject
+ Set<Principal> principals = cs.getAuthInfo().getPrincipals();
+ assertTrue(principals.contains(setup2.gr.getPrincipal()));
+
+ assertFalse(principals.contains(new PrincipalImpl(NON_EXISTING_NAME)));
+ assertFalse(principals.contains(setup1.gr.getPrincipal()));
+ assertFalse(principals.contains(setup3.gr.getPrincipal()));
+
+ // however, the existing auto-membership group must _not_ have changed
+ // and the test user must not be a stored member of this group.
+ root.refresh();
+ UserManager uMgr = getUserManager(root);
+
+ User user = uMgr.getAuthorizable(USER_ID, User.class);
+ Group gr = uMgr.getAuthorizable(setup2.gr.getID(), Group.class);
+
+ assertFalse(gr.isDeclaredMember(user));
+ assertFalse(gr.isMember(user));
+ } finally {
+ options.clear();
+ if (cs != null) {
+ cs.close();
+ }
+ }
+ }
+
+ @Test
+ public void testLoginAfterSyncSetup3() throws Exception {
+ setup3.sync(USER_ID, false);
+
+ ContentSession cs = null;
+ try {
+ cs = login(new SimpleCredentials(USER_ID, new char[0]));
+
+ // the login must set the existing auto-membership principals to the subject
+ Set<Principal> principals = cs.getAuthInfo().getPrincipals();
+ assertTrue(principals.contains(setup3.gr.getPrincipal()));
+
+ assertFalse(principals.contains(new PrincipalImpl(NON_EXISTING_NAME)));
+ assertFalse(principals.contains(setup1.gr.getPrincipal()));
+ assertFalse(principals.contains(setup2.gr.getPrincipal()));
+
+ // however, the existing auto-membership group must _not_ have changed
+ // and the test user must not be a stored member of this group.
+ root.refresh();
+ UserManager uMgr = getUserManager(root);
+
+ User user = uMgr.getAuthorizable(USER_ID, User.class);
+ Group gr = uMgr.getAuthorizable(setup3.gr.getID(), Group.class);
+
+ assertTrue(gr.isDeclaredMember(user));
+ assertTrue(gr.isMember(user));
+ } finally {
+ options.clear();
+ if (cs != null) {
+ cs.close();
+ }
+ }
+ }
+
+ @Test
+ public void testLoginAfterSyncSetup4() throws Exception {
+ setup4.sync(USER_ID, false);
+
+ ContentSession cs = null;
+ try {
+ cs = login(new SimpleCredentials(USER_ID, new char[0]));
+
+ // the login must set the existing auto-membership principals to the subject
+ Set<Principal> principals = cs.getAuthInfo().getPrincipals();
+ assertTrue(principals.contains(setup4.gr.getPrincipal()));
+ assertTrue(principals.contains(setup2.gr.getPrincipal()));
+
+ assertFalse(principals.contains(new PrincipalImpl(NON_EXISTING_NAME)));
+ assertFalse(principals.contains(setup1.gr.getPrincipal()));
+ assertFalse(principals.contains(setup3.gr.getPrincipal()));
+
+ // however, the existing auto-membership group must _not_ have changed
+ // and the test user must not be a stored member of this group.
+ root.refresh();
+ UserManager uMgr = getUserManager(root);
+
+ User user = uMgr.getAuthorizable(USER_ID, User.class);
+ Group gr = uMgr.getAuthorizable(setup4.gr.getID(), Group.class);
+
+ assertFalse(gr.isDeclaredMember(user));
+ assertFalse(gr.isMember(user));
+ } finally {
+ options.clear();
+ if (cs != null) {
+ cs.close();
+ }
+ }
+ }
+
+ @Test
+ public void testLoginAfterSyncSetup5() throws Exception {
+ setup5.sync(USER_ID, false);
+
+ ContentSession cs = null;
+ try {
+ cs = login(new SimpleCredentials(USER_ID, new char[0]));
+
+ // the login must not set any auto-membership principals to the subject
+ // as auto-membership is not configured on this setup.
+ Set<Principal> principals = cs.getAuthInfo().getPrincipals();
+
+ Set<Principal> expected = ImmutableSet.of(EveryonePrincipal.getInstance(), userManager.getAuthorizable(USER_ID).getPrincipal());
+ assertEquals(expected, principals);
+
+ assertFalse(principals.contains(new PrincipalImpl(NON_EXISTING_NAME)));
+ assertFalse(principals.contains(setup1.gr.getPrincipal()));
+ assertFalse(principals.contains(setup2.gr.getPrincipal()));
+ assertFalse(principals.contains(setup3.gr.getPrincipal()));
+ assertFalse(principals.contains(setup4.gr.getPrincipal()));
+ } finally {
+ options.clear();
+ if (cs != null) {
+ cs.close();
+ }
+ }
+ }
+
+ private final class ExternalSetup {
+
+ private final ExternalIdentityProvider idp;
+ private final Registration idpRegistration;
+
+ private final DefaultSyncConfig sc;
+ private final SyncHandler sh;
+ private final Registration shRegistration;
+
+ private final Group gr;
+
+ private SyncContext ctx;
+
+ private ExternalSetup(@Nonnull ExternalIdentityProvider idp, @Nonnull DefaultSyncConfig sc) throws Exception {
+ this(idp, sc, new DefaultSyncHandler(sc), "gr_" + sc.getName());
+ }
+
+ private ExternalSetup(@Nonnull ExternalIdentityProvider idp, @Nonnull DefaultSyncConfig sc, @Nonnull SyncHandler sh, @CheckForNull String groupId) throws Exception {
+ this.idp = idp;
+ this.sc = sc;
+ this.sh = sh;
+
+ if (groupId != null) {
+ Group g = userManager.getAuthorizable(groupId, Group.class);
+ if (g != null) {
+ gr = g;
+ } else {
+ gr = userManager.createGroup(groupId);
+ }
+ r.commit();
+
+ sc.user().setAutoMembership(gr.getID(), NON_EXISTING_NAME).setExpirationTime(Long.MAX_VALUE);
+ } else {
+ gr = null;
+ }
+
+ idpRegistration = whiteboard.register(ExternalIdentityProvider.class, idp, Collections.<String, Object>emptyMap());
+ shRegistration = whiteboard.register(SyncHandler.class, sh, ImmutableMap.of(
+ DefaultSyncConfigImpl.PARAM_NAME, sh.getName(),
+ DefaultSyncConfigImpl.PARAM_USER_DYNAMIC_MEMBERSHIP, sc.user().getDynamicMembership(),
+ DefaultSyncConfigImpl.PARAM_GROUP_AUTO_MEMBERSHIP, sc.user().getAutoMembership()));
+ registerSyncHandlerMapping(context, this);
+ }
+
+ private void sync(@Nonnull String id, boolean isGroup) throws Exception {
+ ctx = sh.createContext(idp, userManager, valueFactory);
+ ExternalIdentity exIdentity = (isGroup) ? idp.getGroup(id) : idp.getUser(id);
+ assertNotNull(exIdentity);
+
+ SyncResult res = ctx.sync(exIdentity);
+ assertEquals(idp.getName(), res.getIdentity().getExternalIdRef().getProviderName());
+ assertSame(SyncResult.Status.ADD, res.getStatus());
+ r.commit();
+ }
+
+ private void close() {
+ if (ctx != null) {
+ ctx.close();
+ }
+ if (idpRegistration != null) {
+ idpRegistration.unregister();
+ }
+ if (shRegistration != null) {
+ shRegistration.unregister();
+ }
+ }
+
+ private AppConfigurationEntry asConfigurationEntry() {
+ return new AppConfigurationEntry(
+ ExternalLoginModule.class.getName(),
+ AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT,
+ ImmutableMap.<String, String>of(
+ SyncHandlerMapping.PARAM_SYNC_HANDLER_NAME, sh.getName(),
+ SyncHandlerMapping.PARAM_IDP_NAME, idp.getName()
+ ));
+ }
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleAutoMembershipTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTestBase.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTestBase.java?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTestBase.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTestBase.java Wed Jun 1 07:11:59 2016
@@ -57,10 +57,10 @@ public abstract class ExternalLoginModul
testIdpReg = whiteboard.register(ExternalIdentityProvider.class, idp, Collections.<String, Object>emptyMap());
- options.put(ExternalLoginModule.PARAM_SYNC_HANDLER_NAME, "default");
- options.put(ExternalLoginModule.PARAM_IDP_NAME, idp.getName());
-
setSyncConfig(syncConfig);
+
+ options.put(ExternalLoginModule.PARAM_SYNC_HANDLER_NAME, syncConfig.getName());
+ options.put(ExternalLoginModule.PARAM_IDP_NAME, idp.getName());
}
@After
Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java Wed Jun 1 07:11:59 2016
@@ -37,10 +37,20 @@ public class TestIdentityProvider implem
public static final String ID_EXCEPTION = "throw!";
+ public static final String DEFAULT_IDP_NAME = "test";
+
private final Map<String, ExternalGroup> externalGroups = new HashMap<String, ExternalGroup>();
private final Map<String, ExternalUser> externalUsers = new HashMap<String, ExternalUser>();
+ private final String idpName;
+
public TestIdentityProvider() {
+ this(DEFAULT_IDP_NAME);
+ }
+
+ public TestIdentityProvider(@Nonnull String idpName) {
+ this.idpName = idpName;
+
addGroup(new TestGroup("aa", getName()));
addGroup(new TestGroup("aaa", getName()));
addGroup(new TestGroup("a", getName()).withGroups("aa", "aaa"));
@@ -82,7 +92,7 @@ public class TestIdentityProvider implem
@Nonnull
@Override
public String getName() {
- return "test";
+ return idpName;
}
@Override
@@ -151,11 +161,11 @@ public class TestIdentityProvider implem
private final Map<String, Object> props = new HashMap<String, Object>();
public TestIdentity() {
- this("externalId", "principalName", "test");
+ this("externalId", "principalName", DEFAULT_IDP_NAME);
}
public TestIdentity(@Nonnull String userId) {
- this(userId, userId, "test");
+ this(userId, userId, DEFAULT_IDP_NAME);
}
public TestIdentity(@Nonnull String userId, @Nonnull String principalName, @Nonnull String idpName) {
Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java Wed Jun 1 07:11:59 2016
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.spi.se
import java.util.HashSet;
import java.util.Set;
+import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.jcr.RepositoryException;
@@ -31,6 +32,7 @@ import com.google.common.collect.Iterabl
import com.google.common.collect.Sets;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Root;
@@ -418,6 +420,21 @@ public class DynamicSyncContextTest exte
assertFalse(r.hasPendingChanges());
}
+ @Test
+ public void testAutoMembership() throws Exception {
+ Group gr = userManager.createGroup("group" + UUID.randomUUID());
+ r.commit();
+
+ syncConfig.user().setAutoMembership(gr.getID(), "non-existing-group");
+
+ SyncResult result = syncContext.sync(idp.getUser(USER_ID));
+ assertSame(SyncResult.Status.ADD, result.getStatus());
+
+ User u = userManager.getAuthorizable(USER_ID, User.class);
+ assertFalse(gr.isDeclaredMember(u));
+ assertFalse(gr.isMember(u));
+ }
+
private static final class TestUserWithGroupRefs extends TestIdentityProvider.TestIdentity implements ExternalUser {
private Iterable<ExternalIdentityRef> declaredGroupRefs;
Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AbstractPrincipalTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AbstractPrincipalTest.java?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AbstractPrincipalTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AbstractPrincipalTest.java Wed Jun 1 07:11:59 2016
@@ -17,9 +17,11 @@
package org.apache.jackrabbit.oak.spi.security.authentication.external.impl.principal;
import java.security.Principal;
+import java.util.Set;
import java.util.UUID;
import javax.annotation.Nonnull;
+import com.google.common.collect.ImmutableMap;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.oak.api.Root;
import org.apache.jackrabbit.oak.namepath.NamePathMapper;
@@ -63,7 +65,9 @@ public abstract class AbstractPrincipalT
@Nonnull
PrincipalProvider createPrincipalProvider() {
- return new ExternalGroupPrincipalProvider(root, getSecurityProvider().getConfiguration(UserConfiguration.class), NamePathMapper.DEFAULT);
+ Set<String> autoMembership = syncConfig.user().getAutoMembership();
+ return new ExternalGroupPrincipalProvider(root, getSecurityProvider().getConfiguration(UserConfiguration.class),
+ NamePathMapper.DEFAULT, ImmutableMap.of(idp.getName(), autoMembership.toArray(new String[autoMembership.size()])));
}
@Override
Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalProviderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalProviderTest.java?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalProviderTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalProviderTest.java Wed Jun 1 07:11:59 2016
@@ -54,7 +54,7 @@ import static org.junit.Assert.fail;
public class ExternalGroupPrincipalProviderTest extends AbstractPrincipalTest {
- private void syncWithMembership(@Nonnull ExternalUser externalUser, long depth) throws Exception {
+ void syncWithMembership(@Nonnull ExternalUser externalUser, long depth) throws Exception {
DefaultSyncConfig sc = new DefaultSyncConfig();
sc.user().setMembershipNestingDepth(depth);
@@ -69,7 +69,11 @@ public class ExternalGroupPrincipalProvi
root.refresh();
}
- private Set<Principal> getDeclaredGroupPrincipals(@Nonnull String userId) throws ExternalIdentityException {
+ Set<Principal> getExpectedGroupPrincipals(@Nonnull String userId) throws Exception {
+ return getDeclaredGroupPrincipals(userId);
+ }
+
+ Set<Principal> getDeclaredGroupPrincipals(@Nonnull String userId) throws Exception {
Set<Principal> principals = ImmutableSet.copyOf(Iterables.transform(idp.getUser(userId).getDeclaredGroups(), new Function<ExternalIdentityRef, Principal>() {
@Nullable
@Override
@@ -77,16 +81,24 @@ public class ExternalGroupPrincipalProvi
try {
return new PrincipalImpl(idp.getIdentity(input).getPrincipalName());
} catch (ExternalIdentityException e) {
- fail(e.getMessage());
- return null;
+ throw new RuntimeException(e);
}
- }
-
- ;
+ };
}));
return principals;
}
+ void collectExpectedPrincipals(Set<Principal> grPrincipals, @Nonnull Iterable<ExternalIdentityRef> declaredGroups, long depth) throws Exception {
+ if (depth <= 0) {
+ return;
+ }
+ for (ExternalIdentityRef ref : declaredGroups) {
+ ExternalIdentity ei = idp.getIdentity(ref);
+ grPrincipals.add(new PrincipalImpl(ei.getPrincipalName()));
+ collectExpectedPrincipals(grPrincipals, ei.getDeclaredGroups(), depth - 1);
+ }
+ }
+
@Test
public void testGetPrincipalLocalUser() throws Exception {
assertNull(principalProvider.getPrincipal(getTestUser().getPrincipal().getName()));
@@ -234,7 +246,7 @@ public class ExternalGroupPrincipalProvi
Authorizable user = getUserManager(root).getAuthorizable(USER_ID);
assertNotNull(user);
- Set<Principal> expected = getDeclaredGroupPrincipals(USER_ID);
+ Set<Principal> expected = getExpectedGroupPrincipals(USER_ID);
Set<? extends Principal> principals = principalProvider.getGroupMembership(user.getPrincipal());
assertEquals(expected, principals);
@@ -275,17 +287,6 @@ public class ExternalGroupPrincipalProvi
assertEquals(expectedGrPrincipals, principals);
}
- private void collectExpectedPrincipals(Set<Principal> grPrincipals, @Nonnull Iterable<ExternalIdentityRef> declaredGroups, long depth) throws ExternalIdentityException {
- if (depth <= 0) {
- return;
- }
- for (ExternalIdentityRef ref : declaredGroups) {
- ExternalIdentity ei = idp.getIdentity(ref);
- grPrincipals.add(new PrincipalImpl(ei.getPrincipalName()));
- collectExpectedPrincipals(grPrincipals, ei.getDeclaredGroups(), depth - 1);
- }
- }
-
@Test
public void testGetGroupMembershipExternalGroup() throws Exception {
Authorizable group = getUserManager(root).getAuthorizable("secondGroup");
@@ -314,7 +315,7 @@ public class ExternalGroupPrincipalProvi
@Test
public void testGetPrincipalsExternalUser() throws Exception {
Set<? extends Principal> principals = principalProvider.getPrincipals(USER_ID);
- assertEquals(getDeclaredGroupPrincipals(USER_ID), principals);
+ assertEquals(getExpectedGroupPrincipals(USER_ID), principals);
}
@Test
Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/PrincipalProviderAutoMembershipTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/PrincipalProviderAutoMembershipTest.java?rev=1746408&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/PrincipalProviderAutoMembershipTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/PrincipalProviderAutoMembershipTest.java Wed Jun 1 07:11:59 2016
@@ -0,0 +1,141 @@
+/*
+ * 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.spi.security.authentication.external.impl.principal;
+
+import java.security.Principal;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.annotation.Nonnull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterators;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityRef;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncConfig;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Extension of the {@link ExternalGroupPrincipalProviderTest} with 'automembership'
+ * configured in the {@link DefaultSyncConfig}.
+ */
+public class PrincipalProviderAutoMembershipTest extends ExternalGroupPrincipalProviderTest {
+
+ private static final String AUTO_MEMBERSHIP_GROUP_ID = "testGroup" + UUID.randomUUID();
+ private static final String AUTO_MEMBERSHIP_GROUP_PRINCIPAL_NAME = "p" + AUTO_MEMBERSHIP_GROUP_ID;
+ private static final String NON_EXISTING_GROUP_ID = "nonExistingGroup";
+
+ private Group autoMembershipGroup;
+
+ @Override
+ public void before() throws Exception {
+ super.before();
+
+ autoMembershipGroup = getUserManager(root).createGroup(AUTO_MEMBERSHIP_GROUP_ID, new PrincipalImpl(AUTO_MEMBERSHIP_GROUP_PRINCIPAL_NAME), null);
+ root.commit();
+ }
+
+ @Override
+ protected DefaultSyncConfig createSyncConfig() {
+ DefaultSyncConfig syncConfig = super.createSyncConfig();
+ syncConfig.user().setAutoMembership(AUTO_MEMBERSHIP_GROUP_ID, NON_EXISTING_GROUP_ID);
+
+ return syncConfig;
+ }
+
+ @Override
+ Set<Principal> getExpectedGroupPrincipals(@Nonnull String userId) throws Exception {
+ return ImmutableSet.<Principal>builder()
+ .addAll(super.getExpectedGroupPrincipals(userId))
+ .add(autoMembershipGroup.getPrincipal()).build();
+ }
+
+ @Override
+ void collectExpectedPrincipals(Set<Principal> grPrincipals, @Nonnull Iterable<ExternalIdentityRef> declaredGroups, long depth) throws Exception {
+ super.collectExpectedPrincipals(grPrincipals, declaredGroups, depth);
+ grPrincipals.add(autoMembershipGroup.getPrincipal());
+ }
+
+ @Test
+ public void testGetAutoMembershipPrincipal() throws Exception {
+ assertNull(principalProvider.getPrincipal(autoMembershipGroup.getPrincipal().getName()));
+ assertNull(principalProvider.getPrincipal(AUTO_MEMBERSHIP_GROUP_PRINCIPAL_NAME));
+ assertNull(principalProvider.getPrincipal(AUTO_MEMBERSHIP_GROUP_ID));
+ assertNull(principalProvider.getPrincipal(NON_EXISTING_GROUP_ID));
+ }
+
+ @Test
+ public void testGetGroupPrincipals() throws Exception {
+ ExternalUser externalUser = idp.getUser(USER_ID);
+ syncWithMembership(externalUser, 1);
+
+ Set<Principal> expected = getExpectedGroupPrincipals(USER_ID);
+
+ Authorizable user = getUserManager(root).getAuthorizable(USER_ID);
+
+ Set<java.security.acl.Group> result = principalProvider.getGroupMembership(user.getPrincipal());
+ assertTrue(result.contains(autoMembershipGroup.getPrincipal()));
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testGetPrincipals() throws Exception {
+ ExternalUser externalUser = idp.getUser(USER_ID);
+ syncWithMembership(externalUser, 1);
+
+ Set<Principal> expected = getExpectedGroupPrincipals(USER_ID);
+
+ Set<? extends Principal> result = principalProvider.getPrincipals(USER_ID);
+ assertTrue(result.contains(autoMembershipGroup.getPrincipal()));
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testFindPrincipalsByHint() throws Exception {
+ List<String> hints = ImmutableList.of(
+ AUTO_MEMBERSHIP_GROUP_PRINCIPAL_NAME,
+ AUTO_MEMBERSHIP_GROUP_ID,
+ AUTO_MEMBERSHIP_GROUP_PRINCIPAL_NAME.substring(1, 6));
+
+ for (String hint : hints) {
+ Iterator<? extends Principal> res = principalProvider.findPrincipals(hint, PrincipalManager.SEARCH_TYPE_GROUP);
+
+ assertFalse(Iterators.contains(res, autoMembershipGroup.getPrincipal()));
+ assertFalse(Iterators.contains(res, new PrincipalImpl(NON_EXISTING_GROUP_ID)));
+ }
+ }
+
+ @Test
+ public void testFindPrincipalsByTypeGroup() throws Exception {
+ Iterator<? extends Principal> res = principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_GROUP);
+
+ assertFalse(Iterators.contains(res, autoMembershipGroup.getPrincipal()));
+ assertFalse(Iterators.contains(res, new PrincipalImpl(NON_EXISTING_GROUP_ID)));
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/PrincipalProviderAutoMembershipTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Copied: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/external/defaultusersync.md (from r1746238, jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/defaultusersync.md)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/external/defaultusersync.md?p2=jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/external/defaultusersync.md&p1=jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/defaultusersync.md&r1=1746238&r2=1746408&rev=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/defaultusersync.md (original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/external/defaultusersync.md Wed Jun 1 07:11:59 2016
@@ -84,81 +84,10 @@ represented by [ExternalIdentityRef].
As of Oak 1.5.3 the default sync handler comes with an addition configuration
option that allows to enable dynamic group membership resolution for external users.
Enabling dynamic membership in the [DefaultSyncConfig] will change the way external
-groups are synchronized (see also [OAK-4101]).
-
-The key benefits of dynamic membership resolution are:
-
-- avoiding duplicate user management effort wrt to membership handling both in the external IDP and the repository
-- ease principal resolution upon repository login
-
-#### SyncContext with Dynamic Membership
-
-With the default `SyncHandler` this configuration option will show the following
-effects:
-
-- If enabled the handler will use an alternative [SyncContext] to synchronize external groups.
-- Instead of synchronizing groups into the user management, this [DynamicSyncContext]
- will additionally set the property `rep:externalPrincipalNames` on the synchronized external user
-- `rep:externalPrincipalNames` is a system maintained multivalued property of type
- 'STRING' storing the names of the `java.security.acl.Group`-principals a given
- external user is member of (both declared and inherited according to the configured
- membership nesting depth)
-- External groups will no longer be synchronised into the repository's user management
- but will only be available as `Principal`s (see section _User Management_ below).
-
-#### Effect of Dynamic Membership on other Security Modules
-
-##### Principal Management
-
-The dynamic (principal) membership features comes with a dedicated `PrincipalConfiguration`
-implementation (i.e. [ExternalPrincipalConfiguration]) that is in charge of securing
-the `rep:externalPrincipalNames` properties (see also section [Validation](#validation)
-and [Configuration](#configuration) below).
-
-Additionally the [ExternalPrincipalConfiguration] provides a `PrincipalProvider`
-implementation which makes external (group) principals available to the repository's
-authentication and authorization using the `rep:externalPrincipalNames` as a
-persistent cache to avoid expensive lookup on the IDP.
-This also makes external `Principal`s retrievable and searchable through the
-Jackrabbit principal management API (see section [Principal Management](../principal.html)
-for a comprehensive description).
-
-Please note the following implementation detail wrt accessibility of group principals:
-A given external principal will be accessible though the principal management API
-if it can be read from any of the `rep:externalPrincipalNames` properties
-present using a dedicated query.
-
-##### User Management
-
-As described above the dynamic membership option will effectively disable the
-synchronization of the complete external group account information into the repository's
-user management feature but limit the synchronized information to the principal
-names and the membership relation between a given `java.security.acl.Group` principal
-and external user accounts.
-
-The user management API will consequently no longer be knowledgeable of external
-group identities (exception: groups that have been synchronized before enabling
-the feature will remain untouched and will be synchronized according to the
-sync configuration).
-
-While this behavior does not affect default authentication and authorization modules
-(see below) it will have an impact on applications that rely on full synchronization
-of external identities. Those application won't be able to benefit from the dynamic
-membership feature until dynamic groups can be created with the
-Jackrabbit [User Management API](../user.html) (see [OAK-2687]).
-
-##### Authentication
-
-The authentication setup provided by Oak is not affected by the dynamic membership
-handling as long as the configured `LoginModule` implementations rely on the
-`PrincipalProvider` for principal resolution and the [ExternalPrincipalConfiguration]
-is properly registered with the `SecurityProvider` (see section [Configuration](#configuration) below).
-
-##### Authorization
-
-The authorization modules shipped with Oak only depend on `Principal`s (and not on
-user management functionality) and are therefore not affected by the dynamic
-membership configuration.
+groups are synchronized (see also [OAK-4101]).
+
+The details and effects on other security related modules are described in
+section [Dynamic Membership](dynamic.html).
<a name="xml_import"/>
#### XML Import
Added: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/external/dynamic.md
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/external/dynamic.md?rev=1746408&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/external/dynamic.md (added)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/external/dynamic.md Wed Jun 1 07:11:59 2016
@@ -0,0 +1,135 @@
+<!--
+ 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.
+-->
+
+User and Group Synchronization : Dynamic Membership
+---------------------------------------------------
+
+As of Oak 1.5.3 the default sync handler comes with an additional configuration
+option (see section [Configuration](defaultusersync.html#configuration)
+that allows to enable dynamic group membership resolution for external users.
+
+Enabling dynamic membership in the [DefaultSyncConfig] will change the way external
+groups are synchronized (see [OAK-4101]) and how automatic group membership
+is being handled (see [OAK-4087])
+
+The key benefits of dynamic membership resolution are:
+
+- avoiding duplicate user management effort wrt to membership handling both in the external IDP and the repository
+- avoid storing/updating auto-membership which is assigned to all external users
+- ease principal resolution upon repository login
+
+#### SyncContext with Dynamic Membership
+
+With the default `SyncHandler` this configuration option will show the following
+effects:
+
+##### External Groups
+
+- If enabled the handler will use an alternative [SyncContext] to synchronize external groups ([DynamicSyncContext]).
+- Instead of synchronizing groups into the user management, this [DynamicSyncContext]
+ will additionally set the property `rep:externalPrincipalNames` on the synchronized external user
+- `rep:externalPrincipalNames` is a system maintained multivalued property of type
+ 'STRING' storing the names of the `java.security.acl.Group`-principals a given
+ external user is member of (both declared and inherited according to the configured
+ membership nesting depth)
+- External groups will no longer be synchronised into the repository's user management
+ but will only be available as `Principal`s (see section _User Management_ below).
+
+##### Automatic Membership
+
+- If enabled automatic membership assignment for existing, local groups will not longer be written to the repository
+- Instead the [ExternalPrincipalConfiguration] will keep track of the mapping
+ between registered [SyncHandler]s (i.e. auto-membership configuration) and [ExternalIdentityProvider]s.
+ This allows to determine auto-membership based on the `rep:externalId` stored with the user accounts.
+- The `PrincipalProvider` associated with this dedicated principal configuration
+ will expand the collection of `Principal`s generated for the following calls
+ with the automatically assigned principals:
+ - `PrincipalProvider.getGroupMembership(Principal)`
+ - `PrincipalProvider.getPrincipals(String)`
+- Configured auto-membership groupIds that cannot be resolved to an existing
+ `o.a.j.api.security.user.Group` will be ignored in accordance to the default behavior.
+- Consequently, the `PrincipalProvider` relies on other `PrincipalProvider`
+ implementations to _own_ these group principals and will not expose them
+ upon other calls (e.g. `PrincipalProvider.getPrincipal(String)`.
+- Any changes to the auto-membership configuration will be immediately reflected
+ to new instances of the `PrincipalProvider`.
+
+#### Effect of Dynamic Membership on other Security Modules
+
+##### Principal Management
+
+The dynamic (principal) membership features comes with a dedicated `PrincipalConfiguration`
+implementation (i.e. [ExternalPrincipalConfiguration]) that is in charge of securing
+the `rep:externalPrincipalNames` properties (see also section [Validation](defaultusersync.html#validation)
+and [Configuration](defaultusersync.html#configuration)).
+
+Additionally the [ExternalPrincipalConfiguration] provides a `PrincipalProvider`
+implementation which makes external (group) principals available to the repository's
+authentication and authorization using the `rep:externalPrincipalNames` as a
+persistent cache to avoid expensive lookup on the IDP.
+This also makes external `Principal`s retrievable and searchable through the
+Jackrabbit principal management API (see section [Principal Management](../../principal.html)
+for a comprehensive description).
+
+Please note the following implementation detail wrt accessibility of group principals:
+A given external principal will be accessible though the principal management API
+if it can be read from any of the `rep:externalPrincipalNames` properties
+present using a dedicated query.
+
+##### User Management
+
+As described above the dynamic membership option will effectively disable the
+synchronization of the complete external group account information into the repository's
+user management feature but limit the synchronized information to the principal
+names and the membership relation between a given `java.security.acl.Group` principal
+and external user accounts.
+
+The user management API will consequently no longer be knowledgeable of external
+group identities (exception: groups that have been synchronized before enabling
+the feature will remain untouched and will be synchronized according to the
+sync configuration).
+
+While this behavior does not affect default authentication and authorization modules
+(see below) it will have an impact on applications that rely on full synchronization
+of external identities. Those application won't be able to benefit from the dynamic
+membership feature until dynamic groups can be created with the
+Jackrabbit [User Management API](../../user.html) (see [OAK-2687]).
+
+##### Authentication
+
+The authentication setup provided by Oak is not affected by the dynamic membership
+handling as long as the configured `LoginModule` implementations rely on the
+`PrincipalProvider` for principal resolution and the [ExternalPrincipalConfiguration]
+is properly registered with the `SecurityProvider` (see section [Configuration](defaultusersync.html#configuration)).
+
+##### Authorization
+
+The authorization modules shipped with Oak only depend on `Principal`s (and not on
+user management functionality) and are therefore not affected by the dynamic
+membership configuration.
+
+<!-- references -->
+[SyncHandler]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncHandler.html
+[SyncContext]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncContext.html
+[DefaultSyncContext]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.html
+[DefaultSyncConfig]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncConfig.html
+[ExternalIdentityProvider]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProvider.html
+[ExternalPrincipalConfiguration]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfiguration.html
+[DynamicSyncContext]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/DynamicSyncContext.html
+[OAK-4101]: https://issues.apache.org/jira/browse/OAK-4101
+[OAK-2687]: https://issues.apache.org/jira/browse/OAK-2687
+[OAK-4087]: https://issues.apache.org/jira/browse/OAK-4087
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/externalloginmodule.md
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/externalloginmodule.md?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/externalloginmodule.md (original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/externalloginmodule.md Wed Jun 1 07:11:59 2016
@@ -64,7 +64,7 @@ if the user is not yet present in the lo
check the credentials with the external system during the `login()` method.
The details of the default user/group synchronization mechanism are described in section
-[User and Group Synchronization : The Default Implementation](defaultusersync.html)
+[User and Group Synchronization : The Default Implementation](external/defaultusersync.html)
##### Supported Credentials
Modified: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/usersync.md
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/usersync.md?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/usersync.md (original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/usersync.md Wed Jun 1 07:11:59 2016
@@ -51,7 +51,7 @@ for the following tasks:
Oak 1.0 provides a default implementation of the user synchronization API that allow
to plug additional `SyncHandler` implementations.
-Default implementation is described in section [User and Group Synchronization : The Default Implementation](defaultusersync.html).
+Default implementation is described in section [User and Group Synchronization : The Default Implementation](external/defaultusersync.html).
### Pluggability
Modified: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/principal/principalprovider.md
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/principal/principalprovider.md?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/principal/principalprovider.md (original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/principal/principalprovider.md Wed Jun 1 07:11:59 2016
@@ -79,7 +79,7 @@ principals on the IDP but relies on a pe
the names of these external principals are synchronized to based on a configurable
expiration time.
-See section [User and Group Synchronization : The Default Implementation](../authentication/defaultusersync.html)
+See section [User and Group Synchronization : The Default Implementation](../authentication/external/defaultusersync.html)
for additional details.
Since Oak 1.5.3
Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/authentication/external/AbstractExternalTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/authentication/external/AbstractExternalTest.java?rev=1746408&r1=1746407&r2=1746408&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/authentication/external/AbstractExternalTest.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/authentication/external/AbstractExternalTest.java Wed Jun 1 07:11:59 2016
@@ -57,6 +57,7 @@ import org.apache.jackrabbit.oak.spi.sec
import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl;
import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler;
import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalIDPManagerImpl;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.SyncHandlerMapping;
import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.SyncManagerImpl;
import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.principal.ExternalPrincipalConfiguration;
import org.apache.jackrabbit.oak.spi.security.principal.CompositePrincipalConfiguration;
@@ -66,7 +67,6 @@ import org.apache.jackrabbit.oak.spi.sec
import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
import org.apache.sling.testing.mock.osgi.context.OsgiContextImpl;
-import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -189,8 +189,15 @@ abstract class AbstractExternalTest exte
// now register the sync-handler with the dynamic membership config
// in order to enable dynamic membership with the external principal configuration
- Map props = ImmutableMap.of(DefaultSyncConfigImpl.PARAM_USER_DYNAMIC_MEMBERSHIP, syncConfig.user().getDynamicMembership());
+ Map props = ImmutableMap.of(
+ DefaultSyncConfigImpl.PARAM_USER_DYNAMIC_MEMBERSHIP, syncConfig.user().getDynamicMembership(),
+ DefaultSyncConfigImpl.PARAM_GROUP_AUTO_MEMBERSHIP, syncConfig.user().getAutoMembership());
context.registerService(SyncHandler.class, WhiteboardUtils.getService(whiteboard, SyncHandler.class), props);
+
+ Map shMappingProps = ImmutableMap.of(
+ SyncHandlerMapping.PARAM_IDP_NAME, idp.getName(),
+ SyncHandlerMapping.PARAM_SYNC_HANDLER_NAME, syncConfig.getName());
+ context.registerService(SyncHandlerMapping.class, new SyncHandlerMapping() {}, shMappingProps);
}
SecurityProvider sp = new TestSecurityProvider(ConfigurationParameters.EMPTY);