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 2019/10/31 10:00:57 UTC

svn commit: r1869208 [1/2] - in /jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external: basic/ impl/ impl/jmx/ impl/principal/

Author: angela
Date: Thu Oct 31 10:00:56 2019
New Revision: 1869208

URL: http://svn.apache.org/viewvc?rev=1869208&view=rev
Log:
OAK-8725 : Improve tests for oak-external-auth (wip)

Added:
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfigImplTest.java   (with props)
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalIDPManagerImplTest.java   (with props)
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java   (with props)
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/SyncManagerImplTest.java   (with props)
Removed:
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/DelegateeBatchOneTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/DelegateeBatchTwoTest.java
Modified:
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContextTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandlerTest.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/ExternalLoginModuleFactoryTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/DelegateeTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/SyncMBeanImplTest.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-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalIdentityRepositoryInitializerTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalIdentityValidatorTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfigurationTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/PrincipalProviderAutoMembershipTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/PrincipalProviderDeepNestingTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ValidatorNoProtectionTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ValidatorNotDynamicTest.java

Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContextTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContextTest.java?rev=1869208&r1=1869207&r2=1869208&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContextTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContextTest.java Thu Oct 31 10:00:56 2019
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.spi.se
 
 import java.io.ByteArrayInputStream;
 import java.math.BigDecimal;
+import java.text.Normalizer;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
@@ -42,6 +43,7 @@ import org.apache.jackrabbit.api.securit
 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.commons.PathUtils;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.AbstractExternalAuthTest;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalGroup;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentity;
@@ -91,10 +93,12 @@ public class DefaultSyncContextTest exte
     }
 
     @Override
+    @NotNull
     protected DefaultSyncConfig createSyncConfig() {
         return new DefaultSyncConfig();
     }
 
+    @NotNull
     private Group createTestGroup() throws Exception {
         return userManager.createGroup("group" + UUID.randomUUID());
     }
@@ -447,7 +451,7 @@ public class DefaultSyncContextTest exte
     }
 
     @Test
-    public void testSyncDisabledUserById() throws Exception {
+    public void testSyncUserDisableMissingEnabled() throws Exception {
         // configure to disable missing users
         syncConfig.user().setDisableMissing(true);
 
@@ -478,6 +482,35 @@ public class DefaultSyncContextTest exte
         assertEquals(SyncResult.Status.ENABLE, result.getStatus());
         assertNotNull(userManager.getAuthorizable(userId));
         assertFalse(((User)authorizable).isDisabled());
+
+        // sync again
+        syncCtx.setForceUserSync(true);
+        result = syncCtx.sync(userId);
+        assertEquals(SyncResult.Status.UPDATE, result.getStatus());
+        assertFalse(((User)authorizable).isDisabled());
+    }
+
+    @Test
+    public void testSyncGroupDisableMissingEnabled() throws Exception {
+        // configure to disable missing users
+        syncConfig.user().setDisableMissing(true);
+
+        // mark a regular repo group as external user from the test IDP
+        Group gr = userManager.createGroup("test" + UUID.randomUUID());
+        String groupId = gr.getID();
+        setExternalID(gr, idp.getName());
+
+        // test sync with 'keepmissing' = true
+        syncCtx.setKeepMissing(true);
+        SyncResult result = syncCtx.sync(groupId);
+        assertEquals(SyncResult.Status.MISSING, result.getStatus());
+        assertNotNull(userManager.getAuthorizable(groupId));
+
+        // test sync with 'keepmissing' = false
+        syncCtx.setKeepMissing(false);
+        result = syncCtx.sync(groupId);
+        assertEquals(SyncResult.Status.DELETE, result.getStatus());
+        assertNull(userManager.getAuthorizable(groupId));
     }
 
     @Test
@@ -1371,6 +1404,20 @@ public class DefaultSyncContextTest exte
         assertTrue(syncCtx.isSameIDP(idp.listUsers().next().getExternalId()));
     }
 
