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/05/17 16:18:30 UTC

svn commit: r1744292 [2/3] - in /jackrabbit/oak/trunk: oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/ oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/...

Copied: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleDynamicMembershipTest.java (from r1742077, jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/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/ExternalLoginModuleDynamicMembershipTest.java?p2=jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleDynamicMembershipTest.java&p1=jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java&r1=1742077&r2=1744292&rev=1744292&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleDynamicMembershipTest.java Tue May 17 16:18:29 2016
@@ -16,123 +16,98 @@
  */
 package org.apache.jackrabbit.oak.spi.security.authentication.external;
 
+import java.util.HashSet;
+import java.util.Set;
+import javax.annotation.Nonnull;
 import javax.jcr.SimpleCredentials;
-import javax.security.auth.login.LoginException;
+import javax.jcr.Value;
 
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.UserManager;
-import org.apache.jackrabbit.oak.api.ContentSession;
-import org.apache.jackrabbit.oak.namepath.NamePathMapper;
-import org.apache.jackrabbit.oak.plugins.value.ValueFactoryImpl;
-import org.junit.After;
-import org.junit.Before;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalIdentityConstants;
 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.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
-/**
- * ExternalLoginModuleTest...
- */
-public class ExternalLoginModuleTest extends ExternalLoginModuleTestBase {
+public class ExternalLoginModuleDynamicMembershipTest extends ExternalLoginModuleTest {
 
-    @Before
+    @Override
     public void before() throws Exception {
         super.before();
+
+        syncConfig.user().setDynamicMembership(true);
     }
 
-    @After
-    public void after() throws Exception {
-        super.after();
+    private void assertExternalPrincipalNames(@Nonnull UserManager userMgr, @Nonnull String id) throws Exception {
+        Authorizable a = userMgr.getAuthorizable(id);
+        assertNotNull(a);
+
+        Set<String> expected = new HashSet();
+        calcExpectedPrincipalNames(idp.getUser(id), syncConfig.user().getMembershipNestingDepth(), expected);
+
+        Set<String> extPrincNames = new HashSet();
+        for (Value v : a.getProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES)) {
+            extPrincNames.add(v.getString());
+        }
+
+        assertEquals(expected, extPrincNames);
     }
 
-    @Test
-    public void testLoginFailed() throws Exception {
-        UserManager userManager = getUserManager(root);
-        try {
-            ContentSession cs = login(new SimpleCredentials("unknown", new char[0]));
-            cs.close();
-            fail("login failure expected");
-        } catch (LoginException e) {
-            // success
-        } finally {
-            assertNull(userManager.getAuthorizable(USER_ID));
+    private void calcExpectedPrincipalNames(@Nonnull ExternalIdentity identity, long depth, @Nonnull Set<String> expected) throws Exception {
+        if (depth <= 0) {
+            return;
+        }
+        for (ExternalIdentityRef ref : identity.getDeclaredGroups()) {
+            ExternalIdentity groupIdentity = idp.getIdentity(ref);
+            expected.add(groupIdentity.getPrincipalName());
+            calcExpectedPrincipalNames(groupIdentity, depth-1, expected);
         }
     }
 
     @Test
-    public void testSyncCreateUser() throws Exception {
-        UserManager userManager = getUserManager(root);
-        ContentSession cs = null;
+    public void testSyncCreatesRepExternalPrincipals() throws Exception {
         try {
-            assertNull(userManager.getAuthorizable(USER_ID));
-
-            cs = login(new SimpleCredentials(USER_ID, new char[0]));
+            login(new SimpleCredentials(USER_ID, new char[0])).close();
 
             root.refresh();
-
-            Authorizable a = userManager.getAuthorizable(USER_ID);
-            assertNotNull(a);
-            ExternalUser user = idp.getUser(USER_ID);
-            for (String prop : user.getProperties().keySet()) {
-                assertTrue(a.hasProperty(prop));
-            }
-            assertEquals(TEST_CONSTANT_PROPERTY_VALUE, a.getProperty(TEST_CONSTANT_PROPERTY_NAME)[0].getString());
+            assertExternalPrincipalNames(getUserManager(root), USER_ID);
         } finally {
-            if (cs != null) {
-                cs.close();
-            }
             options.clear();
         }
     }
 
     @Test
-    public void testSyncCreateUserCaseInsensitive() throws Exception {
-        UserManager userManager = getUserManager(root);
-        ContentSession cs = null;
+    public void testSyncCreatesRepExternalPrincipalsDepthInfinite() throws Exception {
+        syncConfig.user().setMembershipNestingDepth(Long.MAX_VALUE);
         try {
-            assertNull(userManager.getAuthorizable(USER_ID));
-
-            cs = login(new SimpleCredentials(USER_ID.toUpperCase(), new char[0]));
+            login(new SimpleCredentials(USER_ID, new char[0])).close();
 
             root.refresh();
-
-            Authorizable a = userManager.getAuthorizable(USER_ID);
-            assertNotNull(a);
-            ExternalUser user = idp.getUser(USER_ID);
-            for (String prop : user.getProperties().keySet()) {
-                assertTrue(a.hasProperty(prop));
-            }
-            assertEquals(TEST_CONSTANT_PROPERTY_VALUE, a.getProperty(TEST_CONSTANT_PROPERTY_NAME)[0].getString());
+            assertExternalPrincipalNames(getUserManager(root), USER_ID);
         } finally {
-            if (cs != null) {
-                cs.close();
-            }
             options.clear();
         }
     }
 
     @Test
     public void testSyncCreateGroup() throws Exception {
-        UserManager userManager = getUserManager(root);
-        ContentSession cs = null;
         try {
-            cs = login(new SimpleCredentials(USER_ID, new char[0]));
+            login(new SimpleCredentials(USER_ID, new char[0])).close();
 
             root.refresh();
+            UserManager userManager = getUserManager(root);
             for (String id : new String[]{"a", "b", "c"}) {
-                assertNotNull(userManager.getAuthorizable(id));
+                assertNull(userManager.getAuthorizable(id));
             }
             for (String id : new String[]{"aa", "aaa"}) {
                 assertNull(userManager.getAuthorizable(id));
             }
         } finally {
-            if (cs != null) {
-                cs.close();
-            }
             options.clear();
         }
     }
@@ -140,47 +115,72 @@ public class ExternalLoginModuleTest ext
     @Test
     public void testSyncCreateGroupNesting() throws Exception {
         syncConfig.user().setMembershipNestingDepth(2);
-        UserManager userManager = getUserManager(root);
-        ContentSession cs = null;
         try {
-            cs = login(new SimpleCredentials(USER_ID, new char[0]));
+            login(new SimpleCredentials(USER_ID, new char[0])).close();
 
             root.refresh();
             for (String id : new String[]{"a", "b", "c", "aa", "aaa"}) {
-                assertNotNull(userManager.getAuthorizable(id));
+                assertNull(getUserManager(root).getAuthorizable(id));
             }
         } finally {
-            if (cs != null) {
-                cs.close();
-            }
             options.clear();
         }
     }
 
     @Test
-    public void testSyncUpdate() throws Exception {
-        // create user upfront in order to test update mode
-        UserManager userManager = getUserManager(root);
-        ExternalUser externalUser = idp.getUser(USER_ID);
-        Authorizable user = userManager.createUser(externalUser.getId(), null);
-        user.setProperty("rep:externalId", new ValueFactoryImpl(root, NamePathMapper.DEFAULT).createValue(externalUser.getExternalId().getString()));
-        root.commit();
-
-        ContentSession cs = null;
+    public void testSyncUpdateAfterXmlImport() throws Exception {
         try {
-            cs = login(new SimpleCredentials(USER_ID, new char[0]));
+            // force initial sync
+            login(new SimpleCredentials(USER_ID, new char[0])).close();
 
-            root.refresh();
+            // remove properties according to the behavior in the XML-import
+            Root systemRoot = getSystemRoot();
+            UserManager userManager = getUserManager(systemRoot);
 
             Authorizable a = userManager.getAuthorizable(USER_ID);
-            assertNotNull(a);
-            for (String prop : externalUser.getProperties().keySet()) {
-                assertTrue(a.hasProperty(prop));
-            }
+            a.removeProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES);
+            a.removeProperty(ExternalIdentityConstants.REP_LAST_SYNCED);
+            systemRoot.commit();
+
+            // login again to force sync of the user (and it's group membership)
+            login(new SimpleCredentials(USER_ID, new char[0])).close();
+
+            systemRoot.refresh();
+            a = userManager.getAuthorizable(USER_ID);
+            assertTrue(a.hasProperty(ExternalIdentityConstants.REP_LAST_SYNCED));
+            assertExternalPrincipalNames(userManager, USER_ID);
         } finally {
-            if (cs != null) {
-                cs.close();
+            options.clear();
+        }
+    }
+
+    @Test
+    public void testSyncUpdateWithRemovedPrincipalNames() throws Exception {
+        try {
+            // force initial sync
+            login(new SimpleCredentials(USER_ID, new char[0])).close();
+
+            // removing the rep:externalPrincipalNames property only will have the same
+            // effect as the compatibility behavior that respects previously
+            // synchronized users with full membership sync.
+            Root systemRoot = getSystemRoot();
+            UserManager userManager = getUserManager(systemRoot);
+            Authorizable a = userManager.getAuthorizable(USER_ID);
+            a.removeProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES);
+            systemRoot.commit();
+
+            // login again
+            login(new SimpleCredentials(USER_ID, new char[0])).close();
+
+            systemRoot.refresh();
+            a = userManager.getAuthorizable(USER_ID);
+            assertTrue(a.hasProperty(ExternalIdentityConstants.REP_LAST_SYNCED));
+            assertFalse(a.hasProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES));
+
+            for (ExternalIdentityRef ref : idp.getUser(USER_ID).getDeclaredGroups()) {
+                assertNotNull(userManager.getAuthorizable(ref.getId()));
             }
+        } finally {
             options.clear();
         }
     }

Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/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/ExternalLoginModuleTest.java?rev=1744292&r1=1744291&r2=1744292&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java Tue May 17 16:18:29 2016
@@ -22,8 +22,7 @@ import javax.security.auth.login.LoginEx
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.api.ContentSession;
-import org.apache.jackrabbit.oak.namepath.NamePathMapper;
-import org.apache.jackrabbit.oak.plugins.value.ValueFactoryImpl;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncContext;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -163,7 +162,7 @@ public class ExternalLoginModuleTest ext
         UserManager userManager = getUserManager(root);
         ExternalUser externalUser = idp.getUser(USER_ID);
         Authorizable user = userManager.createUser(externalUser.getId(), null);
-        user.setProperty("rep:externalId", new ValueFactoryImpl(root, NamePathMapper.DEFAULT).createValue(externalUser.getExternalId().getString()));
+        user.setProperty(DefaultSyncContext.REP_EXTERNAL_ID, getValueFactory().createValue(externalUser.getExternalId().getString()));
         root.commit();
 
         ContentSession cs = null;

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=1744292&r1=1744291&r2=1744292&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 Tue May 17 16:18:29 2016
@@ -33,6 +33,7 @@ public class TestIdentityProvider implem
 
     public static final String ID_TEST_USER = "testUser";
     public static final String ID_SECOND_USER = "secondUser";
+    public static final String ID_WILDCARD_USER = "wildcardUser";
 
     public static final String ID_EXCEPTION = "throw!";
 
@@ -46,6 +47,8 @@ public class TestIdentityProvider implem
         addGroup(new TestGroup("b", getName()).withGroups("a"));
         addGroup(new TestGroup("c", getName()));
         addGroup(new TestGroup("secondGroup", getName()));
+        addGroup(new TestGroup("_gr_u_", getName()));
+        addGroup(new TestGroup("g%r%", getName()));
 
         addUser(new TestUser(ID_TEST_USER, getName())
                 .withProperty("name", "Test User")
@@ -63,6 +66,9 @@ public class TestIdentityProvider implem
                 .withProperty("charArr", new char[]{'t', 'o', 'b'})
                 .withProperty("byteArr", new byte[0])
                 .withGroups("secondGroup"));
+
+        addUser(new TestUser(ID_WILDCARD_USER, getName())
+                .withGroups("_gr_u_", "g%r%"));
     }
 
     private void addUser(TestIdentity user) {

Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java?rev=1744292&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java Tue May 17 16:18:29 2016
@@ -0,0 +1,42 @@
+/*
+ * 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 javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.security.SecurityProviderImpl;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.principal.ExternalPrincipalConfiguration;
+import org.apache.jackrabbit.oak.spi.security.principal.CompositePrincipalConfiguration;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class TestSecurityProvider extends SecurityProviderImpl {
+    public TestSecurityProvider(@Nonnull ConfigurationParameters configuration) {
+        super(configuration);
+
+        PrincipalConfiguration principalConfiguration = getConfiguration(PrincipalConfiguration.class);
+        if (!(principalConfiguration instanceof CompositePrincipalConfiguration)) {
+            throw new IllegalStateException();
+        } else {
+            PrincipalConfiguration defConfig = checkNotNull(((CompositePrincipalConfiguration) principalConfiguration).getDefaultConfig());
+            bindPrincipalConfiguration((new ExternalPrincipalConfiguration(this)));
+            bindPrincipalConfiguration(defConfig);
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

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=1744292&r1=1744291&r2=1744292&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 Tue May 17 16:18:29 2016
@@ -18,7 +18,6 @@ package org.apache.jackrabbit.oak.spi.se
 
 import java.io.ByteArrayInputStream;
 import java.math.BigDecimal;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
@@ -71,8 +70,6 @@ public class DefaultSyncContextTest exte
     private ValueFactory valueFactory;
     private DefaultSyncContext syncCtx;
 
-    private List<String> authorizableIds = new ArrayList<String>();
-
     @Before
     public void before() throws Exception {
         super.before();
@@ -86,13 +83,6 @@ public class DefaultSyncContextTest exte
         try {
             syncCtx.close();
             root.refresh();
-            for (String id : authorizableIds) {
-                Authorizable a = userManager.getAuthorizable(id);
-                if (a != null) {
-                    a.remove();
-                }
-            }
-            root.commit();
         } finally {
             super.after();
         }
@@ -104,9 +94,7 @@ public class DefaultSyncContextTest exte
     }
 
     private Group createTestGroup() throws Exception {
-        Group gr = userManager.createGroup("group" + UUID.randomUUID());
-        authorizableIds.add(gr.getID());
-        return gr;
+        return userManager.createGroup("group" + UUID.randomUUID());
     }
 
     /**
@@ -380,8 +368,6 @@ public class DefaultSyncContextTest exte
         // mark a regular repo user as external user from the test IDP
         User u = userManager.createUser("test" + UUID.randomUUID(), null);
         String userId = u.getID();
-        authorizableIds.add(userId);
-
         setExternalID(u, idp.getName());
 
         // test sync with 'keepmissing' = true
@@ -484,7 +470,7 @@ public class DefaultSyncContextTest exte
 
     @Test
     public void testSyncByForeignId2() throws Exception {
-        User u = getTestUser();
+        User u = userManager.getAuthorizable(getTestUser().getID(), User.class);
         setExternalID(u, "differentIDP");
 
         SyncResult result = syncCtx.sync(u.getID());
@@ -1138,7 +1124,7 @@ public class DefaultSyncContextTest exte
 
     @Test
     public void testIsSameIDPNull() throws Exception {
-        assertFalse(syncCtx.isSameIDP(null));
+        assertFalse(syncCtx.isSameIDP((Authorizable) null));
     }
 
     @Test
@@ -1186,6 +1172,16 @@ public class DefaultSyncContextTest exte
         assertFalse(syncCtx.isSameIDP(gr));
     }
 
+    @Test
+    public void testIsSameIDPExternalIdentityRef() throws Exception {
+        assertFalse(syncCtx.isSameIDP(new TestIdentityProvider.ForeignExternalUser().getExternalId()));
+        assertFalse(syncCtx.isSameIDP(new TestIdentityProvider.ForeignExternalGroup().getExternalId()));
+
+        assertTrue(syncCtx.isSameIDP(new TestIdentityProvider.TestIdentity().getExternalId()));
+        assertTrue(syncCtx.isSameIDP(idp.listGroups().next().getExternalId()));
+        assertTrue(syncCtx.isSameIDP(idp.listUsers().next().getExternalId()));
+    }
+
     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/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=1744292&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java Tue May 17 16:18:29 2016
@@ -0,0 +1,440 @@
+/*
+ * 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 java.util.HashSet;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+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.UserManager;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+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;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityException;
+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.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.DefaultSyncConfig;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncContext;
+import org.junit.After;
+import org.junit.Before;
+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.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class DynamicSyncContextTest extends AbstractExternalAuthTest {
+
+    private Root r;
+    private UserManager userManager;
+    private ValueFactory valueFactory;
+
+    private DynamicSyncContext syncContext;
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+        r = getSystemRoot();
+        userManager = getUserManager(r);
+        valueFactory = getValueFactory(r);
+        syncContext = new DynamicSyncContext(syncConfig, idp, userManager, valueFactory);
+    }
+
+    @After
+    public void after() throws Exception {
+        try {
+            syncContext.close();
+            r.refresh();
+        } finally {
+            super.after();
+        }
+    }
+
+    @Override
+    protected DefaultSyncConfig createSyncConfig() {
+        DefaultSyncConfig sc = super.createSyncConfig();
+        sc.user().setDynamicMembership(true);
+        return sc;
+    }
+
+    private void sync(@Nonnull ExternalIdentity externalIdentity, @Nonnull SyncResult.Status expectedStatus) throws Exception {
+        SyncResult result = syncContext.sync(externalIdentity);
+        assertSame(expectedStatus, result.getStatus());
+        r.commit();
+    }
+
+    private void assertDynamicMembership(@Nonnull Authorizable a, @Nonnull ExternalIdentity externalIdentity, long depth) throws Exception {
+        Value[] vs = a.getProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES);
+        Iterable<String> pNames = Iterables.transform(ImmutableList.copyOf(vs), new Function<Value, String>() {
+            @Nullable
+            @Override
+            public String apply(Value input) {
+                try {
+                    return input.getString();
+                } catch (RepositoryException e) {
+                    fail(e.getMessage());
+                    return null;
+                }
+            };
+        });
+
+        Set<String> expected = new HashSet<>();
+        collectGroupPrincipals(expected, externalIdentity.getDeclaredGroups(), depth);
+
+        assertEquals(expected, ImmutableSet.copyOf(pNames));
+    }
+
+    private void collectGroupPrincipals(Set<String> pNames, @Nonnull Iterable<ExternalIdentityRef> declaredGroups, long depth) throws ExternalIdentityException {
+        if (depth <= 0) {
+            return;
+        }
+        for (ExternalIdentityRef ref : declaredGroups) {
+            ExternalIdentity ei = idp.getIdentity(ref);
+            pNames.add(ei.getPrincipalName());
+            collectGroupPrincipals(pNames, ei.getDeclaredGroups(), depth - 1);
+        }
+    }
+
+    private static void assertSyncedMembership(@Nonnull UserManager userManager,
+                                               @Nonnull Authorizable a,
+                                               @Nonnull ExternalIdentity externalIdentity) throws Exception {
+        for (ExternalIdentityRef ref : externalIdentity.getDeclaredGroups()) {
+            Group gr = userManager.getAuthorizable(ref.getId(), Group.class);
+            assertNotNull(gr);
+            assertTrue(gr.isMember(a));
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSyncExternalIdentity() throws Exception {
+        syncContext.sync(new TestIdentityProvider.TestIdentity());
+    }
+
+    @Test
+    public void testSyncExternalUser() throws Exception {
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        sync(externalUser, SyncResult.Status.ADD);
+
+        assertNotNull(userManager.getAuthorizable(USER_ID));
+    }
+
+    @Test
+    public void testSyncExternalUserDepth0() throws Exception {
+        syncConfig.user().setMembershipNestingDepth(0);
+
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        sync(externalUser, SyncResult.Status.ADD);
+
+        Tree tree = r.getTree(userManager.getAuthorizable(USER_ID).getPath());
+        PropertyState extPrincipalNames = tree.getProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES);
+        assertNotNull(extPrincipalNames);
+        assertEquals(0, extPrincipalNames.count());
+    }
+
+    @Test
+    public void testSyncExternalUserDepth1() throws Exception {
+        syncConfig.user().setMembershipNestingDepth(1);
+
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        sync(externalUser, SyncResult.Status.ADD);
+
+        Tree tree = r.getTree(userManager.getAuthorizable(USER_ID).getPath());
+        PropertyState extPrincipalNames = tree.getProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES);
+        assertNotNull(extPrincipalNames);
+
+        Set<String> pNames = Sets.newHashSet(extPrincipalNames.getValue(Type.STRINGS));
+        for (ExternalIdentityRef ref : externalUser.getDeclaredGroups()) {
+            assertTrue(pNames.remove(idp.getIdentity(ref).getPrincipalName()));
+        }
+        assertTrue(pNames.isEmpty());
+    }
+
+    @Test
+    public void testSyncExternalUserDepthInfinite() throws Exception {
+        syncConfig.user().setMembershipNestingDepth(Long.MAX_VALUE);
+
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        sync(externalUser, SyncResult.Status.ADD);
+
+        Tree tree = r.getTree(userManager.getAuthorizable(USER_ID).getPath());
+        PropertyState extPrincipalNames = tree.getProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES);
+        assertNotNull(extPrincipalNames);
+
+        Set<String> pNames = Sets.newHashSet(extPrincipalNames.getValue(Type.STRINGS));
+        Set<String> expected = Sets.newHashSet();
+        collectGroupPrincipals(expected, externalUser.getDeclaredGroups(), Long.MAX_VALUE);
+
+        assertEquals(expected, pNames);
+    }
+
+    @Test
+    public void testSyncExternalUserExistingGroups() throws Exception {
+        syncConfig.user().setMembershipNestingDepth(1);
+
+        ExternalUser externalUser = idp.getUser(USER_ID);
+
+        DefaultSyncContext ctx = new DefaultSyncContext(syncConfig, idp, userManager, valueFactory);
+        ctx.sync(externalUser);
+        ctx.close();
+
+        Authorizable a = userManager.getAuthorizable(USER_ID);
+        assertSyncedMembership(userManager, a, externalUser);
+
+        syncContext.setForceUserSync(true);
+        syncConfig.user().setMembershipExpirationTime(-1);
+        syncContext.sync(externalUser);
+
+        Tree t = r.getTree(a.getPath());
+        assertFalse(t.hasProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES));
+
+        assertSyncedMembership(userManager, a, externalUser);
+    }
+
+    @Test
+    public void testSyncExternalGroup() throws Exception {
+        ExternalGroup gr = idp.listGroups().next();
+
+        syncContext.sync(gr);
+        assertNull(userManager.getAuthorizable(gr.getId()));
+        assertFalse(r.hasPendingChanges());
+    }
+
+    @Test
+    public void testSyncExternalGroupVerifyStatus() throws Exception {
+        ExternalGroup gr = idp.listGroups().next();
+
+        SyncResult result = syncContext.sync(gr);
+        assertEquals(SyncResult.Status.NOP, result.getStatus());
+
+        result = syncContext.sync(gr);
+        assertEquals(SyncResult.Status.NOP, result.getStatus());
+
+        syncContext.setForceGroupSync(true);
+        result = syncContext.sync(gr);
+        assertEquals(SyncResult.Status.NOP, result.getStatus());
+    }
+
+    @Test
+    public void testSyncExternalGroupExisting() throws Exception {
+        // create an external external group that already has been synced into the repo
+        ExternalGroup externalGroup = idp.listGroups().next();
+        SyncContext ctx = new DefaultSyncContext(syncConfig, idp, userManager, valueFactory);
+        ctx.sync(externalGroup);
+        ctx.close();
+
+        // synchronizing using DynamicSyncContext must update the existing group
+        syncContext.setForceGroupSync(true);
+        SyncResult result = syncContext.sync(externalGroup);
+        assertSame(SyncResult.Status.UPDATE, result.getStatus());
+    }
+
+    @Test
+    public void testSyncForeignExternalGroup() throws Exception {
+        ExternalGroup foreign = new TestIdentityProvider.ForeignExternalGroup();
+
+        SyncResult res = syncContext.sync(foreign);
+        assertNotNull(res);
+        assertSame(SyncResult.Status.FOREIGN, res.getStatus());
+
+        // expect {@code SyncedIdentity} in accordance with {@code sync(String userId)},
+        // where the authorizable is found to be linked to a different IDP.
+        SyncedIdentity si = res.getIdentity();
+        assertNotNull(si);
+        assertEquals(foreign.getId(), si.getId());
+        ExternalIdentityRef ref = si.getExternalIdRef();
+        assertNotNull(ref);
+        assertEquals(foreign.getExternalId(), ref);
+        assertTrue(si.isGroup());
+        assertEquals(-1, si.lastSynced());
+
+        assertFalse(r.hasPendingChanges());
+    }
+
+    @Test
+    public void testSyncUserByIdUpdate() throws Exception {
+        ExternalIdentity externalId = idp.listUsers().next();
+
+        Authorizable a = userManager.createUser(externalId.getId(), null);
+        a.setProperty(DefaultSyncContext.REP_EXTERNAL_ID, valueFactory.createValue(externalId.getExternalId().getString()));
+
+        syncContext.setForceUserSync(true);
+        SyncResult result = syncContext.sync(externalId.getId());
+        assertEquals(SyncResult.Status.UPDATE, result.getStatus());
+
+        Tree t = r.getTree(a.getPath());
+        assertTrue(t.hasProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES));
+    }
+
+    @Test
+    public void testSyncUserIdExistingGroups() throws Exception {
+        ExternalUser externalUser = idp.getUser(USER_ID);
+
+        DefaultSyncContext ctx = new DefaultSyncContext(syncConfig, idp, userManager, valueFactory);
+        ctx.sync(externalUser);
+        ctx.close();
+
+        Authorizable user = userManager.getAuthorizable(externalUser.getId());
+        for (ExternalIdentityRef ref : externalUser.getDeclaredGroups()) {
+            Group gr = userManager.getAuthorizable(ref.getId(), Group.class);
+            assertTrue(gr.isMember(user));
+        }
+
+        syncContext.setForceUserSync(true);
+        syncContext.sync(externalUser.getId());
+
+        Authorizable a = userManager.getAuthorizable(USER_ID);
+        Tree t = r.getTree(a.getPath());
+        assertFalse(t.hasProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES));
+        assertSyncedMembership(userManager, a, externalUser);
+    }
+
+    @Test
+    public void testSyncMembershipWithNesting() throws Exception {
+        long nesting = 1;
+        syncConfig.user().setMembershipNestingDepth(nesting);
+
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        sync(externalUser, SyncResult.Status.ADD);
+
+        Authorizable a = userManager.getAuthorizable(externalUser.getId());
+        assertDynamicMembership(a, externalUser, nesting);
+
+        // verify that the membership is always reflected in the rep:externalPrincipalNames property
+        // 1. membership nesting  = -1
+        nesting = -1;
+        syncContext.syncMembership(externalUser, a, nesting);
+        assertDynamicMembership(a, externalUser, nesting);
+
+        // 2. membership nesting is > 0
+        nesting = Long.MAX_VALUE;
+        syncContext.syncMembership(externalUser, a, nesting);
+        assertDynamicMembership(a, externalUser, nesting);
+    }
+
+    @Test
+    public void testSyncMembershipWithChangedGroups() throws Exception {
+        long nesting = 1;
+        syncConfig.user().setMembershipNestingDepth(nesting);
+
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        sync(externalUser, SyncResult.Status.ADD);
+
+        Authorizable a = userManager.getAuthorizable(externalUser.getId());
+        assertDynamicMembership(a, externalUser, nesting);
+
+        // sync user with modified membership => must be reflected
+        // 1. empty set of declared groups
+        ExternalUser mod = new TestUserWithGroupRefs(externalUser, ImmutableSet.<ExternalIdentityRef>of());
+        syncContext.syncMembership(mod, a, nesting);
+        assertDynamicMembership(a, mod, nesting);
+
+        // 2. set with different groups that defined on IDP
+        mod = new TestUserWithGroupRefs(externalUser, ImmutableSet.<ExternalIdentityRef>of(
+                idp.getGroup("a").getExternalId(),
+                idp.getGroup("aa").getExternalId(),
+                idp.getGroup("secondGroup").getExternalId()));
+        syncContext.syncMembership(mod, a, nesting);
+        assertDynamicMembership(a, mod, nesting);
+    }
+
+    @Test
+    public void testSyncMembershipWithChangedExistingGroups() throws Exception {
+        long nesting = 1;
+        syncConfig.user().setMembershipNestingDepth(nesting);
+
+        ExternalUser externalUser = idp.getUser(USER_ID);
+
+        DefaultSyncContext ctx = new DefaultSyncContext(syncConfig, idp, userManager, valueFactory);
+        ctx.sync(externalUser);
+        ctx.close();
+
+        Authorizable a = userManager.getAuthorizable(externalUser.getId());
+        assertSyncedMembership(userManager, a, externalUser);
+
+        // sync user with modified membership => must be reflected
+        // 1. empty set of declared groups
+        ExternalUser mod = new TestUserWithGroupRefs(externalUser, ImmutableSet.<ExternalIdentityRef>of());
+        syncContext.syncMembership(mod, a, nesting);
+        assertSyncedMembership(userManager, a, mod);
+
+        // 2. set with different groups that defined on IDP
+        mod = new TestUserWithGroupRefs(externalUser, ImmutableSet.<ExternalIdentityRef>of(
+                        idp.getGroup("a").getExternalId(),
+                        idp.getGroup("aa").getExternalId(),
+                        idp.getGroup("secondGroup").getExternalId()));
+        syncContext.syncMembership(mod, a, nesting);
+        assertSyncedMembership(userManager, a, mod);
+    }
+
+    @Test
+    public void testSyncMembershipForExternalGroup() throws Exception {
+        ExternalGroup externalGroup = idp.getGroup("a"); // a group that has declaredGroups
+        SyncContext ctx = new DefaultSyncContext(syncConfig, idp, userManager, valueFactory);
+        ctx.sync(externalGroup);
+        ctx.close();
+        r.commit();
+
+        Authorizable gr = userManager.getAuthorizable(externalGroup.getId());
+        syncContext.syncMembership(externalGroup, gr, 1);
+
+        assertFalse(gr.hasProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES));
+        assertFalse(r.hasPendingChanges());
+    }
+
+    private static final class TestUserWithGroupRefs extends TestIdentityProvider.TestIdentity implements ExternalUser {
+
+        private Iterable<ExternalIdentityRef> declaredGroupRefs;
+
+        private TestUserWithGroupRefs(@Nonnull ExternalUser base, @Nonnull Iterable<ExternalIdentityRef> declaredGroupRefs) {
+            super(base);
+            this.declaredGroupRefs = declaredGroupRefs;
+        }
+
+        public String getPassword() {
+            return "";
+        }
+
+        @Nonnull
+        @Override
+        public Iterable<ExternalIdentityRef> getDeclaredGroups() {
+            return declaredGroupRefs;
+        }
+    }
+}
\ 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/DynamicSyncContextTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 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=1744292&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AbstractPrincipalTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AbstractPrincipalTest.java Tue May 17 16:18:29 2016
@@ -0,0 +1,97 @@
+/*
+ * 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.UUID;
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.AbstractExternalAuthTest;
+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.TestIdentityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncConfig;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncContext;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DynamicSyncContext;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
+import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public abstract class AbstractPrincipalTest extends AbstractExternalAuthTest {
+
+    PrincipalProvider principalProvider;
+
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        // sync external users into the system using the 2 different sync-context implementations
+        Root systemRoot = getSystemRoot();
+        SyncContext syncContext = new DynamicSyncContext(syncConfig, idp, getUserManager(systemRoot), getValueFactory(systemRoot));
+        syncContext.sync(idp.getUser(USER_ID));
+        syncContext.close();
+
+        syncContext = new DefaultSyncContext(syncConfig, idp, getUserManager(systemRoot), getValueFactory(systemRoot));
+        syncContext.sync(idp.getUser(TestIdentityProvider.ID_SECOND_USER));
+        syncContext.close();
+
+        systemRoot.commit();
+
+        root.refresh();
+        principalProvider = createPrincipalProvider();
+    }
+
+    @Nonnull
+    PrincipalProvider createPrincipalProvider() {
+        return new ExternalGroupPrincipalProvider(root, getSecurityProvider().getConfiguration(UserConfiguration.class), NamePathMapper.DEFAULT);
+    }
+
+    @Override
+    protected DefaultSyncConfig createSyncConfig() {
+        DefaultSyncConfig config = super.createSyncConfig();
+        DefaultSyncConfig.User u = config.user();
+        u.setDynamicMembership(true);
+        return config;
+    }
+
+    java.security.acl.Group getGroupPrincipal() throws Exception {
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        return getGroupPrincipal(externalUser.getDeclaredGroups().iterator().next());
+    }
+
+    java.security.acl.Group getGroupPrincipal(@Nonnull ExternalIdentityRef ref) throws Exception {
+        String principalName = idp.getIdentity(ref).getPrincipalName();
+        Principal p = principalProvider.getPrincipal(principalName);
+
+        assertNotNull(p);
+        assertTrue(p instanceof java.security.acl.Group);
+
+        return (java.security.acl.Group) p;
+    }
+
+    Group createTestGroup() throws Exception {
+        Group gr = getUserManager(root).createGroup("group" + UUID.randomUUID());
+        root.commit();
+        return gr;
+    }
+}
\ 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/AbstractPrincipalTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/EmptyPrincipalProviderTest.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/EmptyPrincipalProviderTest.java?rev=1744292&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/EmptyPrincipalProviderTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/EmptyPrincipalProviderTest.java Tue May 17 16:18:29 2016
@@ -0,0 +1,83 @@
+/*
+ * 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 javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalIdentityConstants;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class EmptyPrincipalProviderTest extends AbstractPrincipalTest {
+
+    private String externalPrincipalName;
+    private Principal testPrincipal;
+
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        assertFalse(principalProvider instanceof ExternalGroupPrincipalProvider);
+
+        externalPrincipalName = getUserManager(root).getAuthorizable(USER_ID).getProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES)[0].getString();
+        testPrincipal = getTestUser().getPrincipal();
+    }
+
+    @Nonnull
+    @Override
+    PrincipalProvider createPrincipalProvider() {
+        return new ExternalPrincipalConfiguration().getPrincipalProvider(root, NamePathMapper.DEFAULT);
+    }
+
+    @Test
+    public void testGetPrincipal() {
+        assertNull(principalProvider.getPrincipal(externalPrincipalName));
+        assertNull(principalProvider.getPrincipal(testPrincipal.getName()));
+    }
+
+    @Test
+    public void testGetGroupMembership() {
+        assertTrue(principalProvider.getGroupMembership(new PrincipalImpl(externalPrincipalName)).isEmpty());
+        assertTrue(principalProvider.getGroupMembership(testPrincipal).isEmpty());
+    }
+
+    @Test
+    public void testGetPrincipals() throws Exception {
+        assertTrue(principalProvider.getPrincipals(USER_ID).isEmpty());
+        assertTrue(principalProvider.getPrincipals(getTestUser().getID()).isEmpty());
+    }
+
+    @Test
+    public void testFindPrincipalsByHint() {
+        assertFalse(principalProvider.findPrincipals("a", PrincipalManager.SEARCH_TYPE_ALL).hasNext());
+    }
+
+    @Test
+    public void testFindPrincipalsByType() {
+        assertFalse(principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_ALL).hasNext());
+        assertFalse(principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_NOT_GROUP).hasNext());
+        assertFalse(principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_GROUP).hasNext());
+    }
+}
\ 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/EmptyPrincipalProviderTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 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=1744292&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalProviderTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalProviderTest.java Tue May 17 16:18:29 2016
@@ -0,0 +1,440 @@
+/*
+ * 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.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+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.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.api.Root;
+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.ExternalIdentityRef;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.TestIdentityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncConfig;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DynamicSyncContext;
+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.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class ExternalGroupPrincipalProviderTest extends AbstractPrincipalTest {
+
+    private void syncWithMembership(@Nonnull ExternalUser externalUser, long depth) throws Exception {
+        DefaultSyncConfig sc = new DefaultSyncConfig();
+        sc.user().setMembershipNestingDepth(depth);
+
+        Root systemRoot = getSystemRoot();
+        DynamicSyncContext syncContext = new DynamicSyncContext(sc, idp, getUserManager(systemRoot), getValueFactory(systemRoot));
+        syncContext.setForceUserSync(true);
+        syncContext.setForceGroupSync(true);
+        syncContext.sync(externalUser);
+        syncContext.close();
+        systemRoot.commit();
+
+        root.refresh();
+    }
+
+    private Set<Principal> getDeclaredGroupPrincipals(@Nonnull String userId) throws ExternalIdentityException {
+        Set<Principal> principals = ImmutableSet.copyOf(Iterables.transform(idp.getUser(userId).getDeclaredGroups(), new Function<ExternalIdentityRef, Principal>() {
+            @Nullable
+            @Override
+            public Principal apply(ExternalIdentityRef input) {
+                try {
+                    return new PrincipalImpl(idp.getIdentity(input).getPrincipalName());
+                } catch (ExternalIdentityException e) {
+                    fail(e.getMessage());
+                    return null;
+                }
+            }
+
+            ;
+        }));
+        return principals;
+    }
+
+    @Test
+    public void testGetPrincipalLocalUser() throws Exception {
+        assertNull(principalProvider.getPrincipal(getTestUser().getPrincipal().getName()));
+    }
+
+    @Test
+    public void testGetPrincipalLocalGroup() throws Exception {
+        Group gr = createTestGroup();
+        assertNull(principalProvider.getPrincipal(gr.getPrincipal().getName()));
+    }
+
+    @Test
+    public void testGetPrincipalExternalUser() throws Exception {
+        UserManager userManager = getUserManager(root);
+
+        // synced by principal-sync-ctx
+        User syncedUser = userManager.getAuthorizable(USER_ID, User.class);
+        assertNull(principalProvider.getPrincipal(syncedUser.getPrincipal().getName()));
+
+        // synced by default-sync-ctx
+        syncedUser = userManager.getAuthorizable(TestIdentityProvider.ID_SECOND_USER, User.class);
+        assertNull(principalProvider.getPrincipal(syncedUser.getPrincipal().getName()));
+    }
+
+
+    @Test
+    public void testGetPrincipalExternalGroup() throws Exception {
+        Group gr = getUserManager(root).getAuthorizable("secondGroup", Group.class);
+        assertNotNull(gr);
+
+        assertNull(principalProvider.getPrincipal(gr.getPrincipal().getName()));
+    }
+
+    @Test
+    public void testGetPrincipalDynamicGroupDepth1() throws Exception {
+        for (ExternalIdentityRef ref : idp.getUser(USER_ID).getDeclaredGroups()) {
+
+            String princName = idp.getIdentity(ref).getPrincipalName();
+            Principal principal = principalProvider.getPrincipal(princName);
+
+            assertNotNull(principal);
+            assertTrue(principal instanceof java.security.acl.Group);
+        }
+    }
+
+    @Test
+    public void testGetPrincipalInheritedGroupsDepth1() throws Exception {
+        ImmutableSet<ExternalIdentityRef> declared = ImmutableSet.<ExternalIdentityRef>copyOf(idp.getUser(USER_ID).getDeclaredGroups());
+
+        for (ExternalIdentityRef ref : declared) {
+            for (ExternalIdentityRef inheritedGroupRef : idp.getIdentity(ref).getDeclaredGroups()) {
+                if (declared.contains(inheritedGroupRef)) {
+                    continue;
+                }
+                String inheritedPrincName = idp.getIdentity(inheritedGroupRef).getPrincipalName();
+                assertNull(principalProvider.getPrincipal(inheritedPrincName));
+            }
+        }
+    }
+
+    @Test
+    public void testGetPrincipalInheritedGroupsDepthInfinite() throws Exception {
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        syncWithMembership(externalUser, Long.MAX_VALUE);
+
+        for (ExternalIdentityRef ref : externalUser.getDeclaredGroups()) {
+            ExternalIdentity externalGroup = idp.getIdentity(ref);
+            Principal grPrincipal = principalProvider.getPrincipal(externalGroup.getPrincipalName());
+
+            for (ExternalIdentityRef inheritedGroupRef : externalGroup.getDeclaredGroups()) {
+                String inheritedPrincName = idp.getIdentity(inheritedGroupRef).getPrincipalName();
+
+                Principal principal = principalProvider.getPrincipal(inheritedPrincName);
+
+                assertNotNull(principal);
+                assertTrue(principal instanceof java.security.acl.Group);
+
+                java.security.acl.Group inheritedGrPrincipal = (java.security.acl.Group) principal;
+                assertTrue(inheritedGrPrincipal.isMember(new PrincipalImpl(externalUser.getPrincipalName())));
+                assertFalse(inheritedGrPrincipal.isMember(grPrincipal));
+            }
+        }
+    }
+
+    @Test
+    public void testGetPrincipalUnderscoreSign() throws Exception {
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        syncWithMembership(externalUser, 1);
+
+        for (ExternalIdentityRef ref : externalUser.getDeclaredGroups()) {
+            String pName = idp.getIdentity(ref).getPrincipalName();
+
+            for (String n : new String[]{"_", "_" + pName.substring(1), pName.substring(0, pName.length() - 1) + "_"}) {
+                assertNull(principalProvider.getPrincipal(n));
+            }
+        }
+    }
+
+    @Test
+    public void testGetPrincipalPercentSign() throws Exception {
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        syncWithMembership(externalUser, 1);
+
+        for (ExternalIdentityRef ref : externalUser.getDeclaredGroups()) {
+            String pName = idp.getIdentity(ref).getPrincipalName();
+
+            for (String n : new String[] {"%", "%" + pName, pName + "%", pName.charAt(0) + "%"}) {
+                assertNull(principalProvider.getPrincipal(n));
+            }
+        }
+    }
+
+    @Test
+    public void testGetPrincipalGroupsWithQueryWildCard() throws Exception {
+        ExternalUser externalUser = idp.getUser(TestIdentityProvider.ID_WILDCARD_USER);
+        syncWithMembership(externalUser, 1);
+
+        for (ExternalIdentityRef ref : externalUser.getDeclaredGroups()) {
+            String pName = idp.getIdentity(ref).getPrincipalName();
+            Principal p = principalProvider.getPrincipal(pName);
+            assertNotNull(p);
+            assertEquals(pName, p.getName());
+        }
+    }
+
+    @Test
+    public void testGetGroupMembershipLocalPrincipal() throws Exception {
+        Set<? extends Principal> principals = principalProvider.getGroupMembership(getTestUser().getPrincipal());
+        assertTrue(principals.isEmpty());
+    }
+
+    @Test
+    public void testGetGroupMembershipLocalGroupPrincipal() throws Exception {
+        Group gr = createTestGroup();
+        Set<? extends Principal> principals = principalProvider.getGroupMembership(gr.getPrincipal());
+        assertTrue(principals.isEmpty());
+
+        // same if the principal is not marked as 'java.security.acl.Group' and not tree-based-principal
+        principals = principalProvider.getGroupMembership(new PrincipalImpl(gr.getPrincipal().getName()));
+        assertTrue(principals.isEmpty());
+    }
+
+    @Test
+    public void testGetGroupMembershipExternalUser() throws Exception {
+        Authorizable user = getUserManager(root).getAuthorizable(USER_ID);
+        assertNotNull(user);
+
+        Set<Principal> expected = getDeclaredGroupPrincipals(USER_ID);
+
+        Set<? extends Principal> principals = principalProvider.getGroupMembership(user.getPrincipal());
+        assertEquals(expected, principals);
+
+        // same if the principal is not a tree-based-principal
+        principals = principalProvider.getGroupMembership(new PrincipalImpl(user.getPrincipal().getName()));
+        assertEquals(expected, principals);
+    }
+
+    @Test
+    public void testGetGroupMembershipExternalUser2() throws Exception {
+        // synchronized by default sync-context => no 'dynamic' group principals
+        Authorizable user = getUserManager(root).getAuthorizable(TestIdentityProvider.ID_SECOND_USER);
+        assertNotNull(user);
+
+        Set<? extends Principal> principals = principalProvider.getGroupMembership(user.getPrincipal());
+        assertTrue(principals.isEmpty());
+
+        // same if the principal is not a tree-based-principal
+        principals = principalProvider.getGroupMembership(new PrincipalImpl(user.getPrincipal().getName()));
+        assertTrue(principals.isEmpty());
+    }
+
+    @Test
+    public void testGetGroupMembershipExternalUserInfiniteDepth() throws Exception {
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        syncWithMembership(externalUser, Long.MAX_VALUE);
+
+        Set<Principal> expectedGrPrincipals = new HashSet();
+        collectExpectedPrincipals(expectedGrPrincipals, externalUser.getDeclaredGroups(), Long.MAX_VALUE);
+
+        Authorizable user = getUserManager(root).getAuthorizable(USER_ID);
+        Set<? extends Principal> principals = principalProvider.getGroupMembership(user.getPrincipal());
+        assertEquals(expectedGrPrincipals, principals);
+
+        // same if the principal is not a tree-based-principal
+        principals = principalProvider.getGroupMembership(new PrincipalImpl(user.getPrincipal().getName()));
+        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");
+        assertNotNull(group);
+
+        Set<? extends Principal> principals = principalProvider.getGroupMembership(group.getPrincipal());
+        assertTrue(principals.isEmpty());
+
+        // same if the principal is not marked as 'java.security.acl.Group' and not tree-based-principal
+        principals = principalProvider.getGroupMembership(new PrincipalImpl(group.getPrincipal().getName()));
+        assertTrue(principals.isEmpty());
+    }
+
+    @Test
+    public void testGetPrincipalsLocalUser() throws Exception {
+        Set<? extends Principal> principals = principalProvider.getPrincipals(getTestUser().getID());
+        assertTrue(principals.isEmpty());
+    }
+
+    @Test
+    public void testGetPrincipalsLocalGroup() throws Exception {
+        Set<? extends Principal> principals = principalProvider.getPrincipals(createTestGroup().getID());
+        assertTrue(principals.isEmpty());
+    }
+
+    @Test
+    public void testGetPrincipalsExternalUser() throws Exception {
+        Set<? extends Principal> principals = principalProvider.getPrincipals(USER_ID);
+        assertEquals(getDeclaredGroupPrincipals(USER_ID), principals);
+    }
+
+    @Test
+    public void testGetPrincipalsExternalUser2() {
+        // synchronized by default sync-context => no 'dynamic' group principals
+        Set<? extends Principal> principals = principalProvider.getPrincipals(TestIdentityProvider.ID_SECOND_USER);
+        assertTrue(principals.isEmpty());
+    }
+
+    @Test
+    public void testGetPrincipalsExternalGroup() throws Exception {
+        Authorizable authorizable = getUserManager(root).getAuthorizable("secondGroup");
+        assertNotNull(authorizable);
+
+        Set<? extends Principal> principals = principalProvider.getPrincipals(authorizable.getID());
+        assertTrue(principals.isEmpty());
+    }
+
+    @Test
+    public void testGetPrincipalsNonExistingUser() throws Exception {
+        assertNull(getUserManager(root).getAuthorizable("nonExistingUser"));
+        Set<? extends Principal> principals = principalProvider.getPrincipals("nonExistingUser");
+        assertTrue(principals.isEmpty());
+    }
+
+    @Test
+    public void testFindPrincipalsByHintTypeNotGroup() {
+        Iterator<? extends Principal> iter = principalProvider.findPrincipals("a", PrincipalManager.SEARCH_TYPE_NOT_GROUP);
+        assertSame(Iterators.emptyIterator(), iter);
+    }
+
+    @Test
+    public void testFindPrincipalsByHintTypeGroup() throws Exception {
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        syncWithMembership(externalUser, Long.MAX_VALUE);
+
+        Set<? extends Principal> expected = ImmutableSet.of(new PrincipalImpl("a"), new PrincipalImpl("aa"), new PrincipalImpl("aaa"));
+        Set<? extends Principal> res = ImmutableSet.copyOf(principalProvider.findPrincipals("a", PrincipalManager.SEARCH_TYPE_GROUP));
+
+        assertEquals(expected, res);
+    }
+
+    @Test
+    public void testFindPrincipalsByHintTypeAll() throws Exception {
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        syncWithMembership(externalUser, Long.MAX_VALUE);
+
+        Set<? extends Principal> expected = ImmutableSet.of(
+                new PrincipalImpl("a"),
+                new PrincipalImpl("aa"),
+                new PrincipalImpl("aaa"));
+        Set<? extends Principal> res = ImmutableSet.copyOf(principalProvider.findPrincipals("a", PrincipalManager.SEARCH_TYPE_ALL));
+
+        assertEquals(expected, res);
+    }
+
+    @Test
+    public void testFindPrincipalsContainingUnderscore() throws Exception {
+        ExternalUser externalUser = idp.getUser(TestIdentityProvider.ID_WILDCARD_USER);
+        syncWithMembership(externalUser, 1);
+
+        Set<? extends Principal> expected = ImmutableSet.of(
+                new PrincipalImpl("_gr_u_"));
+        Set<? extends Principal> res = ImmutableSet.copyOf(principalProvider.findPrincipals("_", PrincipalManager.SEARCH_TYPE_ALL));
+
+        assertEquals(expected, res);
+    }
+
+    @Test
+    public void testFindPrincipalsContainingPercentSign() throws Exception {
+        ExternalUser externalUser = idp.getUser(TestIdentityProvider.ID_WILDCARD_USER);
+        syncWithMembership(externalUser, 1);
+
+        Set<? extends Principal> expected = ImmutableSet.of(
+                new PrincipalImpl("g%r%"));
+        Set<? extends Principal> res = ImmutableSet.copyOf(principalProvider.findPrincipals("%", PrincipalManager.SEARCH_TYPE_ALL));
+
+        assertEquals(expected, res);
+    }
+
+    @Test
+    public void testFindPrincipalsByTypeNotGroup() {
+        Iterator<? extends Principal> iter = principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_NOT_GROUP);
+        assertSame(Iterators.emptyIterator(), iter);
+    }
+
+    @Test
+    public void testFindPrincipalsByTypeGroup() throws Exception {
+        Set<? extends Principal> res = ImmutableSet.copyOf(principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_GROUP));
+        assertEquals(getDeclaredGroupPrincipals(USER_ID), res);
+    }
+
+    @Test
+    public void testFindPrincipalsByTypeAll() throws Exception {
+        Set<? extends Principal> res = ImmutableSet.copyOf(principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_ALL));
+        assertEquals(getDeclaredGroupPrincipals(USER_ID), res);
+    }
+
+    @Test
+    public void testFindPrincipalsFiltersDuplicates() throws Exception {
+        ExternalUser otherUser = new TestUser("anotherUser", ImmutableSet.of(idp.getGroup("a").getExternalId()));
+        syncWithMembership(otherUser, 1);
+
+        Iterator<? extends Principal> res = principalProvider.findPrincipals("a", PrincipalManager.SEARCH_TYPE_ALL);
+        assertTrue(res.hasNext());
+        assertEquals(new PrincipalImpl("a"), res.next());
+        assertFalse(res.hasNext());
+    }
+
+    private static final class TestUser extends TestIdentityProvider.TestIdentity implements ExternalUser {
+
+        private final Iterable<ExternalIdentityRef> declaredGroups;
+
+        private TestUser(@Nonnull String id, @Nonnull Iterable<ExternalIdentityRef> declaredGroups) {
+            super(id);
+            this.declaredGroups = declaredGroups;
+        }
+        @Nonnull
+        @Override
+        public Iterable<ExternalIdentityRef> getDeclaredGroups() {
+            return declaredGroups;
+        }
+    }
+}
\ 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/ExternalGroupPrincipalProviderTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalTest.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/ExternalGroupPrincipalTest.java?rev=1744292&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalGroupPrincipalTest.java Tue May 17 16:18:29 2016
@@ -0,0 +1,121 @@
+/*
+ * 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.Enumeration;
+import javax.annotation.Nullable;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalGroup;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
+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.assertTrue;
+
+public class ExternalGroupPrincipalTest extends AbstractPrincipalTest {
+
+    @Test
+    public void testIsMember() throws Exception {
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        java.security.acl.Group principal = getGroupPrincipal(externalUser.getDeclaredGroups().iterator().next());
+
+        assertTrue(principal.isMember(new PrincipalImpl(externalUser.getPrincipalName())));
+        assertTrue(principal.isMember(getUserManager(root).getAuthorizable(USER_ID).getPrincipal()));
+    }
+
+    @Test
+    public void testIsMemberExternalGroup() throws Exception {
+        java.security.acl.Group principal = getGroupPrincipal();
+
+        Iterable<String> exGroupPrincNames = Iterables.transform(ImmutableList.copyOf(idp.listGroups()), new Function<ExternalGroup, String>() {
+            @Nullable
+            @Override
+            public String apply(ExternalGroup input) {
+                return input.getPrincipalName();
+            }
+        });
+
+        for (String principalName : exGroupPrincNames) {
+            assertFalse(principal.isMember(new PrincipalImpl(principalName)));
+        }
+    }
+
+    @Test
+    public void testIsMemberLocalUser() throws Exception {
+        java.security.acl.Group principal = getGroupPrincipal();
+
+        assertFalse(principal.isMember(getTestUser().getPrincipal()));
+        assertFalse(principal.isMember(new PrincipalImpl(getTestUser().getPrincipal().getName())));
+    }
+
+    @Test
+    public void testIsMemberLocalGroup() throws Exception {
+        Group gr = createTestGroup();
+        java.security.acl.Group principal = getGroupPrincipal();
+
+        assertFalse(principal.isMember(gr.getPrincipal()));
+        assertFalse(principal.isMember(new PrincipalImpl(gr.getPrincipal().getName())));
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testAddMember() throws Exception {
+        java.security.acl.Group principal = getGroupPrincipal();
+        principal.addMember(getTestUser().getPrincipal());
+    }
+
+    @Test
+    public void testAddMemberExistingMember() throws Exception {
+        java.security.acl.Group principal = getGroupPrincipal();
+        assertFalse(principal.addMember(getUserManager(root).getAuthorizable(USER_ID).getPrincipal()));
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemoveMember() throws Exception {
+        java.security.acl.Group principal = getGroupPrincipal();
+        principal.removeMember(getUserManager(root).getAuthorizable(USER_ID).getPrincipal());
+    }
+
+    @Test
+    public void testRemoveMemberNotMember() throws Exception {
+        java.security.acl.Group principal = getGroupPrincipal();
+        assertFalse(principal.removeMember(getTestUser().getPrincipal()));
+    }
+
+    @Test
+    public void testMembers() throws Exception {
+        java.security.acl.Group principal = getGroupPrincipal();
+
+        Principal[] expectedMembers = new Principal[] {
+                getUserManager(root).getAuthorizable(USER_ID).getPrincipal(),
+                new PrincipalImpl(idp.getUser(USER_ID).getPrincipalName())
+        };
+
+        for (Principal expected : expectedMembers) {
+            Enumeration<? extends Principal> members = principal.members();
+            assertTrue(members.hasMoreElements());
+            assertEquals(expected, members.nextElement());
+            assertFalse(members.hasMoreElements());
+        }
+    }
+}
\ 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/ExternalGroupPrincipalTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalIdentityImporterTest.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/ExternalIdentityImporterTest.java?rev=1744292&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalIdentityImporterTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalIdentityImporterTest.java Tue May 17 16:18:29 2016
@@ -0,0 +1,147 @@
+/*
+ * 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.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.PrivilegedExceptionAction;
+import javax.annotation.Nonnull;
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.security.auth.Subject;
+
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.oak.jcr.Jcr;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.SystemSubject;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.TestSecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalIdentityConstants;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test XML import of external users/groups with protection of external identity
+ * properties turned on.
+ */
+public class ExternalIdentityImporterTest {
+
+    public static final String XML_EXTERNAL_USER_WITH_PRINCIPAL_NAMES = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+            "<sv:node sv:name=\"t\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+            "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:User</sv:value></sv:property>" +
+            "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>e358efa4-89f5-3062-b10d-d7316b65649e</sv:value></sv:property>" +
+            "   <sv:property sv:name=\"rep:authorizableId\" sv:type=\"String\"><sv:value>t</sv:value></sv:property>" +
+            "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>tPrinc</sv:value></sv:property>" +
+            "   <sv:property sv:name=\"rep:externalId\" sv:type=\"String\"><sv:value>idp;ext-t</sv:value></sv:property>" +
+            "   <sv:property sv:name=\"rep:externalPrincipalNames\" sv:type=\"String\"><sv:value>grPrinc</sv:value><sv:value>gr2Princ</sv:value></sv:property>" +
+            "   <sv:property sv:name=\"rep:lastSynced\" sv:type=\"Date\"><sv:value>2016-05-03T10:03:08.061+02:00</sv:value></sv:property>" +
+            "</sv:node>";
+
+    private Repository repo;
+    private SecurityProvider securityProvider;
+    private JackrabbitSession session;
+
+    @Before
+    public void before() throws Exception {
+        securityProvider = new TestSecurityProvider(getConfigurationParameters());
+        Jcr jcr = new Jcr();
+        jcr.with(securityProvider);
+        repo = jcr.createRepository();
+    }
+
+    @After
+    public void after() throws Exception {
+        try {
+            if (session != null) {
+                session.logout();
+            }
+        } finally {
+            if (repo instanceof JackrabbitRepository) {
+                ((JackrabbitRepository) repo).shutdown();
+            }
+        }
+    }
+
+    @Nonnull
+    ConfigurationParameters getConfigurationParameters() {
+        return ConfigurationParameters.EMPTY;
+    }
+
+    JackrabbitSession createSession(boolean isSystem) throws Exception {
+        if (isSystem) {
+            session = (JackrabbitSession) Subject.doAs(SystemSubject.INSTANCE, new PrivilegedExceptionAction<Session>() {
+                @Override
+                public Session run() throws RepositoryException {
+                    return repo.login(null, null);
+                }
+            });
+        } else {
+            session = (JackrabbitSession) repo.login(new SimpleCredentials(UserConstants.DEFAULT_ADMIN_ID, UserConstants.DEFAULT_ADMIN_ID.toCharArray()));
+        }
+        return session;
+    }
+
+    Node doImport(Session importSession, String parentPath, String xml) throws Exception {
+        InputStream in;
+        if (xml.charAt(0) == '<') {
+            in = new ByteArrayInputStream(xml.getBytes());
+        } else {
+            in = getClass().getResourceAsStream(xml);
+        }
+        try {
+            importSession.importXML(parentPath, in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+            return importSession.getNode(parentPath);
+        } finally {
+            in.close();
+        }
+    }
+
+    static void assertHasProperties(@Nonnull Node node, @Nonnull String... propertyNames) throws Exception {
+        for (String pN : propertyNames) {
+            assertTrue(node.hasProperty(pN));
+        }
+    }
+
+    static void assertNotHasProperties(@Nonnull Node node, @Nonnull String... propertyNames) throws Exception {
+        for (String pN : propertyNames) {
+            assertFalse(node.hasProperty(pN));
+        }
+    }
+
+    @Test
+    public void importExternalUserWithPrincipalNames() throws Exception {
+        Node parent = doImport(createSession(false), UserConstants.DEFAULT_USER_PATH, XML_EXTERNAL_USER_WITH_PRINCIPAL_NAMES);
+        assertHasProperties(parent.getNode("t"), ExternalIdentityConstants.REP_EXTERNAL_ID);
+        assertNotHasProperties(parent.getNode("t"), ExternalIdentityConstants.REP_LAST_SYNCED, ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES);
+    }
+
+    @Test
+    public void importExternalUserWithPrincipalNamesAsSystem() throws Exception {
+        Node parent = doImport(createSession(true), UserConstants.DEFAULT_USER_PATH, XML_EXTERNAL_USER_WITH_PRINCIPAL_NAMES);
+        assertHasProperties(parent.getNode("t"), ExternalIdentityConstants.REP_EXTERNAL_ID, ExternalIdentityConstants.REP_LAST_SYNCED, ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES);
+    }
+}
\ 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/ExternalIdentityImporterTest.java
------------------------------------------------------------------------------
    svn:eol-style = native