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 1794732) +++ oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentity.java (working copy) @@ -75,4 +75,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/impl/jmx/Delegatee.java =================================================================== --- oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/jmx/Delegatee.java (revision 1794732) +++ 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 1794732) +++ 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 1794732) +++ 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 1794732) +++ oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java (working copy) @@ -34,6 +34,7 @@ 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_EXCEPTION = "throw!"; @@ -79,6 +80,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") + ); } public void addUser(TestIdentity user) { @@ -113,7 +122,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 +134,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,7 +152,15 @@ @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 @@ -156,6 +174,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 +234,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 +262,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 { 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 1794732) +++ 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 1794732) +++ 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/LdapIdentity.java =================================================================== --- oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapIdentity.java (revision 1794732) +++ oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapIdentity.java (working copy) @@ -109,4 +109,9 @@ public String toString() { return "LdapIdentity{" + "ref=" + ref + ", id='" + id + '\'' + '}'; } + + @Override + public boolean isActive() throws ExternalIdentityException { + return provider.getUser(id) != null; + } }