+    @Test
+    public void testJoinPaths() {
+        assertEquals(PathUtils.concatRelativePaths("a", "b", "c"), DefaultSyncContext.joinPaths("a", "b", "c"));
+    }
+
+    @Test
+    public void testCreateUserWithApplyRFC7613() throws Exception {
+        syncCtx.config.user().setApplyRFC7613UsernameCaseMapped(true);
+
+        String id = "\uff21\uff4d\uff41\uff4c\uff49\uff41\u017F";
+        User user = syncCtx.createUser(new TestIdentityProvider.TestUser(id, idp.getName()));
+        assertEquals(Normalizer.normalize(id.toLowerCase(), Normalizer.Form.NFKC), user.getID());
+    }
+
     private final class ExternalUserWithDeclaredGroup extends TestIdentityProvider.TestIdentity implements ExternalUser {
 
         private final ExternalIdentityRef declaredGroupRef;

Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfigImplTest.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/DefaultSyncConfigImplTest.java?rev=1869208&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfigImplTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfigImplTest.java Thu Oct 31 10:00:56 2019
@@ -0,0 +1,102 @@
+/*
+ * 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;
+
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncConfig;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.Map;
+
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_DISABLE_MISSING_USERS_DEFAULT;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_ENABLE_RFC7613_USERCASE_MAPPED_PROFILE_DEFAULT;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_GROUP_AUTO_MEMBERSHIP_DEFAULT;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_GROUP_EXPIRATION_TIME_DEFAULT;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_GROUP_PATH_PREFIX_DEFAULT;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_GROUP_PROPERTY_MAPPING_DEFAULT;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_USER_AUTO_MEMBERSHIP_DEFAULT;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_USER_DYNAMIC_MEMBERSHIP_DEFAULT;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_USER_EXPIRATION_TIME_DEFAULT;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_USER_MEMBERSHIP_EXPIRATION_TIME_DEFAULT;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_USER_MEMBERSHIP_NESTING_DEPTH_DEFAULT;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_USER_PATH_PREFIX_DEFAULT;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_USER_PROPERTY_MAPPING;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncConfigImpl.PARAM_USER_PROPERTY_MAPPING_DEFAULT;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class DefaultSyncConfigImplTest {
+
+    @Test
+    public void testDefaultName() {
+        DefaultSyncConfig config = DefaultSyncConfigImpl.of(ConfigurationParameters.EMPTY);
+        assertEquals(DefaultSyncConfigImpl.PARAM_NAME_DEFAULT, config.getName());
+    }
+
+    @Test
+    public void testDefaultUser() {
+        DefaultSyncConfig.User userConfig = DefaultSyncConfigImpl.of(ConfigurationParameters.EMPTY).user();
+        assertEquals(PARAM_DISABLE_MISSING_USERS_DEFAULT, userConfig.getDisableMissing());
+        assertEquals(PARAM_USER_DYNAMIC_MEMBERSHIP_DEFAULT, userConfig.getDynamicMembership());
+        assertEquals(PARAM_USER_MEMBERSHIP_NESTING_DEPTH_DEFAULT, userConfig.getMembershipNestingDepth());
+        assertEquals(ConfigurationParameters.Milliseconds.of(PARAM_USER_MEMBERSHIP_EXPIRATION_TIME_DEFAULT), ConfigurationParameters.Milliseconds.of(userConfig.getMembershipExpirationTime()));
+
+        assertEquals(ConfigurationParameters.Milliseconds.of(PARAM_USER_EXPIRATION_TIME_DEFAULT), ConfigurationParameters.Milliseconds.of(userConfig.getExpirationTime()));
+        assertEquals(PARAM_ENABLE_RFC7613_USERCASE_MAPPED_PROFILE_DEFAULT, userConfig.isApplyRFC7613UsernameCaseMapped());
+        assertArrayEquals(PARAM_USER_AUTO_MEMBERSHIP_DEFAULT, userConfig.getAutoMembership().toArray(new String[0]));
+        assertEquals(getMapping(PARAM_USER_PROPERTY_MAPPING_DEFAULT), userConfig.getPropertyMapping());
+        assertEquals(PARAM_USER_PATH_PREFIX_DEFAULT, userConfig.getPathPrefix());
+    }
+
+    @Test
+    public void testDefaultGroup() {
+        DefaultSyncConfig.Group groupConfig = DefaultSyncConfigImpl.of(ConfigurationParameters.EMPTY).group();
+        assertEquals(ConfigurationParameters.Milliseconds.of(PARAM_GROUP_EXPIRATION_TIME_DEFAULT), ConfigurationParameters.Milliseconds.of(groupConfig.getExpirationTime()));
+        assertEquals(PARAM_ENABLE_RFC7613_USERCASE_MAPPED_PROFILE_DEFAULT, groupConfig.isApplyRFC7613UsernameCaseMapped());
+        assertArrayEquals(PARAM_GROUP_AUTO_MEMBERSHIP_DEFAULT, groupConfig.getAutoMembership().toArray(new String[0]));
+        assertEquals(getMapping(PARAM_GROUP_PROPERTY_MAPPING_DEFAULT), groupConfig.getPropertyMapping());
+        assertEquals(PARAM_GROUP_PATH_PREFIX_DEFAULT, groupConfig.getPathPrefix());
+    }
+
+    @Test
+    public void testInvalidMapping() {
+        String[] invalidMapping = new String[] {"invalid:Mapping"};
+        DefaultSyncConfig.User userConfig = DefaultSyncConfigImpl.of(ConfigurationParameters.of(PARAM_USER_PROPERTY_MAPPING, invalidMapping)).user();
+        assertEquals(Collections.emptyMap(), userConfig.getPropertyMapping());
+    }
+
+    @Test
+    public void testValidMapping() {
+        String[] validMapping = new String[] {"valid=mapping","valid2=mapping"};
+        DefaultSyncConfig.User userConfig = DefaultSyncConfigImpl.of(ConfigurationParameters.of(PARAM_USER_PROPERTY_MAPPING, validMapping)).user();
+        assertEquals(ImmutableMap.of("valid","mapping","valid2","mapping"), userConfig.getPropertyMapping());
+    }
+
+    private static Map<String,String> getMapping(@NotNull String[] defaultMapping) {
+        Map<String,String> expectedMapping = Maps.newHashMap();
+        for (String s : defaultMapping) {
+            int indx = s.indexOf('=');
+            expectedMapping.put(s.substring(0, indx), s.substring(indx+1));
+        }
+        return expectedMapping;
+    }
+}
\ 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/DefaultSyncConfigImplTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandlerTest.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/DefaultSyncHandlerTest.java?rev=1869208&r1=1869207&r2=1869208&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandlerTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandlerTest.java Thu Oct 31 10:00:56 2019
@@ -18,10 +18,13 @@ package org.apache.jackrabbit.oak.spi.se
 
 import java.util.Calendar;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 import javax.jcr.SimpleCredentials;
 import javax.jcr.Value;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterators;
 import com.google.common.collect.Sets;
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.UserManager;
@@ -48,6 +51,8 @@ import static org.junit.Assert.assertNul
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 /**
  * DefaultSyncHandlerTest
@@ -267,6 +272,28 @@ public class DefaultSyncHandlerTest exte
     }
 
     @Test
+    public void testListIdentitiesIgnoresMissingExternalIdRef() throws Exception {
+        Iterator<Authorizable> it = Iterators.singletonIterator(getTestUser());
+
+        UserManager um = mock(UserManager.class);
+        when(um.findAuthorizables(DefaultSyncContext.REP_EXTERNAL_ID, null)).thenReturn(it);
+
+        Iterator<SyncedIdentity> identities = syncHandler.listIdentities(um);
+        assertFalse(identities.hasNext());
+    }
+
+    @Test
+    public void testListIdentitiesIgnoresNull() throws Exception {
+        Iterator<Authorizable> it = Iterators.singletonIterator(null);
+
+        UserManager um = mock(UserManager.class);
+        when(um.findAuthorizables(DefaultSyncContext.REP_EXTERNAL_ID, null)).thenReturn(it);
+
+        Iterator<SyncedIdentity> identities = syncHandler.listIdentities(um);
+        assertFalse(identities.hasNext());
+    }
+
+    @Test
     public void testLastSynced() throws Exception {
         sync(USER_ID, false);
 
@@ -286,4 +313,13 @@ public class DefaultSyncHandlerTest exte
         // the conflict handler needs to fix overlapping update
         root.commit();
     }
+
+    @Test
+    public void testActivate() {
+        DefaultSyncHandler handler = new DefaultSyncHandler();
+        Map<String,Object> props = ImmutableMap.of(DefaultSyncConfigImpl.PARAM_NAME, "testName");
+        context.registerInjectActivateService(handler, props);
+
+        assertEquals("testName", handler.getName());
+    }
 }

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=1869208&r1=1869207&r2=1869208&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 Thu Oct 31 10:00:56 2019
@@ -92,6 +92,7 @@ public class DynamicSyncContextTest exte
     }
 
     @Override
+    @NotNull
     protected DefaultSyncConfig createSyncConfig() {
         DefaultSyncConfig sc = super.createSyncConfig();
         sc.user().setDynamicMembership(true);

Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalIDPManagerImplTest.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/ExternalIDPManagerImplTest.java?rev=1869208&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalIDPManagerImplTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalIDPManagerImplTest.java Thu Oct 31 10:00:56 2019
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProvider;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ExternalIDPManagerImplTest {
+
+    @Rule
+    public final OsgiContext context = new OsgiContext();
+
+    private ExternalIDPManagerImpl externalIDPManager = new ExternalIDPManagerImpl();
+
+    @Before
+    public void before() {
+        context.registerInjectActivateService(externalIDPManager);
+    }
+
+    @Test
+    public void testGetProvider() {
+        assertNull(externalIDPManager.getProvider("test"));
+        assertNull(externalIDPManager.getProvider("unknown"));
+
+        ExternalIdentityProvider provider = when(mock(ExternalIdentityProvider.class).getName()).thenReturn("test").getMock();
+        context.registerService(ExternalIdentityProvider.class, provider);
+
+        assertSame(provider, externalIDPManager.getProvider("test"));
+        assertNull(externalIDPManager.getProvider("unknown"));
+    }
+}
\ 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/ExternalIDPManagerImplTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleFactoryTest.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/ExternalLoginModuleFactoryTest.java?rev=1869208&r1=1869207&r2=1869208&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleFactoryTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleFactoryTest.java Thu Oct 31 10:00:56 2019
@@ -16,13 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.jackrabbit.oak.spi.security.authentication.external.impl;
 
-import javax.jcr.Repository;
-import javax.jcr.SimpleCredentials;
-import javax.security.auth.login.AppConfigurationEntry;
-import javax.security.auth.login.Configuration;
-import javax.security.auth.spi.LoginModule;
+package org.apache.jackrabbit.oak.spi.security.authentication.external.impl;
 
 import org.apache.felix.jaas.LoginModuleFactory;
 import org.apache.felix.jaas.boot.ProxyLoginModule;
@@ -34,11 +29,20 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalLoginModuleTestBase;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncManager;
+import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.jetbrains.annotations.NotNull;
 import org.junit.Test;
 
+import javax.jcr.Repository;
+import javax.jcr.SimpleCredentials;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import java.lang.reflect.Field;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 
@@ -68,8 +72,6 @@ public class ExternalLoginModuleFactoryT
         };
     }
 
-    //~-------------------------------------------------------------< tests >---
-
     @Test
     public void testSyncCreateUser() throws Exception {
         setUpJaasFactoryWithInjection();
@@ -85,6 +87,7 @@ public class ExternalLoginModuleFactoryT
             Authorizable a = userManager.getAuthorizable(USER_ID);
             assertNotNull(a);
             ExternalUser user = idp.getUser(USER_ID);
+            assertNotNull(user);
             for (String prop : user.getProperties().keySet()) {
                 assertTrue(a.hasProperty(prop));
             }
@@ -107,11 +110,113 @@ public class ExternalLoginModuleFactoryT
         context.registerService(ExternalIdentityProviderManager.class, new ExternalIDPManagerImpl(whiteboard));
 
         final LoginModuleFactory lmf = context.registerInjectActivateService(new ExternalLoginModuleFactory());
-        options.put(ProxyLoginModule.PROP_LOGIN_MODULE_FACTORY, new ProxyLoginModule.BootLoginModuleFactory() {
-            @Override
-            public LoginModule createLoginModule() {
-                return lmf.createLoginModule();
-            }
-        });
+        options.put(ProxyLoginModule.PROP_LOGIN_MODULE_FACTORY, (ProxyLoginModule.BootLoginModuleFactory) () -> lmf.createLoginModule());
+    }
+
+    @Test
+    public void testMissingBundleContext() throws Exception {
+        ExternalLoginModuleFactory factory = new ExternalLoginModuleFactory();
+
+        factory.bindContentRepository(getContentRepository());
+        factory.bindSecurityProvider(getSecurityProvider());
+
+        assertNull(getMBeanRegistration(factory));
+
+        factory.unbindContentRepository(getContentRepository());
+        factory.unbindSecurityProvider(getSecurityProvider());
+
+        assertNull(getMBeanRegistration(factory));
+    }
+
+    @Test
+    public void testBindNullContentRepository() throws Exception {
+        context.registerService(SyncManager.class, mock(SyncManager.class));
+        context.registerService(ExternalIdentityProviderManager.class, mock(ExternalIdentityProviderManager.class));
+
+        ExternalLoginModuleFactory factory = new ExternalLoginModuleFactory();
+        context.registerInjectActivateService(factory);
+
+        assertNull(getMBeanRegistration(factory));
+
+        factory.bindContentRepository(null);
+        factory.bindSecurityProvider(getSecurityProvider());
+
+        assertNull(getMBeanRegistration(factory));
+
+        factory.unbindContentRepository(null);
+        factory.unbindSecurityProvider(getSecurityProvider());
+
+        assertNull(getMBeanRegistration(factory));
+    }
+
+    @Test
+    public void testBindNullSecurityProvider() throws Exception {
+        context.registerService(SyncManager.class, mock(SyncManager.class));
+        context.registerService(ExternalIdentityProviderManager.class, mock(ExternalIdentityProviderManager.class));
+
+        ExternalLoginModuleFactory factory = new ExternalLoginModuleFactory();
+        context.registerInjectActivateService(factory);
+
+        assertNull(getMBeanRegistration(factory));
+
+        factory.bindContentRepository(getContentRepository());
+        factory.bindSecurityProvider(null);
+
+        assertNull(getMBeanRegistration(factory));
+
+        factory.unbindContentRepository(getContentRepository());
+        factory.unbindSecurityProvider(getSecurityProvider());
+
+        assertNull(getMBeanRegistration(factory));
+    }
+
+    @Test
+    public void testMbeanRegistration() throws Exception {
+        context.registerService(SyncManager.class, mock(SyncManager.class));
+        context.registerService(ExternalIdentityProviderManager.class, mock(ExternalIdentityProviderManager.class));
+
+        ExternalLoginModuleFactory factory = new ExternalLoginModuleFactory();
+        context.registerInjectActivateService(factory);
+
+        assertNull(getMBeanRegistration(factory));
+
+        factory.bindSecurityProvider(getSecurityProvider());
+        factory.bindContentRepository(getContentRepository());
+
+        Registration mbeanregistration = getMBeanRegistration(factory);
+        assertNotNull(mbeanregistration);
+
+        factory.unbindContentRepository(getContentRepository());
+        assertNull(getMBeanRegistration(factory));
+
+        factory.unbindSecurityProvider(getSecurityProvider());
+        assertNull(getMBeanRegistration(factory));
+    }
+
+    @Test
+    public void testMBeanRegistrationAlreadyPresent() throws Exception {
+        context.registerService(SyncManager.class, mock(SyncManager.class));
+        context.registerService(ExternalIdentityProviderManager.class, mock(ExternalIdentityProviderManager.class));
+
+        ExternalLoginModuleFactory factory = new ExternalLoginModuleFactory();
+        context.registerInjectActivateService(factory);
+
+        assertNull(getMBeanRegistration(factory));
+
+        factory.bindSecurityProvider(getSecurityProvider());
+        factory.bindContentRepository(getContentRepository());
+
+        Registration mbeanregistration = getMBeanRegistration(factory);
+        assertNotNull(mbeanregistration);
+
+        factory.bindContentRepository(getContentRepository());
+        assertSame(mbeanregistration, getMBeanRegistration(factory));
+    }
+
+    private static Registration getMBeanRegistration(@NotNull ExternalLoginModuleFactory factory) throws Exception {
+        Field f = ExternalLoginModuleFactory.class.getDeclaredField("mbeanRegistration");
+        f.setAccessible(true);
+
+        return (Registration) f.get(factory);
     }
 }

Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.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/ExternalLoginModuleTest.java?rev=1869208&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java Thu Oct 31 10:00:56 2019
@@ -0,0 +1,428 @@
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.AuthInfo;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.ImpersonationCredentials;
+import org.apache.jackrabbit.oak.spi.security.authentication.PreAuthenticatedLogin;
+import org.apache.jackrabbit.oak.spi.security.authentication.callback.CredentialsCallback;
+import org.apache.jackrabbit.oak.spi.security.authentication.callback.RepositoryCallback;
+import org.apache.jackrabbit.oak.spi.security.authentication.callback.WhiteboardCallback;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.CredentialsSupport;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProviderManager;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncException;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncManager;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.TestIdentityProvider;
+import org.apache.jackrabbit.oak.spi.security.principal.EmptyPrincipalProvider;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration;
+import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
+import org.apache.jackrabbit.oak.spi.whiteboard.DefaultWhiteboard;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+
+import javax.jcr.Credentials;
+import javax.jcr.GuestCredentials;
+import javax.jcr.SimpleCredentials;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.jackrabbit.oak.api.CommitFailedException.OAK;
+import static org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule.SHARED_KEY_ATTRIBUTES;
+import static org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule.SHARED_KEY_PRE_AUTH_LOGIN;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.TestIdentityProvider.DEFAULT_IDP_NAME;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.TestIdentityProvider.ID_TEST_USER;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalIdentityConstants.REP_EXTERNAL_ID;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.SyncHandlerMapping.PARAM_IDP_NAME;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.impl.SyncHandlerMapping.PARAM_SYNC_HANDLER_NAME;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
+
+public class ExternalLoginModuleTest extends AbstractSecurityTest {
+
+    private final ExternalLoginModule loginModule = new ExternalLoginModule();
+
+    private final Whiteboard wb = spy(new DefaultWhiteboard());
+    private final ExternalIdentityProviderManager extIPMgr = mock(ExternalIdentityProviderManager.class);
+    private final SyncManager syncManager = mock(SyncManager.class);
+
+    private CallbackHandler createCallbackHandler(@Nullable Whiteboard wb, @Nullable ContentRepository contentRepository, @Nullable SecurityProvider securityProvider, @Nullable Credentials creds) {
+        return callbacks -> {
+            for (Callback cb : callbacks) {
+                if (cb instanceof WhiteboardCallback) {
+                    ((WhiteboardCallback) cb).setWhiteboard(wb);
+                } else if (cb instanceof RepositoryCallback) {
+                    ((RepositoryCallback) cb).setContentRepository(contentRepository);
+                    ((RepositoryCallback) cb).setSecurityProvider(securityProvider);
+                } else if (cb instanceof CredentialsCallback) {
+                    ((CredentialsCallback) cb).setCredentials(creds);
+                }else {
+                    throw new UnsupportedCallbackException(cb);
+                }
+            }
+        };
+    }
+
+    @Test
+    public void testInitializeMissingWhiteboard() throws LoginException {
+        loginModule.setIdpManager(extIPMgr);
+        loginModule.setSyncManager(syncManager);
+
+        CallbackHandler cbh = mock(CallbackHandler.class);
+        loginModule.initialize(new Subject(), cbh, Collections.emptyMap(), ImmutableMap.of(PARAM_IDP_NAME, "idp", PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+
+        verify(extIPMgr, never()).getProvider("idp");
+        verify(syncManager, never()).getSyncHandler("syncHandler");
+        assertFalse(loginModule.login());
+    }
+
+    @Test
+    public void testInitializeMissingIdpSyncHandler() throws LoginException {
+        loginModule.initialize(new Subject(), createCallbackHandler(wb, null, null, null), Collections.emptyMap(), ImmutableMap.of(PARAM_IDP_NAME, "idp", PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+        assertFalse(loginModule.login());
+    }
+
+    @Test
+    public void testInitializedUnknownIdpSyncHandlerNames() throws LoginException {
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+        wb.register(SyncManager.class, syncManager, Collections.emptyMap());
+
+        loginModule.initialize(new Subject(), createCallbackHandler(wb, null, null, null), Collections.emptyMap(), ImmutableMap.of(PARAM_IDP_NAME, "idp", PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+
+        verify(extIPMgr, times(1)).getProvider("idp");
+        verify(syncManager, times(1)).getSyncHandler("syncHandler");
+        assertFalse(loginModule.login());
+    }
+
+    @Test
+    public void testInitializEmptyIdpName() {
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+
+        loginModule.initialize(new Subject(), createCallbackHandler(wb, null, null, null), Collections.emptyMap(), Collections.singletonMap(PARAM_IDP_NAME, ""));
+        verify(extIPMgr, never()).getProvider("");
+    }
+
+    @Test
+    public void testInitializEmptySyncHanderName() {
+        wb.register(SyncManager.class, syncManager, Collections.emptyMap());
+
+        loginModule.initialize(new Subject(), createCallbackHandler(wb, null, null, null), Collections.emptyMap(), Collections.singletonMap(PARAM_SYNC_HANDLER_NAME, ""));
+        verify(syncManager, never()).getSyncHandler("");
+    }
+
+
+    @Test
+    public void testLoginWithoutInit() throws Exception {
+        assertFalse(loginModule.login());
+    }
+
+    @Test
+    public void testLoginMissingSyncHandler() throws Exception {
+        when(extIPMgr.getProvider("idpName")).thenReturn(new TestIdentityProvider());
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+
+        loginModule.initialize(new Subject(), createCallbackHandler(wb, null, null, null), Collections.emptyMap(), Collections.singletonMap(PARAM_IDP_NAME, "idpName"));
+        assertFalse(loginModule.login());
+    }
+
+    @Test
+    public void testLoginMissingUserId() throws Exception {
+        ExternalIdentityProvider idp = mock(ExternalIdentityProvider.class, withSettings().extraInterfaces(CredentialsSupport.class));
+        when(idp.getName()).thenReturn(DEFAULT_IDP_NAME);
+        when(((CredentialsSupport) idp).getUserId(any(Credentials.class))).thenReturn(null);
+        when(((CredentialsSupport) idp).getCredentialClasses()).thenReturn(ImmutableSet.of(GuestCredentials.class));
+
+        when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(idp);
+        when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler")));
+
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+        wb.register(SyncManager.class, syncManager, Collections.emptyMap());
+
+        CallbackHandler cbh = createCallbackHandler(wb, getContentRepository(), getSecurityProvider(), new GuestCredentials());
+
+        loginModule.initialize(new Subject(), cbh, new HashMap<>(), ImmutableMap.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+        assertFalse(loginModule.login());
+    }
+
+    @Test
+    public void testLoginCommitUpdatesSubject() throws Exception {
+        when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(new TestIdentityProvider());
+        when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler")));
+
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+        wb.register(SyncManager.class, syncManager, Collections.emptyMap());
+
+        Map<String,Object> sharedState = Maps.newHashMap();
+        sharedState.put(SHARED_KEY_PRE_AUTH_LOGIN, new PreAuthenticatedLogin(ID_TEST_USER));
+        sharedState.put(SHARED_KEY_ATTRIBUTES, Collections.singletonMap("att", "value"));
+
+        CallbackHandler cbh = createCallbackHandler(wb, getContentRepository(), getSecurityProvider(), null);
+
+        Subject subject = new Subject();
+        loginModule.initialize(subject, cbh, sharedState, ImmutableMap.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+        assertTrue(loginModule.login());
+        assertTrue(loginModule.commit());
+
+        AuthInfo authInfo = subject.getPublicCredentials(AuthInfo.class).iterator().next();
+        assertEquals("value", authInfo.getAttribute("att"));
+
+        root.refresh();
+        assertNotNull(getUserManager(root).getAuthorizable(ID_TEST_USER));
+    }
+
+    @Test
+    public void testLoginCommitEmptyPrincipalSet() throws Exception {
+        when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(new TestIdentityProvider());
+        when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler")));
+
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+        wb.register(SyncManager.class, syncManager, Collections.emptyMap());
+
+        Map<String,Object> sharedState = Maps.newHashMap();
+        sharedState.put(SHARED_KEY_PRE_AUTH_LOGIN, new PreAuthenticatedLogin(ID_TEST_USER));
+        sharedState.put(SHARED_KEY_ATTRIBUTES, Collections.singletonMap("att", "value"));
+
+        PrincipalConfiguration pc = when(mock(PrincipalConfiguration.class).getPrincipalProvider(any(Root.class), any(NamePathMapper.class))).thenReturn(EmptyPrincipalProvider.INSTANCE).getMock();
+        SecurityProvider sp = spy(getSecurityProvider());
+        when(sp.getConfiguration(PrincipalConfiguration.class)).thenReturn(pc);
+
+        CallbackHandler cbh = createCallbackHandler(wb, getContentRepository(), sp, null);
+
+        Subject subject = new Subject();
+        loginModule.initialize(subject, cbh, sharedState, ImmutableMap.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+        assertTrue(loginModule.login());
+        assertFalse(loginModule.commit());
+
+        assertFalse(subject.getPublicCredentials(AuthInfo.class).iterator().hasNext());
+        assertTrue(subject.getPrincipals().isEmpty());
+    }
+
+    @Test
+    public void testLoginCommitImpersonationCredentials() throws Exception {
+        SimpleCredentials sc = new SimpleCredentials(ID_TEST_USER, new char[0]);
+        ImpersonationCredentials creds = new ImpersonationCredentials(sc, AuthInfo.EMPTY);
+        ExternalIdentityProvider idp = mock(ExternalIdentityProvider.class, withSettings().extraInterfaces(CredentialsSupport.class));
+        when(idp.getName()).thenReturn(DEFAULT_IDP_NAME);
+        when(idp.authenticate(creds)).thenReturn(new TestIdentityProvider.TestUser(ID_TEST_USER, DEFAULT_IDP_NAME));
+        when(((CredentialsSupport) idp).getUserId(any(Credentials.class))).thenReturn(ID_TEST_USER);
+        when(((CredentialsSupport) idp).getCredentialClasses()).thenReturn(ImmutableSet.of(ImpersonationCredentials.class));
+        Map attr = ImmutableMap.of("attr","value");
+        when(((CredentialsSupport) idp).getAttributes(creds)).thenReturn(attr);
+        when(((CredentialsSupport) idp).getAttributes(sc)).thenReturn(Collections.emptyMap());
+
+        when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(idp);
+        when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler")));
+
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+        wb.register(SyncManager.class, syncManager, Collections.emptyMap());
+
+        CallbackHandler cbh = createCallbackHandler(wb, getContentRepository(), getSecurityProvider(), creds);
+
+        Subject subject = new Subject();
+        loginModule.initialize(subject, cbh, Maps.newHashMap(), ImmutableMap.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+        assertTrue(loginModule.login());
+        assertTrue(loginModule.commit());
+
+        AuthInfo authInfo = subject.getPublicCredentials(AuthInfo.class).iterator().next();
+        assertNull(authInfo.getAttribute("attr"));
+    }
+
+    @Test
+    public void testLoginCommitReadonlySubject() throws Exception {
+        when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(new TestIdentityProvider());
+        when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler")));
+
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+        wb.register(SyncManager.class, syncManager, Collections.emptyMap());
+
+        Map<String,Object> sharedState = Maps.newHashMap();
+        sharedState.put(SHARED_KEY_PRE_AUTH_LOGIN, new PreAuthenticatedLogin(ID_TEST_USER));
+        sharedState.put(SHARED_KEY_ATTRIBUTES, Collections.singletonMap("att", "value"));
+
+        CallbackHandler cbh = createCallbackHandler(wb, getContentRepository(), getSecurityProvider(), null);
+
+        Subject readOnly = new Subject(true, Collections.emptySet(), Collections.emptySet(), Collections.emptySet());
+        loginModule.initialize(readOnly, cbh, sharedState, ImmutableMap.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+        assertTrue(loginModule.login());
+        assertTrue(loginModule.commit());
+
+        root.refresh();
+        assertNotNull(getUserManager(root).getAuthorizable(ID_TEST_USER));
+    }
+
+    @Test(expected = LoginException.class)
+    public void testSyncUserMissingRoot() throws LoginException {
+        when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(new TestIdentityProvider());
+        when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler")));
+
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+        wb.register(SyncManager.class, syncManager, Collections.emptyMap());
+
+        Map<String,Object> sharedState = Maps.newHashMap();
+        sharedState.put(SHARED_KEY_PRE_AUTH_LOGIN, new PreAuthenticatedLogin(ID_TEST_USER));
+
+        loginModule.initialize(new Subject(), createCallbackHandler(wb, null, null, null), sharedState, ImmutableMap.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+        try {
+            loginModule.login();
+        } catch (LoginException e) {
+            assertTrue(e.getCause() instanceof SyncException);
+            throw e;
+        }
+    }
+
+    @Test(expected = LoginException.class)
+    public void testSyncUserMissingUserManager() throws LoginException {
+        when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(new TestIdentityProvider());
+        when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler")));
+
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+        wb.register(SyncManager.class, syncManager, Collections.emptyMap());
+
+        UserConfiguration uc = mock(UserConfiguration.class);
+        SecurityProvider sp = when(mock(SecurityProvider.class).getConfiguration(UserConfiguration.class)).thenReturn(uc).getMock();
+
+        CallbackHandler cbh = createCallbackHandler(wb, getContentRepository(), sp, null);
+
+        Map<String,Object> sharedState = Maps.newHashMap();
+        sharedState.put(SHARED_KEY_PRE_AUTH_LOGIN, new PreAuthenticatedLogin(ID_TEST_USER));
+
+        loginModule.initialize(new Subject(), cbh, sharedState, ImmutableMap.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+        try {
+            loginModule.login();
+        } catch (LoginException e) {
+            assertTrue(e.getCause() instanceof SyncException);
+            throw e;
+        }
+    }
+
+    @Test(expected = LoginException.class)
+    public void testSyncUserFailsCommit() throws Exception {
+        when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(new TestIdentityProvider());
+        when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler")));
+
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+        wb.register(SyncManager.class, syncManager, Collections.emptyMap());
+
+        ContentRepository repository = spy(getContentRepository());
+        ContentSession s = spy(adminSession);
+        Root r = spy(root);
+        doThrow(new CommitFailedException(OAK, -1, "error")).when(r).commit();
+        when(r.hasPendingChanges()).thenReturn(true);
+        doReturn(s).when(repository).login(null, null);
+        when(s.getLatestRoot()).thenReturn(r);
+
+        CallbackHandler cbh = createCallbackHandler(wb, repository, getSecurityProvider(), null);
+
+        Map<String,Object> sharedState = Maps.newHashMap();
+        sharedState.put(SHARED_KEY_PRE_AUTH_LOGIN, new PreAuthenticatedLogin(ID_TEST_USER));
+
+        loginModule.initialize(new Subject(), cbh, sharedState, ImmutableMap.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+        try {
+            loginModule.login();
+        } catch (LoginException e) {
+            assertTrue(e.getCause() instanceof SyncException);
+            throw e;
+        }
+    }
+
+    @Test
+    public void testValidateUser() throws Exception {
+        when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(new TestIdentityProvider());
+        when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler")));
+
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+        wb.register(SyncManager.class, syncManager, Collections.emptyMap());
+
+        Map<String,Object> sharedState = Maps.newHashMap();
+        sharedState.put(SHARED_KEY_PRE_AUTH_LOGIN, new PreAuthenticatedLogin("local"));
+
+        User u = getUserManager(root).createUser("local", null);
+        u.setProperty(REP_EXTERNAL_ID, getValueFactory().createValue("local;"+TestIdentityProvider.DEFAULT_IDP_NAME));
+        root.commit();
+
+        CallbackHandler cbh = createCallbackHandler(wb, getContentRepository(), getSecurityProvider(), null);
+
+        loginModule.initialize(new Subject(), cbh, sharedState, ImmutableMap.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+        assertFalse(loginModule.login());
+        root.refresh();
+        assertNull(getUserManager(root).getAuthorizable("local"));
+    }
+
+    @Test(expected = LoginException.class)
+    public void testValidateUserFailsCommit() throws Exception {
+        when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(new TestIdentityProvider());
+        when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler")));
+
+        wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap());
+        wb.register(SyncManager.class, syncManager, Collections.emptyMap());
+
+        Map<String,Object> sharedState = Maps.newHashMap();
+        sharedState.put(SHARED_KEY_PRE_AUTH_LOGIN, new PreAuthenticatedLogin("local"));
+
+        User u = getUserManager(root).createUser("local", null);
+        u.setProperty(REP_EXTERNAL_ID, getValueFactory().createValue("local;"+TestIdentityProvider.DEFAULT_IDP_NAME));
+
+        ContentRepository repository = spy(getContentRepository());
+        ContentSession s = spy(adminSession);
+        Root r = spy(root);
+        doThrow(new CommitFailedException(OAK, -1, "error")).when(r).commit();
+        when(r.hasPendingChanges()).thenReturn(true);
+        doReturn(s).when(repository).login(null, null);
+        when(s.getLatestRoot()).thenReturn(r);
+
+        CallbackHandler cbh = createCallbackHandler(wb, repository, getSecurityProvider(), null);
+
+        loginModule.initialize(new Subject(), cbh, sharedState, ImmutableMap.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+        try {
+            loginModule.login();
+        } catch (LoginException e) {
+            assertTrue(e.getCause() instanceof SyncException);
+            throw e;
+        }
+    }
+}
\ 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/ExternalLoginModuleTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/SyncManagerImplTest.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/SyncManagerImplTest.java?rev=1869208&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/SyncManagerImplTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/SyncManagerImplTest.java Thu Oct 31 10:00:56 2019
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncHandler;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SyncManagerImplTest {
+
+    @Rule
+    public final OsgiContext context = new OsgiContext();
+
+    private SyncManagerImpl syncManager = new SyncManagerImpl();
+
+    @Before()
+    public void before() {
+        context.registerInjectActivateService(syncManager);
+    }
+
+    @Test
+    public void testGetHandler() {
+        assertNull(syncManager.getSyncHandler("test"));
+        assertNull(syncManager.getSyncHandler("another"));
+
+        SyncHandler syncHandler = when(mock(SyncHandler.class).getName()).thenReturn("test").getMock();
+        context.registerService(SyncHandler.class, syncHandler);
+
+        assertSame(syncHandler, syncManager.getSyncHandler("test"));
+        assertNull(syncManager.getSyncHandler("another"));
+    }
+}
\ 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/SyncManagerImplTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/DelegateeTest.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/jmx/DelegateeTest.java?rev=1869208&r1=1869207&r2=1869208&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/DelegateeTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/DelegateeTest.java Thu Oct 31 10:00:56 2019
@@ -16,47 +16,84 @@
  */
 package org.apache.jackrabbit.oak.spi.security.authentication.external.impl.jmx;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.ContentSession;
 import org.apache.jackrabbit.oak.api.QueryEngine;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentity;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityException;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProvider;
 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.SyncContext;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncHandler;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncResult;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncedIdentity;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.TestIdentityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncResultImpl;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncedIdentity;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import javax.jcr.ValueFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.TestIdentityProvider.DEFAULT_IDP_NAME;
+import static org.apache.jackrabbit.oak.spi.security.authentication.external.TestIdentityProvider.ID_TEST_USER;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
+@RunWith(Parameterized.class)
 public class DelegateeTest extends AbstractJmxTest {
 
+    @Parameterized.Parameters(name = "name={1}")
+    public static Collection<Object[]> parameters() {
+        return Lists.newArrayList(
+                new Object[] { 100, "BatchSize 100" },
+                new Object[] { 1, "BatchSize 1" },
+                new Object[] { 2, "BatchSize 2" });
+    };
+
+    private final int batchSize;
+
     private Delegatee delegatee;
 
     private static final String[] TEST_IDS = new String[] {
-            TestIdentityProvider.ID_TEST_USER,
+            ID_TEST_USER,
             TestIdentityProvider.ID_SECOND_USER,
             TestIdentityProvider.ID_WILDCARD_USER};
 
+    public DelegateeTest(int batchSize, String name) {
+        this.batchSize = batchSize;
+    }
+
     @Before
     public void before() throws Exception {
         super.before();
 
-        delegatee = createDelegatee(new TestIdentityProvider());
+        delegatee = createDelegatee(new TestIdentityProvider(), new DefaultSyncHandler(syncConfig));
     }
 
     @Override
@@ -70,12 +107,8 @@ public class DelegateeTest extends Abstr
         }
     }
 
