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 2015/10/29 11:08:17 UTC

svn commit: r1711209 - in /jackrabbit/oak/trunk/oak-auth-external/src: main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/ test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/

Author: angela
Date: Thu Oct 29 10:08:16 2015
New Revision: 1711209

URL: http://svn.apache.org/viewvc?rev=1711209&view=rev
Log:
OAK-3523 : DefaultSyncContext catches ClassCastException

Added:
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContextTest.java
Modified:
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java

Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java?rev=1711209&r1=1711208&r2=1711209&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java Thu Oct 29 10:08:16 2015
@@ -297,7 +297,7 @@ public class DefaultSyncContext implemen
                 return new DefaultSyncResultImpl(new DefaultSyncedIdentity(id, null, false, -1), SyncResult.Status.FOREIGN);
             }
 
-            if (auth instanceof Group) {
+            if (auth.isGroup()) {
                 Group group = (Group) auth;
                 ExternalGroup external = idp.getGroup(id);
                 timer.mark("retrieve");
@@ -505,35 +505,32 @@ public class DefaultSyncContext implemen
             // get group
             ExternalGroup extGroup;
             try {
-                extGroup = (ExternalGroup) idp.getIdentity(ref);
-            } catch (ClassCastException e) {
-                // this should really not be the case, so catching the CCE is ok here.
-                log.warn("External identity '{}' is not a group, but should be one.", ref.getString());
-                continue;
+                ExternalIdentity extId = idp.getIdentity(ref);
+                if (extId instanceof ExternalGroup) {
+                    extGroup = (ExternalGroup) extId;
+                } else {
+                    log.warn("No external group found for ref '{}'.", ref.getString());
+                    continue;
+                }
             } catch (ExternalIdentityException e) {
                 log.warn("Unable to retrieve external group '{}' from provider.", ref.getString(), e);
                 continue;
             }
-            if (extGroup == null) {
-                log.warn("External group for ref '{}' could not be retrieved from provider.", ref);
-                continue;
-            }
             log.debug("- idp returned '{}'", extGroup.getId());
 
             Group grp;
-            try {
-                grp = (Group) userManager.getAuthorizable(extGroup.getId());
-            } catch (ClassCastException e) {
-                // this should really not be the case, so catching the CCE is ok here.
+            Authorizable a = userManager.getAuthorizable(extGroup.getId());
+            if (a == null) {
+                grp = createGroup(extGroup);
+                log.debug("- created new group");
+            } else if (a.isGroup()) {
+                grp = (Group) a;
+            } else {
                 log.warn("Authorizable '{}' is not a group, but should be one.", extGroup.getId());
                 continue;
             }
             log.debug("- user manager returned '{}'", grp);
 
-            if (grp == null) {
-                grp = createGroup(extGroup);
-                log.debug("- created new group");
-            }
             syncGroup(extGroup, grp);
 
             // ensure membership

Added: 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=1711209&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContextTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContextTest.java Thu Oct 29 10:08:16 2015
@@ -0,0 +1,385 @@
+/*
+ * 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.basic;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.jcr.RepositoryException;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterators;
+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.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.value.ValueFactoryImpl;
+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.ExternalIdentityRef;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncResult;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.TestIdentityProvider;
+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.assertTrue;
+
+public class DefaultSyncContextTest extends AbstractSecurityTest {
+
+    private TestIdentityProvider idp = new TestIdentityProvider();
+    private DefaultSyncConfig config = new DefaultSyncConfig();
+
+    private DefaultSyncContext syncCtx;
+
+    private List<String> authorizableIds = new ArrayList<String>();
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+        syncCtx = new DefaultSyncContext(config, idp, getUserManager(root), new ValueFactoryImpl(root, NamePathMapper.DEFAULT));
+    }
+
+    @After
+    public void after() throws Exception {
+        try {
+            syncCtx.close();
+            UserManager umgr = getUserManager(root);
+            Iterator<ExternalIdentity> ids = Iterators.concat(idp.listGroups(), idp.listUsers());
+            while (ids.hasNext()) {
+                Authorizable a = umgr.getAuthorizable(ids.next().getId());
+                if (a != null) {
+                    a.remove();
+                }
+            }
+            for (String id : authorizableIds) {
+                Authorizable a = umgr.getAuthorizable(id);
+                if (a != null) {
+                    a.remove();
+                }
+            }
+            root.commit();
+        } finally {
+            super.after();
+        }
+    }
+
+    private Group createTestGroup() throws Exception {
+        Group gr = getUserManager(root).createGroup("group" + UUID.randomUUID());
+        authorizableIds.add(gr.getID());
+        return gr;
+    }
+
+    private void setExternalID(@Nonnull Authorizable authorizable, @Nullable String idpName) throws RepositoryException {
+        authorizable.setProperty(DefaultSyncContext.REP_EXTERNAL_ID, getValueFactory().createValue(authorizable.getID() + ';' + idpName));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSyncInvalidExternalIdentity() throws Exception {
+        syncCtx.sync(new TestExternalIdentity());
+    }
+
+    @Test
+    public void testSyncExternalUser() throws Exception {
+        ExternalUser user = idp.listUsers().next();
+        assertNotNull(user);
+
+        SyncResult result = syncCtx.sync(user);
+        assertEquals(SyncResult.Status.ADD, result.getStatus());
+
+        result = syncCtx.sync(user);
+        assertEquals(SyncResult.Status.NOP, result.getStatus());
+
+        syncCtx.setForceUserSync(true);
+        result = syncCtx.sync(user);
+        assertEquals(SyncResult.Status.UPDATE, result.getStatus());
+
+    }
+
+    @Test
+    public void testSyncExternalGroup() throws Exception {
+        ExternalGroup gr = idp.listGroups().next();
+        assertNotNull(gr);
+
+        SyncResult result = syncCtx.sync(gr);
+        assertEquals(SyncResult.Status.ADD, result.getStatus());
+
+        result = syncCtx.sync(gr);
+        assertEquals(SyncResult.Status.NOP, result.getStatus());
+
+        syncCtx.setForceGroupSync(true);
+        result = syncCtx.sync(gr);
+        assertEquals(SyncResult.Status.UPDATE, result.getStatus());
+    }
+
+    @Test
+    public void testSyncUserById() throws Exception {
+        ExternalIdentity externalId = idp.listUsers().next();
+
+        // no initial sync -> sync-by-id doesn't succeed
+        SyncResult result = syncCtx.sync(externalId.getId());
+        assertEquals(SyncResult.Status.NO_SUCH_AUTHORIZABLE, result.getStatus());
+
+        // force sync
+        syncCtx.sync(externalId);
+
+        // try again
+        syncCtx.setForceUserSync(true);
+        result = syncCtx.sync(externalId.getId());
+        assertEquals(SyncResult.Status.UPDATE, result.getStatus());
+    }
+
+    @Test
+    public void testSyncRemovedUserById() throws Exception {
+        // mark a regular repo user as external user from the test IDP
+        User u = getUserManager(root).createUser("test" + UUID.randomUUID(), null);
+        String userId = u.getID();
+        authorizableIds.add(userId);
+
+        setExternalID(u, idp.getName());
+
+        // test sync with 'keepmissing' = true
+        syncCtx.setKeepMissing(true);
+        SyncResult result = syncCtx.sync(userId);
+        assertEquals(SyncResult.Status.MISSING, result.getStatus());
+        assertNotNull(getUserManager(root).getAuthorizable(userId));
+
+        // test sync with 'keepmissing' = false
+        syncCtx.setKeepMissing(false);
+        result = syncCtx.sync(userId);
+        assertEquals(SyncResult.Status.DELETE, result.getStatus());
+
+        assertNull(getUserManager(root).getAuthorizable(userId));
+    }
+
+    @Test
+    public void testSyncGroupById() throws Exception {
+        ExternalIdentity externalId = idp.listGroups().next();
+
+        // no initial sync -> sync-by-id doesn't succeed
+        SyncResult result = syncCtx.sync(externalId.getId());
+        assertEquals(SyncResult.Status.NO_SUCH_AUTHORIZABLE, result.getStatus());
+
+        // force sync
+        syncCtx.sync(externalId);
+
+        // try again
+        syncCtx.setForceGroupSync(true);
+        result = syncCtx.sync(externalId.getId());
+        assertEquals(SyncResult.Status.UPDATE, result.getStatus());
+    }
+
+    @Test
+    public void testSyncRemovedGroupById() throws Exception {
+        // mark a regular repo user as external user from the test IDP
+        Group gr = createTestGroup();
+        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(getUserManager(root).getAuthorizable(groupId));
+
+        // test sync with 'keepmissing' = false
+        syncCtx.setKeepMissing(false);
+        result = syncCtx.sync(groupId);
+        assertEquals(SyncResult.Status.DELETE, result.getStatus());
+
+        assertNull(getUserManager(root).getAuthorizable(groupId));
+    }
+
+    @Test
+    public void testSyncRemovedGroupWithMembers() throws Exception {
+        // mark a regular repo user as external user from the test IDP
+        Group gr = createTestGroup();
+        gr.addMember(getTestUser());
+
+        String groupId = gr.getID();
+        setExternalID(gr, idp.getName());
+
+        // test sync with 'keepmissing' = true
+        syncCtx.setKeepMissing(true);
+        SyncResult result = syncCtx.sync(groupId);
+        assertEquals(SyncResult.Status.NOP, result.getStatus());
+        assertNotNull(getUserManager(root).getAuthorizable(groupId));
+
+        // test sync with 'keepmissing' = false
+        syncCtx.setKeepMissing(false);
+        result = syncCtx.sync(groupId);
+        assertEquals(SyncResult.Status.NOP, result.getStatus());
+
+        assertNotNull(getUserManager(root).getAuthorizable(groupId));
+    }
+
+    @Test
+    public void testSyncByForeignId() throws Exception {
+        SyncResult result = syncCtx.sync(getTestUser().getID());
+        assertEquals(SyncResult.Status.FOREIGN, result.getStatus());
+    }
+
+    @Test
+    public void testSyncByForeignId2() throws Exception {
+        User u = getTestUser();
+        setExternalID(u, "differentIDP");
+
+        SyncResult result = syncCtx.sync(u.getID());
+        assertEquals(SyncResult.Status.FOREIGN, result.getStatus());
+    }
+
+    @Test
+    public void testSyncAutoMembership() throws Exception {
+        Group gr = createTestGroup();
+
+        config.user().setAutoMembership(gr.getID());
+
+        SyncResult result = syncCtx.sync(idp.listUsers().next());
+        assertEquals(SyncResult.Status.ADD, result.getStatus());
+
+        Authorizable a = getUserManager(root).getAuthorizable(result.getIdentity().getId());
+        assertTrue(gr.isDeclaredMember(a));
+    }
+
+    @Test
+    public void testSyncAutoMembershipListsNonExistingGroup() throws Exception {
+        config.user().setAutoMembership("nonExistingGroup");
+
+        SyncResult result = syncCtx.sync(idp.listUsers().next());
+        assertEquals(SyncResult.Status.ADD, result.getStatus());
+    }
+
+    @Test
+    public void testSyncAutoMembershipListsUser() throws Exception {
+        // set auto-membership config to point to a user instead a group
+        config.user().setAutoMembership(getTestUser().getID());
+        syncCtx.sync(idp.listUsers().next());
+    }
+
+    @Test
+    public void testLostMembership() throws Exception {
+        // create a group in the repository which is marked as being external
+        // and associated with the test-IDP to setup the situation that a
+        // repository group is no longer listed in the IDP.
+        Group gr = createTestGroup();
+        setExternalID(gr, idp.getName());
+
+        // sync an external user from the IDP into the repo and make it member
+        // of the test group
+        SyncResult result = syncCtx.sync(idp.listUsers().next());
+        User user = getUserManager(root).getAuthorizable(result.getIdentity().getId(), User.class);
+        gr.addMember(user);
+        root.commit();
+
+        // enforce synchronization of the user and it's group membership
+        syncCtx.setForceUserSync(true);
+        config.user().setMembershipExpirationTime(-1);
+
+        // 1. membership nesting is < 0 => membership not synchronized
+        config.user().setMembershipNestingDepth(-1);
+        syncCtx.sync(user.getID()).getStatus();
+        assertTrue(gr.isDeclaredMember(user));
+
+        // 2. membership nesting is > 0 => membership gets synchronized
+        config.user().setMembershipNestingDepth(1);
+        assertEquals(SyncResult.Status.UPDATE, syncCtx.sync(user.getID()).getStatus());
+
+        assertFalse(gr.isDeclaredMember(user));
+    }
+
+    @Test
+    public void testLostMembershipDifferentIDP() throws Exception {
+        // create a group in the repository which is marked as being external
+        // and associated with another IPD.
+        Group gr = createTestGroup();
+        setExternalID(gr, "differentIDP");
+
+        // sync an external user from the IDP into the repo and make it member
+        // of the test group
+        SyncResult result = syncCtx.sync(idp.listUsers().next());
+        User user = getUserManager(root).getAuthorizable(result.getIdentity().getId(), User.class);
+        gr.addMember(user);
+        root.commit();
+
+        // enforce synchronization of the user and it's group membership
+        syncCtx.setForceUserSync(true);
+        config.user().setMembershipExpirationTime(-1);
+        config.user().setMembershipNestingDepth(1);
+
+        assertEquals(SyncResult.Status.UPDATE, syncCtx.sync(user.getID()).getStatus());
+
+        // since the group is not associated with the test-IDP the group-membership
+        // must NOT be modified during the sync.
+        assertTrue(gr.isDeclaredMember(user));
+    }
+
+    /**
+     * ExternalIdentity implementation that is neither user nor group.
+     */
+    private final class TestExternalIdentity implements ExternalIdentity {
+
+        @Nonnull
+        @Override
+        public ExternalIdentityRef getExternalId() {
+            return new ExternalIdentityRef(getId(), idp.getName());
+        }
+
+        @Nonnull
+        @Override
+        public String getId() {
+            return "externalId";
+        }
+
+        @Nonnull
+        @Override
+        public String getPrincipalName() {
+            return "principalName";
+        }
+
+        @CheckForNull
+        @Override
+        public String getIntermediatePath() {
+            return null;
+        }
+
+        @Nonnull
+        @Override
+        public Iterable<ExternalIdentityRef> getDeclaredGroups() {
+            return ImmutableSet.of();
+        }
+
+        @Nonnull
+        @Override
+        public Map<String, ?> getProperties() {
+            return ImmutableMap.of();
+        }
+    }
+}
\ No newline at end of file