Index: oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentity.java =================================================================== --- oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentity.java (revision 1798052) +++ oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentity.java (working copy) @@ -21,9 +21,12 @@ import javax.annotation.CheckForNull; import javax.annotation.Nonnull; +import aQute.bnd.annotation.ProviderType; + /** * {@code ExternalIdentity} defines an identity provided by an external system. */ +@ProviderType public interface ExternalIdentity { /** @@ -61,7 +64,7 @@ String getIntermediatePath(); /** - * Returns an iterable of the declared groups of this external identity. + * Returns an iterable of the declared active groups of this external identity. * @return the declared groups * @throws ExternalIdentityException if an error occurs */ @@ -75,4 +78,12 @@ @Nonnull Map getProperties(); + /** + * Returns a boolean value indicating whether this identity is active. + * @return true if this identity is active. + * @throws ExternalIdentityException if an error occurs + */ + default boolean isActive() throws ExternalIdentityException { + return true; + } } \ No newline at end of file Index: oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncResult.java =================================================================== --- oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncResult.java (revision 1798052) +++ oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncResult.java (working copy) @@ -91,7 +91,12 @@ /** * nothing changed. idp name not correct */ - FOREIGN + FOREIGN, + + /** + * nothing changed. identity is inactive + */ + INACTIVE } } \ No newline at end of file Index: oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java =================================================================== --- oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java (revision 1798052) +++ oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java (working copy) @@ -229,6 +229,9 @@ return new DefaultSyncResultImpl(new DefaultSyncedIdentity(identity.getId(), ref, isGroup, -1), SyncResult.Status.FOREIGN); } try { + if (!identity.isActive()) { + return new DefaultSyncResultImpl(new DefaultSyncedIdentity(identity.getId(), ref, identity instanceof ExternalGroup, -1), SyncResult.Status.INACTIVE); + } DebugTimer timer = new DebugTimer(); DefaultSyncResultImpl ret; boolean created = false; @@ -262,7 +265,7 @@ ret.setStatus(SyncResult.Status.ADD); } return ret; - } catch (RepositoryException e) { + } catch (RepositoryException | ExternalIdentityException e) { throw new SyncException(e); } } @@ -290,7 +293,7 @@ if (auth.isGroup()) { ExternalGroup external = idp.getGroup(id); timer.mark("retrieve"); - if (external == null) { + if (external == null || !external.isActive()) { ret = handleMissingIdentity(id, auth, timer); } else { ret = syncGroup(external, (Group) auth); @@ -299,7 +302,7 @@ } else { ExternalUser external = idp.getUser(id); timer.mark("retrieve"); - if (external == null) { + if (external == null || !external.isActive()) { ret = handleMissingIdentity(id, auth, timer); } else { ret = syncUser(external, (User) auth); @@ -327,18 +330,18 @@ status = SyncResult.Status.NOP; } else if (!keepMissing) { if (config.user().getDisableMissing() && !authorizable.isGroup()) { - ((User) authorizable).disable("No longer exists on external identity provider '" + idp.getName() + "'"); - log.debug("disabling user '{}' that no longer exists on IDP {}", id, idp.getName()); + ((User) authorizable).disable("No longer active on external identity provider '" + idp.getName() + "'"); + log.debug("disabling user '{}' who is no longer active on IDP {}", id, idp.getName()); status = SyncResult.Status.DISABLE; } else { authorizable.remove(); - log.debug("removing authorizable '{}' that no longer exists on IDP {}", id, idp.getName()); + log.debug("removing authorizable '{}' who is no longer active on IDP {}", id, idp.getName()); status = SyncResult.Status.DELETE; } timer.mark("remove"); } else { status = SyncResult.Status.MISSING; - log.info("external identity missing for {}, but purge == false.", id); + log.info("no active external identity for {}, but purge == false.", id); } return new DefaultSyncResultImpl(syncId, status); } @@ -537,10 +540,10 @@ ExternalGroup extGroup; try { ExternalIdentity extId = idp.getIdentity(ref); - if (extId instanceof ExternalGroup) { + if (extId instanceof ExternalGroup && extId.isActive()) { extGroup = (ExternalGroup) extId; } else { - log.warn("No external group found for ref '{}'.", ref.getString()); + log.warn("No active external group found for ref '{}'.", ref.getString()); continue; } } catch (ExternalIdentityException e) { Index: oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContext.java =================================================================== --- oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContext.java (revision 1798052) +++ oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContext.java (working copy) @@ -81,6 +81,9 @@ return super.sync(identity); } else if (identity instanceof ExternalGroup) { try { + if (!identity.isActive()) { + return new DefaultSyncResultImpl(new DefaultSyncedIdentity(identity.getId(), identity.getExternalId(), true, -1), SyncResult.Status.INACTIVE); + } Group group = getAuthorizable(identity, Group.class); if (group != null) { // group has been synchronized before -> continue updating for consistency. @@ -98,7 +101,7 @@ SyncResult.Status status = (isSameIDP(ref)) ? SyncResult.Status.NOP : SyncResult.Status.FOREIGN; return new DefaultSyncResultImpl(new DefaultSyncedIdentity(identity.getId(), ref, true, -1), status); } - } catch (RepositoryException e) { + } catch (RepositoryException | ExternalIdentityException e) { throw new SyncException(e); } } else { @@ -158,7 +161,7 @@ } else { // get group from the IDP ExternalIdentity extId = idp.getIdentity(ref); - if (extId instanceof ExternalGroup) { + if (extId instanceof ExternalGroup && extId.isActive()) { principalNames.add(extId.getPrincipalName()); // recursively apply further membership until the configured depth is reached if (depth > 1) { @@ -165,7 +168,7 @@ collectPrincipalNames(principalNames, extId.getDeclaredGroups(), depth - 1); } } else { - log.debug("Not an external group ({}) => ignore.", extId); + log.debug("Not an active external group ({}) => ignore.", extId); } } } Index: oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/Delegatee.java =================================================================== --- oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/Delegatee.java (revision 1798052) +++ oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/Delegatee.java (working copy) @@ -201,7 +201,7 @@ } else { try { ExternalIdentity id = idp.getIdentity(ref); - if (id != null) { + if (id != null && id.isActive()) { results = syncUser(id, results, list); } else { results.add(new DefaultSyncResultImpl( @@ -335,7 +335,7 @@ ExternalIdentityRef ref = syncedIdentity.getExternalIdRef(); try { ExternalIdentity extId = (ref == null) ? null : idp.getIdentity(ref); - if (extId == null) { + if (extId == null || !extId.isActive()) { return syncedIdentity.getId(); } } catch (ExternalIdentityException e) { Index: oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/SynchronizationMBean.java =================================================================== --- oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/SynchronizationMBean.java (revision 1798052) +++ oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/SynchronizationMBean.java (working copy) @@ -67,7 +67,7 @@ /** * Synchronizes the local users with the given user ids. * @param userIds the user ids - * @param purge if {@code true} users that don't exist in the IDP are deleted. + * @param purge if {@code true} users that don't exist in the IDP or are flagged inactive by the IDP are deleted. * @return result messages. */ @Nonnull @@ -77,7 +77,7 @@ * Synchronizes all local users with the given user ids. Note that this can be an expensive operation since all * potential users need to be examined. * - * @param purge if {@code true} users that don't exist in the IDP are deleted. + * @param purge if {@code true} users that don't exist in the IDP or are flagged inactive by the IDP are deleted. * @return result messages. */ @Nonnull @@ -84,7 +84,7 @@ String[] syncAllUsers(boolean purge); /** - * Synchronizes the external users with the given external ids. + * Synchronizes the active external users with the given external ids. * @param externalIds the external id * @return result messages. */ @@ -92,7 +92,7 @@ String[] syncExternalUsers(@Nonnull String[] externalIds); /** - * Synchronizes all the external users, i.e. basically imports the entire IDP. Note that this can be an expensive + * Synchronizes all active external users, i.e. basically imports the entire IDP. Note that this can be an expensive * operation. * * @return result messages. @@ -101,8 +101,9 @@ String[] syncAllExternalUsers(); /** - * Returns a list of orphaned users, i.e. users that don't exist anymore on the IDP. Note that this can be an - * expensive operation since all potential users need to be examined. + * Returns a list of orphaned users, i.e. users that don't exist anymore on the IDP or that are flagged inactive + * by the IDP ({@see ExternalIdentity#isActive()}). + * Note that this can be an expensive operation since all potential users need to be examined. * @return a list of the user ids of orphaned users. */ @Nonnull @@ -109,8 +110,10 @@ String[] listOrphanedUsers(); /** - * Purges all orphaned users. this is similar to invoke {@link #syncUsers(String[], boolean)} with the list of - * orphaned users. Note tha this can be an expensive operation since all potential users need to be examined. + * Purges all orphaned users, i.e. users that don't exist anymore on the IDP or that are flagged inactive + * by the IDP ({@see ExternalIdentity#isActive()}). This is similar to invoking {@link #syncUsers(String[], boolean)} + * with the list of orphaned users. + * Note that this can be an expensive operation since all potential users need to be examined. * * @return result messages. */ Index: oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/package-info.java =================================================================== --- oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/package-info.java (revision 1798052) +++ oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/package-info.java (working copy) @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@Version("2.3.0") +@Version("2.4.0") @Export package org.apache.jackrabbit.oak.spi.security.authentication.external; Index: oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java =================================================================== --- oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java (revision 1798052) +++ oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java (working copy) @@ -21,6 +21,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.jcr.Credentials; @@ -34,7 +35,11 @@ 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_PASSIVE_USER = "passiveUser"; + public static final String ID_PASSIVE_GROUP = "passiveGroup"; + public static final String ID_PASSIVE_GROUP_WITH_MEMBER = "passiveGroupWithMember"; + public static final String ID_EXCEPTION = "throw!"; public static final String DEFAULT_IDP_NAME = "test"; @@ -59,6 +64,8 @@ addGroup(new TestGroup("secondGroup", getName())); addGroup(new TestGroup("_gr_u_", getName())); addGroup(new TestGroup("g%r%", getName())); + addGroup(new TestGroup(ID_PASSIVE_GROUP, getName(), false)); + addGroup(new TestGroup(ID_PASSIVE_GROUP_WITH_MEMBER, getName(), false)); addUser(new TestUser(ID_TEST_USER, getName()) .withProperty("name", "Test User") @@ -79,6 +86,14 @@ addUser(new TestUser(ID_WILDCARD_USER, getName()) .withGroups("_gr_u_", "g%r%")); + + addUser(new TestUser(ID_PASSIVE_USER, getName(), false) + .withProperty("name", "Passive User") + .withProperty("profile/name", "Passive User") + .withProperty("profile/age", 144) + .withProperty("email", "passive@testuser.com") + .withGroups("a", "b", "c", ID_PASSIVE_GROUP_WITH_MEMBER) + ); } public void addUser(TestIdentity user) { @@ -113,7 +128,8 @@ if (ID_EXCEPTION.equals(userId)) { throw new ExternalIdentityException(ID_EXCEPTION); } - return externalUsers.get(userId.toLowerCase()); + ExternalUser externalUser = externalUsers.get(userId.toLowerCase()); + return externalUser != null? externalUser.isActive()? externalUser : null : null; } @Override @@ -124,7 +140,7 @@ SimpleCredentials creds = (SimpleCredentials) credentials; TestUser user = (TestUser) getUser(creds.getUserID()); if (user != null) { - if (!new String(creds.getPassword()).equals(user.getPassword())) { + if (!user.isActive() || !new String(creds.getPassword()).equals(user.getPassword())) { throw new LoginException("Invalid User/Password"); } } @@ -142,13 +158,29 @@ @Nonnull @Override public Iterator listUsers() throws ExternalIdentityException { - return externalUsers.values().iterator(); + return externalUsers.values().stream().filter(p -> { + try { + return p.isActive(); + } + catch (ExternalIdentityException e) { + //ignore + return false; + } + }).iterator(); } @Nonnull @Override public Iterator listGroups() throws ExternalIdentityException { - return externalGroups.values().iterator(); + return externalGroups.values().stream().filter(p -> { + try { + return p.isActive(); + } + catch (ExternalIdentityException e) { + //ignore + return false; + } + }).iterator(); } public static class TestIdentity implements ExternalIdentity { @@ -156,6 +188,7 @@ private final String userId; private final String principalName; private final ExternalIdentityRef id; + private boolean active = true; private final Set groups = new HashSet(); private final Map props = new HashMap(); @@ -215,6 +248,15 @@ return props; } + @Override + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + protected TestIdentity withProperty(String name, Object value) { props.put(name, value); return this; @@ -234,10 +276,14 @@ super(userId, userId, idpName); } + public TestUser(String userId, @Nonnull String idpName, boolean active) { + super(userId, userId, idpName); + this.setActive(active); + } + public String getPassword() { return ""; } - } public static class TestGroup extends TestIdentity implements ExternalGroup { @@ -246,6 +292,11 @@ super(userId, userId, idpName); } + public TestGroup(@Nonnull String userId, @Nonnull String idpName, boolean active) { + super(userId, userId, idpName); + this.setActive(active); + } + @Nonnull @Override public Iterable getDeclaredMembers() throws ExternalIdentityException { Index: oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContextTest.java =================================================================== --- oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContextTest.java (revision 1798052) +++ oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContextTest.java (working copy) @@ -476,7 +476,8 @@ result = syncCtx.sync(userId); assertEquals(SyncResult.Status.ENABLE, result.getStatus()); - assertNotNull(userManager.getAuthorizable(userId)); + authorizable = userManager.getAuthorizable(userId); + assertNotNull(authorizable); assertFalse(((User)authorizable).isDisabled()); } @@ -512,6 +513,69 @@ } @Test + public void testSyncPassiveUserById() throws Exception { + + //create a synced user, set it passive + TestIdentityProvider.TestIdentity passiveUser = (TestIdentityProvider.TestIdentity)idp.getIdentity(new ExternalIdentityRef(TestIdentityProvider.ID_PASSIVE_USER, TestIdentityProvider.DEFAULT_IDP_NAME)); + passiveUser.setActive(true); + sync(passiveUser); + User u = userManager.getAuthorizable(passiveUser.getId(), User.class); + assertNotNull(u); + passiveUser.setActive(false); + + // test sync with 'keepmissing' = true + syncCtx.setKeepMissing(true); + SyncResult result = syncCtx.sync(u.getID()); + assertEquals(SyncResult.Status.MISSING, result.getStatus()); + assertNotNull(userManager.getAuthorizable(u.getID())); + + // test sync with 'keepmissing' = false + syncCtx.setKeepMissing(false); + result = syncCtx.sync(u.getID()); + assertEquals(SyncResult.Status.DELETE, result.getStatus()); + assertNull(userManager.getAuthorizable(u.getID())); + } + + @Test + public void testSyncPassiveDisabledUserById() throws Exception { + + // configure to disable missing users + syncConfig.user().setDisableMissing(true); + + //create a synced user, set it passive + TestIdentityProvider.TestIdentity passiveUser = (TestIdentityProvider.TestIdentity)idp.getIdentity(new ExternalIdentityRef(TestIdentityProvider.ID_PASSIVE_USER, TestIdentityProvider.DEFAULT_IDP_NAME)); + passiveUser.setActive(true); + sync(passiveUser); + User u = userManager.getAuthorizable(passiveUser.getId(), User.class); + assertNotNull(u); + passiveUser.setActive(false); + + // test sync with 'keepmissing' = true + syncCtx.setKeepMissing(true); + SyncResult result = syncCtx.sync(u.getID()); + assertEquals(SyncResult.Status.MISSING, result.getStatus()); + assertNotNull(userManager.getAuthorizable(u.getID())); + + // test sync with 'keepmissing' = false + syncCtx.setKeepMissing(false); + result = syncCtx.sync(u.getID()); + assertEquals(SyncResult.Status.DISABLE, result.getStatus()); + + Authorizable authorizable = userManager.getAuthorizable(u.getID()); + assertNotNull(authorizable); + assertTrue(((User)authorizable).isDisabled()); + + //set identity active again + passiveUser.setActive(true); + syncCtx.setForceUserSync(true); + result = syncCtx.sync(u.getID()); + assertEquals(SyncResult.Status.ENABLE, result.getStatus()); + authorizable = userManager.getAuthorizable(u.getID()); + assertNotNull(authorizable); + assertFalse(((User)authorizable).isDisabled()); + } + + @Test public void testSyncGroupById() throws Exception { ExternalIdentity externalId = idp.listGroups().next(); @@ -574,6 +638,63 @@ } @Test + public void testSyncPassiveGroupById() throws Exception { + + //create a synced group, set it passive + TestIdentityProvider.TestIdentity passiveGroup = (TestIdentityProvider.TestIdentity)idp.getIdentity(new ExternalIdentityRef(TestIdentityProvider.ID_PASSIVE_GROUP, TestIdentityProvider.DEFAULT_IDP_NAME)); + passiveGroup.setActive(true); + sync(passiveGroup); + Group g = userManager.getAuthorizable(passiveGroup.getId(), Group.class); + assertNotNull(g); + passiveGroup.setActive(false); + + // test sync with 'keepmissing' = true + syncCtx.setKeepMissing(true); + SyncResult result = syncCtx.sync(g.getID()); + assertEquals(SyncResult.Status.MISSING, result.getStatus()); + assertNotNull(userManager.getAuthorizable(g.getID())); + + // test sync with 'keepmissing' = false + syncCtx.setKeepMissing(false); + result = syncCtx.sync(g.getID()); + assertEquals(SyncResult.Status.DELETE, result.getStatus()); + assertNull(userManager.getAuthorizable(g.getID())); + } + + @Test + public void testSyncPassiveGroupWithMembers() throws Exception { + + syncCtx.setForceGroupSync(true); + syncCtx.setForceUserSync(true); + + //create a synced group with one synced member, set it passive + TestIdentityProvider.TestIdentity passiveGroup = (TestIdentityProvider.TestIdentity)idp.getIdentity(new ExternalIdentityRef(TestIdentityProvider.ID_PASSIVE_GROUP_WITH_MEMBER, TestIdentityProvider.DEFAULT_IDP_NAME)); + passiveGroup.setActive(true); + TestIdentityProvider.TestIdentity passiveUser = (TestIdentityProvider.TestIdentity)idp.getIdentity(new ExternalIdentityRef(TestIdentityProvider.ID_PASSIVE_USER, TestIdentityProvider.DEFAULT_IDP_NAME)); + passiveUser.setActive(true); + syncConfig.user().setMembershipNestingDepth(1); + sync(passiveUser); + User u = userManager.getAuthorizable(passiveUser.getId(), User.class); + assertNotNull(u); + Group g = userManager.getAuthorizable(passiveGroup.getId(), Group.class); + assertNotNull(g); + assertTrue(g.isDeclaredMember(u)); + passiveGroup.setActive(false); + + // test sync with 'keepmissing' = true + syncCtx.setKeepMissing(true); + SyncResult result = syncCtx.sync(g.getID()); + assertEquals(SyncResult.Status.NOP, result.getStatus()); + assertNotNull(userManager.getAuthorizable(g.getID())); + + // test sync with 'keepmissing' = false + syncCtx.setKeepMissing(false); + result = syncCtx.sync(g.getID()); + assertEquals(SyncResult.Status.NOP, result.getStatus()); + assertNotNull(userManager.getAuthorizable(g.getID())); + } + + @Test public void testSyncByForeignUserId() throws Exception { SyncResult result = syncCtx.sync(getTestUser().getID()); Index: oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/DelegateeTest.java =================================================================== --- oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/DelegateeTest.java (revision 1798052) +++ oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/DelegateeTest.java (working copy) @@ -51,7 +51,8 @@ private static final String[] TEST_IDS = new String[] { TestIdentityProvider.ID_TEST_USER, TestIdentityProvider.ID_SECOND_USER, - TestIdentityProvider.ID_WILDCARD_USER}; + TestIdentityProvider.ID_WILDCARD_USER, + TestIdentityProvider.ID_PASSIVE_USER}; @Before public void before() throws Exception { @@ -103,7 +104,8 @@ assertResultMessages(result, ImmutableMap.of( TestIdentityProvider.ID_TEST_USER, "nsa", TestIdentityProvider.ID_SECOND_USER, "nsa", - TestIdentityProvider.ID_WILDCARD_USER, "nsa")); + TestIdentityProvider.ID_WILDCARD_USER, "nsa", + TestIdentityProvider.ID_PASSIVE_USER, "nsa")); assertFalse(r.hasPendingChanges()); } @@ -222,7 +224,8 @@ assertResultMessages(result, ImmutableMap.of( TestIdentityProvider.ID_TEST_USER, "ERR", TestIdentityProvider.ID_SECOND_USER, "ERR", - TestIdentityProvider.ID_WILDCARD_USER, "ERR")); + TestIdentityProvider.ID_WILDCARD_USER, "ERR", + "", "nsi")); assertFalse(r.hasPendingChanges()); } Index: oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/SyncMBeanImplTest.java =================================================================== --- oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/SyncMBeanImplTest.java (revision 1798052) +++ oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/SyncMBeanImplTest.java (working copy) @@ -214,11 +214,13 @@ public void testSyncUsersPurge() throws Exception { sync(new TestIdentityProvider.TestUser("thirdUser", idp.getName()), idp); sync(new TestIdentityProvider.TestGroup("gr", idp.getName()), idp); + sync(new TestIdentityProvider.TestUser("passiveUser", idp.getName()), idp); UserManager userManager = getUserManager(); Authorizable[] authorizables = new Authorizable[] { userManager.getAuthorizable("thirdUser"), - userManager.getAuthorizable("gr") + userManager.getAuthorizable("gr"), + userManager.getAuthorizable("passiveUser") }; for (Authorizable a : authorizables) { Index: oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapGroup.java =================================================================== --- oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapGroup.java (revision 1798052) +++ oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapGroup.java (working copy) @@ -40,4 +40,9 @@ } return members.values(); } + + public boolean isActive() throws ExternalIdentityException { + //identity is considered active when it matches the group filter + return provider.getGroup(id) != null; + } } Index: oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapUser.java =================================================================== --- oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapUser.java (revision 1798052) +++ oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapUser.java (working copy) @@ -16,6 +16,7 @@ */ package org.apache.jackrabbit.oak.security.authentication.ldap.impl; +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; @@ -25,4 +26,8 @@ super(provider, ref, id, path); } + public boolean isActive() throws ExternalIdentityException { + //identity is considered active when it matches the user filter + return provider.getUser(id) != null; + } }