-    int getBatchSize() {
-        return 100;
-    }
-
-    private Delegatee createDelegatee(@NotNull ExternalIdentityProvider idp) {
-        return Delegatee.createInstance(getContentRepository(), getSecurityProvider(), new DefaultSyncHandler(syncConfig), idp, getBatchSize());
+    private Delegatee createDelegatee(@NotNull ExternalIdentityProvider idp, @NotNull SyncHandler syncHandler) {
+        return Delegatee.createInstance(getContentRepository(), getSecurityProvider(), syncHandler, idp, batchSize);
     }
 
     private static Root preventRootCommit(@NotNull Delegatee delegatee) throws Exception {
@@ -100,7 +133,7 @@ public class DelegateeTest extends Abstr
 
         String[] result = delegatee.syncUsers(TEST_IDS, false);
         assertResultMessages(result, ImmutableMap.of(
-                TestIdentityProvider.ID_TEST_USER, "nsa",
+                ID_TEST_USER, "nsa",
                 TestIdentityProvider.ID_SECOND_USER, "nsa",
                 TestIdentityProvider.ID_WILDCARD_USER, "nsa"));
         assertFalse(r.hasPendingChanges());
@@ -108,18 +141,18 @@ public class DelegateeTest extends Abstr
 
     @Test
     public void testSyncUsersSaveError() throws Exception {
-        sync(idp, TestIdentityProvider.ID_TEST_USER, false);
+        sync(idp, ID_TEST_USER, false);
         sync(foreignIDP, TestIdentityProvider.ID_SECOND_USER, false);
         // don't sync ID_WILDCARD_USER
 
         Root r = preventRootCommit(delegatee);
 
         String[] result = delegatee.syncUsers(new String[] {
-                TestIdentityProvider.ID_TEST_USER,
+                ID_TEST_USER,
                 TestIdentityProvider.ID_SECOND_USER,
                 TestIdentityProvider.ID_WILDCARD_USER}, false);
         assertResultMessages(result, ImmutableMap.of(
-                TestIdentityProvider.ID_TEST_USER, "ERR",
+                ID_TEST_USER, "ERR",
                 TestIdentityProvider.ID_SECOND_USER, "for",
                 TestIdentityProvider.ID_WILDCARD_USER, "nsa"));
         assertFalse(r.hasPendingChanges());
@@ -136,7 +169,7 @@ public class DelegateeTest extends Abstr
 
     @Test
     public void testSyncAllUsersSaveError() throws Exception {
-        sync(idp, TestIdentityProvider.ID_TEST_USER, false);
+        sync(idp, ID_TEST_USER, false);
         sync(idp, TestIdentityProvider.ID_SECOND_USER, false);
         sync(new TestIdentityProvider.TestUser("third", idp.getName()), idp);
         sync(foreignIDP, TestIdentityProvider.ID_WILDCARD_USER, false);
@@ -144,7 +177,7 @@ public class DelegateeTest extends Abstr
         Root r = preventRootCommit(delegatee);;
 
         ImmutableMap<String, String> expected = ImmutableMap.<String, String>builder()
-                .put(TestIdentityProvider.ID_TEST_USER, "ERR")
+                .put(ID_TEST_USER, "ERR")
                 .put("a", "ERR")
                 .put("b", "ERR")
                 .put("c", "ERR")
@@ -160,7 +193,7 @@ public class DelegateeTest extends Abstr
 
     @Test
     public void testSyncAllUsersPurgeSaveError() throws Exception {
-        sync(idp, TestIdentityProvider.ID_TEST_USER, false);
+        sync(idp, ID_TEST_USER, false);
         sync(idp, TestIdentityProvider.ID_SECOND_USER, false);
         sync(new TestIdentityProvider.TestUser("third", idp.getName()), idp);
         sync(foreignIDP, TestIdentityProvider.ID_WILDCARD_USER, false);
@@ -168,7 +201,7 @@ public class DelegateeTest extends Abstr
         Root r = preventRootCommit(delegatee);;
 
         ImmutableMap<String, String> expected = ImmutableMap.<String, String>builder()
-                .put(TestIdentityProvider.ID_TEST_USER, "ERR")
+                .put(ID_TEST_USER, "ERR")
                 .put("a", "ERR")
                 .put("b", "ERR")
                 .put("c", "ERR")
@@ -195,8 +228,8 @@ public class DelegateeTest extends Abstr
     public void testSyncForeignExternalUserSaveError() throws Exception {
         Root r = preventRootCommit(delegatee);;
 
-        String[] result = delegatee.syncExternalUsers(new String[] {new ExternalIdentityRef(TestIdentityProvider.ID_TEST_USER, foreignIDP.getName()).getString()});
-        assertResultMessages(result, TestIdentityProvider.ID_TEST_USER, "for");
+        String[] result = delegatee.syncExternalUsers(new String[] {new ExternalIdentityRef(ID_TEST_USER, foreignIDP.getName()).getString()});
+        assertResultMessages(result, ID_TEST_USER, "for");
         assertFalse(r.hasPendingChanges());
     }
 
@@ -219,19 +252,33 @@ public class DelegateeTest extends Abstr
         }
         String[] result = delegatee.syncExternalUsers(externalIds.toArray(new String[0]));
         assertResultMessages(result, ImmutableMap.of(
-                TestIdentityProvider.ID_TEST_USER, "ERR",
+                ID_TEST_USER, "ERR",
                 TestIdentityProvider.ID_SECOND_USER, "ERR",
                 TestIdentityProvider.ID_WILDCARD_USER, "ERR"));
         assertFalse(r.hasPendingChanges());
     }
 
     @Test
+    public void testSyncExternalUsersGeneratesNullIdentity() throws Exception {
+        SyncContext ctx = mock(SyncContext.class);
+        when(ctx.sync(any(ExternalIdentity.class))).thenReturn(new DefaultSyncResultImpl(null, SyncResult.Status.NOP));
+        when(ctx.setForceGroupSync(anyBoolean())).thenReturn(ctx);
+        SyncHandler syncHandler = when(mock(SyncHandler.class).createContext(any(ExternalIdentityProvider.class), any(UserManager.class), any(ValueFactory.class))).thenReturn(ctx).getMock();
+
+        Delegatee d = createDelegatee(new TestIdentityProvider(), syncHandler);
+        ExternalIdentityRef ref = new ExternalIdentityRef(ID_TEST_USER, DEFAULT_IDP_NAME);
+        String[] res = d.syncExternalUsers(new String[] {ref.getString()});
+
+        assertResultMessages(res, ID_TEST_USER, "nsi");
+    }
+
+    @Test
     public void testSyncAllExternalUsersSaveError() throws Exception {
         Root r = preventRootCommit(delegatee);;
 
         String[] result = delegatee.syncAllExternalUsers();
         assertResultMessages(result, ImmutableMap.of(
-                TestIdentityProvider.ID_TEST_USER, "ERR",
+                ID_TEST_USER, "ERR",
                 TestIdentityProvider.ID_SECOND_USER, "ERR",
                 TestIdentityProvider.ID_WILDCARD_USER, "ERR"));
         assertFalse(r.hasPendingChanges());
@@ -247,16 +294,40 @@ public class DelegateeTest extends Abstr
             public Iterator<ExternalUser> listUsers() throws ExternalIdentityException {
                 throw new ExternalIdentityException();
             }
-        });
+        }, new DefaultSyncHandler(syncConfig));
 
         dg.syncAllExternalUsers();
     }
 
     @Test
+    public void testListOrphanedUsersFiltersNullSyncIdentity() throws Exception {
+        Iterator<SyncedIdentity> it = Arrays.asList((SyncedIdentity) null).iterator();
+        SyncHandler syncHandler = mock(SyncHandler.class);
+        when(syncHandler.listIdentities(any(UserManager.class))).thenReturn(it);
+
+        Delegatee dg = createDelegatee(new TestIdentityProvider(), syncHandler);
+        assertEquals(0, dg.listOrphanedUsers().length);
+    }
+
+    @Test
+    public void testListOrphanedUsersFiltersForeignSyncIdentity() throws Exception {
+        SyncedIdentity foreign = new DefaultSyncedIdentity(ID_TEST_USER, null, false, -1);
+        SyncedIdentity foreign2 = new DefaultSyncedIdentity(ID_TEST_USER, new ExternalIdentityRef(ID_TEST_USER, null), false, -1);
+        SyncedIdentity foreign3 = new DefaultSyncedIdentity(ID_TEST_USER, new ExternalIdentityRef(ID_TEST_USER, "other"), false, -1);
+        SyncedIdentity emptyIdpName = new DefaultSyncedIdentity(ID_TEST_USER, new ExternalIdentityRef(ID_TEST_USER, ""), false, -1);
+        Iterator<SyncedIdentity> it = Arrays.asList(foreign, foreign2, foreign3, emptyIdpName).iterator();
+        SyncHandler syncHandler = mock(SyncHandler.class);
+        when(syncHandler.listIdentities(any(UserManager.class))).thenReturn(it);
+
+        Delegatee dg = createDelegatee(new TestIdentityProvider(), syncHandler);
+        assertEquals(0, dg.listOrphanedUsers().length);
+    }
+
+    @Test
     public void testPurgeOrphanedSaveError() throws Exception {
         sync(new TestIdentityProvider.TestUser("third", idp.getName()), idp);
         sync(new TestIdentityProvider.TestUser("forth", idp.getName()), idp);
-        sync(idp, TestIdentityProvider.ID_TEST_USER, false);
+        sync(idp, ID_TEST_USER, false);
 
         Root r = preventRootCommit(delegatee);;
 

Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/SyncMBeanImplTest.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/jmx/SyncMBeanImplTest.java?rev=1869208&r1=1869207&r2=1869208&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/SyncMBeanImplTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/SyncMBeanImplTest.java Thu Oct 31 10:00:56 2019
@@ -37,7 +37,6 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncContext;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncException;
-import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncHandler;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncManager;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncResult;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncedIdentity;
@@ -69,30 +68,22 @@ public class SyncMBeanImplTest extends A
     public void before() throws Exception {
         super.before();
 
-        syncMgr = new SyncManager() {
-            @Nullable
-            @Override
-            public SyncHandler getSyncHandler(@NotNull String name) {
-                if (SYNC_NAME.equals(name)) {
-                    return new DefaultSyncHandler(syncConfig);
-                } else if (ThrowingSyncHandler.NAME.equals(name)) {
-                    return new ThrowingSyncHandler(false);
-                } else if (ThrowingSyncHandler.NAME_ALLOWS_IDENTITY_LISTING.equals(name)) {
-                    return new ThrowingSyncHandler(true);
-                } else {
-                    return null;
-                }
+        syncMgr = name -> {
+            if (SYNC_NAME.equals(name)) {
+                return new DefaultSyncHandler(syncConfig);
+            } else if (ThrowingSyncHandler.NAME.equals(name)) {
+                return new ThrowingSyncHandler(false);
+            } else if (ThrowingSyncHandler.NAME_ALLOWS_IDENTITY_LISTING.equals(name)) {
+                return new ThrowingSyncHandler(true);
+            } else {
+                return null;
             }
         };
-        idpMgr = new ExternalIdentityProviderManager() {
-            @Nullable
-            @Override
-            public ExternalIdentityProvider getProvider(@NotNull String name) {
-                if (name.equals(idp.getName())) {
-                    return idp;
-                } else {
-                    return null;
-                }
+        idpMgr = name -> {
+            if (name.equals(idp.getName())) {
+                return idp;
+            } else {
+                return null;
             }
         };
 

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=1869208&r1=1869207&r2=1869208&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 Thu Oct 31 10:00:56 2019
@@ -16,13 +16,8 @@
  */
 package org.apache.jackrabbit.oak.spi.security.authentication.external.impl.principal;
 
-import java.security.Principal;
-import java.util.Set;
-import java.util.UUID;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
-
 import org.apache.jackrabbit.api.security.principal.GroupPrincipal;
 import org.apache.jackrabbit.api.security.user.Group;
 import org.apache.jackrabbit.oak.api.Root;
@@ -39,6 +34,9 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
 import org.jetbrains.annotations.NotNull;
 
+import java.security.Principal;
+import java.util.UUID;
+
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -68,12 +66,12 @@ public abstract class AbstractPrincipalT
 
     @NotNull
     private PrincipalProvider createPrincipalProvider() {
-        Set<String> autoMembership = ImmutableSet.copyOf(Iterables.concat(syncConfig.user().getAutoMembership(),syncConfig.group().getAutoMembership()));
         return new ExternalGroupPrincipalProvider(root, getSecurityProvider().getConfiguration(UserConfiguration.class),
-                NamePathMapper.DEFAULT, ImmutableMap.of(idp.getName(), autoMembership.toArray(new String[0])));
+                NamePathMapper.DEFAULT, ImmutableMap.of(idp.getName(), getAutoMembership()));
     }
 
     @Override
+    @NotNull
     protected DefaultSyncConfig createSyncConfig() {
         DefaultSyncConfig config = super.createSyncConfig();
         DefaultSyncConfig.User u = config.user();
@@ -81,6 +79,10 @@ public abstract class AbstractPrincipalT
         return config;
     }
 
+    String[] getAutoMembership() {
+        return Iterables.toArray(Iterables.concat(syncConfig.user().getAutoMembership(),syncConfig.group().getAutoMembership()), String.class);
+    }
+
     GroupPrincipal getGroupPrincipal() throws Exception {
         ExternalUser externalUser = idp.getUser(USER_ID);
         return getGroupPrincipal(externalUser.getDeclaredGroups().iterator().next());