Author: angela
Date: Thu Nov 28 15:37:42 2019
New Revision: 1870559

URL: http://svn.apache.org/viewvc?rev=1870559&view=rev
Log:
OAK-8710 : AbstractLoginModule#logout() must not remove 'foreign' 
principals/credentials 

Modified:
    
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
    
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/PreAuthLoginModule.java
    
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModule.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImpl.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestLoginModule.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModuleTest.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImplTest.java
    
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/JaasConfigSpiTest.groovy
    
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/TokenAuthenticationTest.groovy
    
jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java
    
jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/GuestLoginModule.java
    
jackrabbit/oak/trunk/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModuleTest.java
    
jackrabbit/oak/trunk/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/GuestLoginModuleTest.java

Modified: 
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
 Thu Nov 28 15:37:42 2019
@@ -57,7 +57,10 @@ import javax.security.auth.login.LoginEx
 import java.security.Principal;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * {@code ExternalLoginModule} implements a {@code LoginModule} that uses an
@@ -190,19 +193,19 @@ public class ExternalLoginModule extends
         if (idp == null || syncHandler == null) {
             return false;
         }
-        credentials = getCredentials();
+        Credentials creds = getCredentials();
 
         // check if we have a pre authenticated login from a previous login 
module
         final PreAuthenticatedLogin preAuthLogin = getSharedPreAuthLogin();
-        final String userId = getUserId(preAuthLogin, credentials);
+        final String userId = getUserId(preAuthLogin, creds);
 
-        if (userId == null && credentials == null) {
+        if (userId == null && creds == null) {
             log.debug("No credentials|userId found for external login module. 
ignoring.");
             return false;
         }
 
         // remember identification for log-output
-        Object logId = (userId != null) ? userId : credentials;
+        Object logId = (userId != null) ? userId : creds;
         try {
             // check if there exists a user with the given ID that has been 
synchronized
             // before into the repository.
@@ -220,15 +223,15 @@ public class ExternalLoginModule extends
             if (preAuthLogin != null) {
                 externalUser = idp.getUser(preAuthLogin.getUserId());
             } else {
-                externalUser = idp.authenticate(credentials);
+                externalUser = idp.authenticate(creds);
             }
 
             if (externalUser != null) {
                 log.debug("IDP {} returned valid user {}", idp.getName(), 
externalUser);
 
-                if (credentials != null) {
+                if (creds != null) {
                     //noinspection unchecked
-                    sharedState.put(SHARED_KEY_CREDENTIALS, credentials);
+                    sharedState.put(SHARED_KEY_CREDENTIALS, creds);
                 }
 
                 //noinspection unchecked
@@ -236,6 +239,8 @@ public class ExternalLoginModule extends
 
                 syncUser(externalUser);
 
+                // login successful -> remember credentials for commit/logout
+                credentials = creds;
                 return true;
             } else {
                 debug("IDP {} returned null for {}", idp.getName(), 
logId.toString());
@@ -293,6 +298,12 @@ public class ExternalLoginModule extends
         return true;
     }
 
+    @Override
+    public boolean logout() throws LoginException {
+        Set creds = Stream.of(credentials, 
authInfo).filter(Objects::nonNull).collect(Collectors.toSet());
+        return logout((creds.isEmpty() ? null : creds), principals);
+    }
+
     //------------------------------------------------------------< private 
>---
 
     @Nullable

Modified: 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/PreAuthLoginModule.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/PreAuthLoginModule.java?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/PreAuthLoginModule.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/PreAuthLoginModule.java
 Thu Nov 28 15:37:42 2019
@@ -58,4 +58,9 @@ public final class PreAuthLoginModule ex
     public boolean commit() {
         return false;
     }
+
+    @Override
+    public boolean logout() {
+        return false;
+    }
 }

Modified: 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java
 Thu Nov 28 15:37:42 2019
@@ -121,12 +121,16 @@ public class ExternalLoginModuleTest ext
         verify(extIPMgr, never()).getProvider("idp");
         verify(syncManager, never()).getSyncHandler("syncHandler");
         assertFalse(loginModule.login());
+        assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test
     public void testInitializeMissingIdpSyncHandler() throws LoginException {
         loginModule.initialize(new Subject(), createCallbackHandler(wb, null, 
null, null), Collections.emptyMap(), ImmutableMap.of(PARAM_IDP_NAME, "idp", 
PARAM_SYNC_HANDLER_NAME, "syncHandler"));
         assertFalse(loginModule.login());
+        assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test
@@ -139,6 +143,8 @@ public class ExternalLoginModuleTest ext
         verify(extIPMgr, times(1)).getProvider("idp");
         verify(syncManager, times(1)).getSyncHandler("syncHandler");
         assertFalse(loginModule.login());
+        assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test
@@ -161,6 +167,8 @@ public class ExternalLoginModuleTest ext
     @Test
     public void testLoginWithoutInit() throws Exception {
         assertFalse(loginModule.login());
+        assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test
@@ -170,6 +178,8 @@ public class ExternalLoginModuleTest ext
 
         loginModule.initialize(new Subject(), createCallbackHandler(wb, null, 
null, null), Collections.emptyMap(), Collections.singletonMap(PARAM_IDP_NAME, 
"idpName"));
         assertFalse(loginModule.login());
+        assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test
@@ -189,6 +199,8 @@ public class ExternalLoginModuleTest ext
 
         loginModule.initialize(new Subject(), cbh, new HashMap<>(), 
ImmutableMap.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, 
"syncHandler"));
         assertFalse(loginModule.login());
+        assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test
@@ -215,6 +227,9 @@ public class ExternalLoginModuleTest ext
 
         root.refresh();
         assertNotNull(getUserManager(root).getAuthorizable(ID_TEST_USER));
+
+        assertTrue(loginModule.logout());
+        assertTrue(subject.getPublicCredentials().isEmpty());
     }
 
     @Test
@@ -247,6 +262,13 @@ public class ExternalLoginModuleTest ext
         assertFalse(subject.getPublicCredentials(AuthInfo.class).isEmpty());
         AuthInfo info = 
subject.getPublicCredentials(AuthInfo.class).iterator().next();
         assertTrue(info.getPrincipals().contains(principal));
+
+        assertTrue(loginModule.logout());
+
+        // authinfo must be removed upon logout
+        assertTrue(subject.getPublicCredentials(AuthInfo.class).isEmpty());
+        // predefined principal must _not_ be removed
+        assertTrue(subject.getPrincipals().contains(principal));
     }
 
     @Test
@@ -277,6 +299,10 @@ public class ExternalLoginModuleTest ext
 
         AuthInfo authInfo = 
subject.getPublicCredentials(AuthInfo.class).iterator().next();
         assertNull(authInfo.getAttribute("attr"));
+
+        assertTrue(loginModule.logout());
+        assertTrue(subject.getPublicCredentials().isEmpty());
+        assertTrue(subject.getPrincipals().isEmpty());
     }
 
     @Test
@@ -300,6 +326,8 @@ public class ExternalLoginModuleTest ext
 
         root.refresh();
         assertNotNull(getUserManager(root).getAuthorizable(ID_TEST_USER));
+
+        assertTrue(loginModule.logout());
     }
 
     @Test(expected = LoginException.class)
@@ -398,6 +426,9 @@ public class ExternalLoginModuleTest ext
         assertFalse(loginModule.login());
         root.refresh();
         assertNull(getUserManager(root).getAuthorizable("local"));
+
+        assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test(expected = LoginException.class)

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModule.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModule.java?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModule.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModule.java
 Thu Nov 28 15:37:42 2019
@@ -21,7 +21,10 @@ import java.security.Principal;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import javax.jcr.Credentials;
 import javax.security.auth.Subject;
 import javax.security.auth.callback.Callback;
@@ -191,6 +194,12 @@ public final class TokenLoginModule exte
         return false;
     }
 
+    @Override
+    public boolean logout() throws LoginException {
+        Set creds = Stream.of(tokenCredentials, 
authInfo).filter(Objects::nonNull).collect(Collectors.toSet());
+        return logout((creds.isEmpty() ? null : creds), principals);
+    }
+
     //------------------------------------------------< AbstractLoginModule 
>---
     @NotNull
     @Override

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImpl.java?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImpl.java
 Thu Nov 28 15:37:42 2019
@@ -44,7 +44,10 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * Default login module implementation that authenticates JCR {@code 
Credentials}
@@ -181,6 +184,12 @@ public final class LoginModuleImpl exten
         }
     }
 
+    @Override
+    public boolean logout() throws LoginException {
+        Set creds = Stream.of(credentials, 
authInfo).filter(Objects::nonNull).collect(Collectors.toSet());
+        return logout((creds.isEmpty() ? null : creds), principals);
+    }
+
     //------------------------------------------------< AbstractLoginModule 
>---
     @NotNull
     @Override

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestLoginModule.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestLoginModule.java?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestLoginModule.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestLoginModule.java
 Thu Nov 28 15:37:42 2019
@@ -16,23 +16,29 @@
  */
 package org.apache.jackrabbit.oak.security.authentication.token;
 
-import java.util.Map;
-import java.util.Set;
-import javax.jcr.Credentials;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.oak.api.AuthInfo;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule;
 import org.apache.jackrabbit.oak.spi.security.authentication.AuthInfoImpl;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.credentials.CredentialsSupport;
 import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
 import org.jetbrains.annotations.NotNull;
 
+import javax.jcr.Credentials;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
 public class TestLoginModule extends AbstractLoginModule {
 
     private CredentialsSupport credentialsSupport;
     private Credentials credentials;
     private String userId;
+    private AuthInfo info;
 
     @Override
     public void initialize(Subject subject, CallbackHandler callbackHandler, 
Map<String, ?> sharedState, Map<String, ?> options) {
@@ -48,6 +54,16 @@ public class TestLoginModule extends Abs
     }
 
     @Override
+    public boolean logout() throws LoginException {
+        if (credentials != null) {
+            Set<? extends Principal> s = 
Collections.singleton(EveryonePrincipal.getInstance());
+            return logout(ImmutableSet.of(credentials), s);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
     public boolean login() {
         credentials = getCredentials();
         if (credentials != null) {
@@ -64,9 +80,11 @@ public class TestLoginModule extends Abs
     public boolean commit() {
         if (userId != null) {
             subject.getPrincipals().add(EveryonePrincipal.getInstance());
-            setAuthInfo(new AuthInfoImpl(userId, 
credentialsSupport.getAttributes(credentials), subject.getPrincipals()), 
subject);
+            info = new AuthInfoImpl(userId, 
credentialsSupport.getAttributes(credentials), subject.getPrincipals());
+            setAuthInfo(info, subject);
             return true;
         } else {
+            clearState();
             return false;
         }
     }

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModuleTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModuleTest.java?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModuleTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModuleTest.java
 Thu Nov 28 15:37:42 2019
@@ -59,7 +59,6 @@ import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
@@ -196,6 +195,8 @@ public class TokenLoginModuleTest extend
         lm.initialize(new Subject(), null, ImmutableMap.<String, Object>of(), 
ImmutableMap.<String, Object>of());
 
         assertFalse(lm.login());
+        assertFalse(lm.commit());
+        assertFalse(lm.logout());
     }
 
     @Test
@@ -204,6 +205,8 @@ public class TokenLoginModuleTest extend
         lm.initialize(new Subject(), new TestCallbackHandler(null), 
ImmutableMap.<String, Object>of(), ImmutableMap.<String, Object>of());
 
         assertFalse(lm.login());
+        assertFalse(lm.commit());
+        assertFalse(lm.logout());
     }
 
 
@@ -216,6 +219,7 @@ public class TokenLoginModuleTest extend
 
         assertFalse(lm.login());
         assertFalse(lm.commit());
+        assertFalse(lm.logout());
     }
 
     @Test
@@ -226,6 +230,8 @@ public class TokenLoginModuleTest extend
         lm.initialize(new Subject(), new TestCallbackHandler(tp), 
ImmutableMap.<String, Object>of(), ImmutableMap.<String, Object>of());
 
         assertFalse(lm.login());
+        assertFalse(lm.commit());
+        assertFalse(lm.logout());
     }
 
     @Test
@@ -234,6 +240,8 @@ public class TokenLoginModuleTest extend
         lm.initialize(new Subject(), new 
ThrowingCallbackHandler(UnsupportedCallbackException.class), 
ImmutableMap.<String, Object>of(), ImmutableMap.<String, Object>of());
 
         assertFalse(lm.login());
+        assertFalse(lm.commit());
+        assertFalse(lm.logout());
     }
 
     @Test
@@ -242,6 +250,8 @@ public class TokenLoginModuleTest extend
         lm.initialize(new Subject(), new 
ThrowingCallbackHandler(IOException.class), ImmutableMap.<String, Object>of(), 
ImmutableMap.<String, Object>of());
 
         assertFalse(lm.login());
+        assertFalse(lm.commit());
+        assertFalse(lm.logout());
     }
 
     @Test(expected = LoginException.class)
@@ -261,6 +271,7 @@ public class TokenLoginModuleTest extend
             lm.commit();
         } finally {
             verify(tp, times(1)).doCreateToken(any(Credentials.class));
+            assertFalse(lm.logout());
         }
     }
 
@@ -288,6 +299,12 @@ public class TokenLoginModuleTest extend
         assertTrue(lm.commit());
 
         assertEquals(ImmutableSet.of(getTestUser().getPrincipal(), 
EveryonePrincipal.getInstance()), subject.getPrincipals());
+        assertFalse(subject.getPublicCredentials(AuthInfo.class).isEmpty());
+        
assertFalse(subject.getPublicCredentials(TokenCredentials.class).isEmpty());
+
+        assertTrue(lm.logout());
+        assertTrue(subject.getPublicCredentials().isEmpty());
+        assertTrue(subject.getPrincipals().isEmpty());
     }
 
     @Test
@@ -311,6 +328,8 @@ public class TokenLoginModuleTest extend
         assertFalse(lm.commit());
         verify(tp, times(1)).createToken(sc);
         
assertTrue(subject.getPublicCredentials(TokenCredentials.class).isEmpty());
+
+        assertFalse(lm.logout());
     }
 
     @Test
@@ -325,6 +344,7 @@ public class TokenLoginModuleTest extend
         assertFalse(lm.login());
         assertFalse(lm.commit());
         verify(tp, never()).createToken(any(Credentials.class));
+        assertFalse(lm.logout());
     }
 
     @Test
@@ -339,6 +359,7 @@ public class TokenLoginModuleTest extend
         assertFalse(lm.login());
         assertFalse(lm.commit());
         verify(tp, never()).doCreateToken(any(Credentials.class));
+        assertFalse(lm.logout());
     }
 
     @Test
@@ -365,6 +386,7 @@ public class TokenLoginModuleTest extend
         assertFalse(lm.login());
         assertFalse(lm.commit());
         verify(tp, times(1)).createToken(sc);
+        assertFalse(lm.logout());
     }
 
     @Test
@@ -391,6 +413,7 @@ public class TokenLoginModuleTest extend
         assertFalse(lm.login());
         assertFalse(lm.commit());
         verify(tp, times(1)).createToken(sc);
+        assertFalse(lm.logout());
     }
 
     @Test

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImplTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImplTest.java?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImplTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImplTest.java
 Thu Nov 28 15:37:42 2019
@@ -19,7 +19,6 @@ package org.apache.jackrabbit.oak.securi
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-import 
org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.api.security.user.UserManager;
@@ -28,8 +27,8 @@ import org.apache.jackrabbit.oak.api.Aut
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.ContentSession;
 import org.apache.jackrabbit.oak.api.Root;
-import 
org.apache.jackrabbit.oak.security.authentication.token.TokenLoginModule;
-import 
org.apache.jackrabbit.oak.security.authentication.token.TokenLoginModuleTest;
+import org.apache.jackrabbit.oak.commons.junit.LogCustomizer;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.security.internal.SecurityProviderBuilder;
 import org.apache.jackrabbit.oak.security.user.UserAuthenticationFactoryImpl;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
@@ -42,6 +41,7 @@ import org.apache.jackrabbit.oak.spi.sec
 import 
org.apache.jackrabbit.oak.spi.security.authentication.PreAuthenticatedLogin;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.callback.CredentialsCallback;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.callback.RepositoryCallback;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
 import org.apache.jackrabbit.oak.spi.security.user.UserAuthenticationFactory;
 import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
@@ -50,6 +50,7 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.junit.Test;
+import org.slf4j.event.Level;
 
 import javax.jcr.Credentials;
 import javax.jcr.GuestCredentials;
@@ -63,11 +64,13 @@ import javax.security.auth.login.Configu
 import javax.security.auth.login.LoginException;
 import java.lang.reflect.Field;
 import java.security.Principal;
+import java.security.PrivilegedExceptionAction;
 import java.util.Arrays;
-import java.util.HashMap;
+import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 
+import static 
org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule.SHARED_KEY_CREDENTIALS;
 import static 
org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule.SHARED_KEY_PRE_AUTH_LOGIN;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -76,13 +79,8 @@ import static org.junit.Assert.assertNul
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 public class LoginModuleImplTest extends AbstractSecurityTest {
@@ -292,27 +290,43 @@ public class LoginModuleImplTest extends
 
     @Test
     public void testLoginPreAuthenticated() throws Exception {
+        User testUser = getTestUser();
+        Set<? extends Principal> principals = 
getConfig(PrincipalConfiguration.class).getPrincipalProvider(root, 
NamePathMapper.DEFAULT).getPrincipals(testUser.getID());
+
         Authentication authentication = mock(Authentication.class);
         
when(authentication.authenticate(any(Credentials.class))).thenReturn(true).getMock();
-        when(authentication.getUserId()).thenReturn("uid"); // but 
getUserPrincipal returns null
+        when(authentication.getUserId()).thenReturn(testUser.getID()); // but 
getUserPrincipal returns null
+
+        Principal foreignPrincipal = new PrincipalImpl("foreign");
 
         UserAuthenticationFactory uaf = 
when(mock(UserAuthenticationFactory.class).getAuthentication(any(UserConfiguration.class),
 any(Root.class), anyString())).thenReturn(authentication).getMock();
         Map<String, Object> sharedState = Maps.newHashMap();
         sharedState.put(SHARED_KEY_PRE_AUTH_LOGIN, new 
PreAuthenticatedLogin("uid"));
 
-        Subject subject = new Subject();
+        Subject subject = new Subject(false, 
ImmutableSet.of(foreignPrincipal), ImmutableSet.of(), ImmutableSet.of());
         LoginModuleImpl lm = new LoginModuleImpl();
         lm.initialize(subject, new TestCallbackHandler(uaf), sharedState, 
Maps.newHashMap());
         assertTrue(lm.login());
         assertTrue(lm.commit());
 
-        assertTrue(subject.getPrincipals().isEmpty());
+        // verify subject has been updated with test-user principals
+        Set<Principal> expected = new 
ImmutableSet.Builder().add(foreignPrincipal).addAll(principals).build();
+        assertEquals(expected, subject.getPrincipals());
         // no other public credentials than the AuthInfo
         assertEquals(1, subject.getPublicCredentials().size());
+
         // verify AuthInfo
         Set<AuthInfo> authInfos = subject.getPublicCredentials(AuthInfo.class);
         assertFalse(authInfos.isEmpty());
-        assertEquals("uid", authInfos.iterator().next().getUserID());
+        AuthInfo ai = authInfos.iterator().next();
+        assertEquals(testUser.getID(), ai.getUserID());
+        assertEquals(expected, ai.getPrincipals());
+
+        // verify logout only removes credentials/principals associated with 
this very login module
+        assertTrue(lm.logout());
+        assertTrue(subject.getPublicCredentials().isEmpty());
+        assertTrue(subject.getPrincipals().contains(foreignPrincipal));
+        assertFalse(subject.getPrincipals().containsAll(principals));
     }
 
     @Test
@@ -332,6 +346,31 @@ public class LoginModuleImplTest extends
 
         assertTrue(subject.getPrincipals().isEmpty());
         assertTrue(subject.getPublicCredentials().isEmpty());
+
+        assertTrue(lm.logout());
+    }
+
+    @Test
+    public void testLoginWithReadOnlySubject() throws Exception {
+        Map<String, Object> sharedState = Maps.newHashMap();
+        sharedState.put(SHARED_KEY_CREDENTIALS, getAdminCredentials());
+
+        Principal unknownPrincipal = new PrincipalImpl("unknown");
+        Subject subject = new Subject(true, 
Collections.singleton(unknownPrincipal), Collections.emptySet(), 
Collections.emptySet());
+
+        LoginModuleImpl lm = new LoginModuleImpl();
+        lm.initialize(subject, new TestCallbackHandler(new 
UserAuthenticationFactoryImpl()), sharedState, Maps.newHashMap());
+
+        assertTrue(lm.login());
+        assertTrue(lm.commit());
+
+        assertFalse(subject.getPrincipals().isEmpty());
+        assertTrue(subject.getPublicCredentials().isEmpty());
+
+        assertTrue(lm.logout());
+
+        assertFalse(subject.getPrincipals().isEmpty());
+        assertTrue(subject.getPublicCredentials().isEmpty());
     }
 
     @Test
@@ -342,6 +381,7 @@ public class LoginModuleImplTest extends
 
         assertFalse(loginModule.login());
         assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test
@@ -364,6 +404,7 @@ public class LoginModuleImplTest extends
 
         assertFalse(loginModule.login());
         assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test
@@ -384,6 +425,7 @@ public class LoginModuleImplTest extends
 
         assertFalse(loginModule.login());
         assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test
@@ -404,6 +446,7 @@ public class LoginModuleImplTest extends
 
         assertFalse(loginModule.login());
         assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test
@@ -424,6 +467,7 @@ public class LoginModuleImplTest extends
 
         assertFalse(loginModule.login());
         assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test
@@ -433,6 +477,7 @@ public class LoginModuleImplTest extends
 
         assertFalse(loginModule.login());
         assertFalse(loginModule.commit());
+        assertFalse(loginModule.logout());
     }
 
     @Test
@@ -468,6 +513,10 @@ public class LoginModuleImplTest extends
         // authinfo falls back to loginId because Authentication.getUserId 
returned null
         AuthInfo authInfo = 
subject.getPublicCredentials(AuthInfo.class).iterator().next();
         assertEquals("loginId", authInfo.getUserID());
+
+        assertTrue(loginModule.logout());
+        assertTrue(subject.getPrincipals().isEmpty());
+        assertTrue(subject.getPublicCredentials().isEmpty());
     }
 
     @Test
@@ -502,6 +551,10 @@ public class LoginModuleImplTest extends
         AuthInfo authInfo = 
subject.getPublicCredentials(AuthInfo.class).iterator().next();
         assertNull(authInfo.getUserID());
         assertTrue(subject.getPrincipals().isEmpty());
+
+        assertTrue(loginModule.logout());
+        assertTrue(subject.getPublicCredentials().isEmpty());
+        assertTrue(subject.getPrincipals().isEmpty());
     }
 
     @Test
@@ -527,6 +580,28 @@ public class LoginModuleImplTest extends
         assertTrue(ai.getPrincipals().contains(getTestUser().getPrincipal()));
     }
 
+    @Test
+    public void testLoginLogoutPreexistingReadonlySubject() throws Exception {
+        createTestUser();
+        Subject subject = new Subject(true, Collections.singleton(() -> 
"JMXPrincipal: foo"), Collections.EMPTY_SET, Collections.EMPTY_SET);
+        Subject.doAs(subject, (PrivilegedExceptionAction<Void>) () -> {
+            LogCustomizer logCustomizer = LogCustomizer
+                    
.forLogger("org.apache.jackrabbit.oak.core.ContentSessionImpl")
+                    .enable(Level.ERROR)
+                    .create();
+
+            ContentSession cs = login(new SimpleCredentials(USER_ID, 
USER_PW.toCharArray()));
+            try {
+                logCustomizer.starting();
+                cs.close();
+            } finally {
+                //verify that ContentSessionImpl.close() did not log anything
+                assertEquals(0, logCustomizer.getLogs().size());
+                logCustomizer.finished();
+            }
+            return null;
+        });
+    }
 
     private class TestCallbackHandler implements CallbackHandler {
 

Modified: 
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/JaasConfigSpiTest.groovy
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/JaasConfigSpiTest.groovy?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/JaasConfigSpiTest.groovy
 (original)
+++ 
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/JaasConfigSpiTest.groovy
 Thu Nov 28 15:37:42 2019
@@ -122,5 +122,10 @@ class JaasConfigSpiTest extends Abstract
             }
             return false;
         }
+
+        @Override
+        boolean logout() throws LoginException {
+            return super.logout(ImmutableSet.of(credentials), principals);
+        }
     }
 }
\ No newline at end of file

Modified: 
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/TokenAuthenticationTest.groovy
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/TokenAuthenticationTest.groovy?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/TokenAuthenticationTest.groovy
 (original)
+++ 
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/TokenAuthenticationTest.groovy
 Thu Nov 28 15:37:42 2019
@@ -105,7 +105,12 @@ class TokenAuthenticationTest extends Ab
             if(sharedCreds instanceof SimpleCredentials) {
                 credential.setAttribute(".token", 
((SimpleCredentials)sharedCreds).getAttribute(".token"))
             }
-            return false
+            return false;
+        }
+
+        @Override
+        boolean logout() {
+            return false;
         }
     }
 

Modified: 
jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java
 Thu Nov 28 15:37:42 2019
@@ -26,6 +26,8 @@ import java.util.Map;
 import java.util.Set;
 import javax.jcr.Credentials;
 import javax.jcr.NoSuchWorkspaceException;
+import javax.security.auth.DestroyFailedException;
+import javax.security.auth.Destroyable;
 import javax.security.auth.Subject;
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
@@ -33,6 +35,7 @@ import javax.security.auth.callback.Unsu
 import javax.security.auth.login.LoginException;
 import javax.security.auth.spi.LoginModule;
 
+import com.google.common.collect.ImmutableSet;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.api.AuthInfo;
 import org.apache.jackrabbit.oak.api.ContentRepository;
@@ -185,14 +188,36 @@ public abstract class AbstractLoginModul
         this.options = (options == null) ? ConfigurationParameters.EMPTY : 
ConfigurationParameters.of(options);
     }
 
+    /**
+     * Besteffort default implementation of {@link LoginModule#logout()}, 
which removes all principals and all public
+     * credentials of type {@link Credentials} and {@link AuthInfo} from the 
subject.
+     * It will return {@code false}, if either principal set or credentials 
set is empty.
+     *
+     * Note, that this implementation is not able to only remove those 
principals/credentials that have been added
+     * by {@code this} very login module instance. Therefore subclasses should 
overwrite this method to provide a fully
+     * compliant solution of {@link #logout()}. They may however take 
advantage of {@link #logout(Set, Set)}
+     * in order to simplify the implementation of a logout that is compatible 
with the {@link LoginModule#logout()}
+     * contract incorporating the additional recommendations highlighted at
+     * <a 
href="https://docs.oracle.com/en/java/javase/13/security/java-authentication-and-authorization-service-jaas-loginmodule-developers-guide1.html#GUID-E9C5810B-ADB6-4454-869D-B269ECA8145F__LOGINMODULE.LOGOUTMETHOD-21144F6A";>JAAS
 LoginModule Dev Guide</a>
+     *
+     * @return {@code true} if neither principals nor public credentials of 
type {@link Credentials} or {@link AuthInfo}
+     * stored in the {@link Subject} are empty; {@code false} otherwise
+     * @throws {@code LoginException} if the subject is readonly and 
destroying {@link Destroyable} credentials fails
+     * with {@link DestroyFailedException}.
+     */
     @Override
-    public boolean logout() {
+    public boolean logout() throws LoginException {
         boolean success = false;
-        if (!subject.getPrincipals().isEmpty() && 
!subject.getPublicCredentials(Credentials.class).isEmpty()) {
+        Set<Object> creds = ImmutableSet.builder()
+                .addAll(subject.getPublicCredentials(Credentials.class))
+                .addAll(subject.getPublicCredentials(AuthInfo.class)).build();
+        if (!subject.getPrincipals().isEmpty() && !creds.isEmpty()) {
             // clear subject if not readonly
             if (!subject.isReadOnly()) {
                 subject.getPrincipals().clear();
-                subject.getPublicCredentials().clear();
+                subject.getPublicCredentials().removeAll(creds);
+            } else {
+                destroyCredentials(creds);
             }
             success = true;
         }
@@ -234,6 +259,51 @@ public abstract class AbstractLoginModul
     }
 
     /**
+     * General logout-helper that will return {@code false} if both {@code 
credentials} and {@code principals} are {@code null}.
+     * Note, that this implementation will only throw {@code LoginException} 
if the {@code subject} is marked readonly
+     * and destroying {@link Destroyable} credentials fails.
+     *
+     * @param credentials The set of credentials extracted by this instance 
during login/commit to be removed from {@link Subject#getPublicCredentials()}
+     * @param principals A set of principals extracted by this instance during 
login/commit to be removed from {@link Subject#getPrincipals()}
+     * @return {@code true} if either the credential set or the principal set 
is not {@code null}, {@code false} otherwise.
+     * @throws LoginException If the subject is readonly and an error occurs 
while destroying any of the given credentials.
+     * @see <a 
href="https://docs.oracle.com/en/java/javase/13/security/java-authentication-and-authorization-service-jaas-loginmodule-developers-guide1.html#GUID-E9C5810B-ADB6-4454-869D-B269ECA8145F__LOGINMODULE.LOGOUTMETHOD-21144F6A";>JAASLMDevGuide</a>
+     */
+    protected boolean logout(@Nullable Set<Object> credentials, @Nullable 
Set<? extends Principal> principals) throws LoginException {
+        if (credentials != null || principals != null) {
+            if (!subject.isReadOnly()) {
+                if (credentials != null) {
+                    subject.getPublicCredentials().removeAll(credentials);
+                }
+                if (principals != null) {
+                    subject.getPrincipals().removeAll(principals);
+                }
+            } else if (credentials != null) {
+                destroyCredentials(credentials);
+            }
+            return true;
+        } else {
+            // this login module didn't add credentials/authinfo/principals to 
the subject upon commit
+            // -> logout of this LoginModule should be ignored
+            return false;
+        }
+    }
+
+    private static void destroyCredentials(@NotNull Iterable<Object> 
credentials) throws LoginException {
+        for (Object cred : credentials) {
+            if (cred instanceof Destroyable) {
+                try {
+                    ((Destroyable) cred).destroy();
+                } catch (DestroyFailedException e) {
+                    throw new LoginException(e.getMessage());
+                }
+            } else {
+                log.debug("Unable to destroy credentials ({}) of read-only 
subject.", credentials.getClass().getName());
+            }
+        }
+    }
+
+    /**
      * @return A set of supported credential classes.
      */
     @NotNull
@@ -410,7 +480,7 @@ public abstract class AbstractLoginModul
                     });
                     root = systemSession.getLatestRoot();
                 } else {
-                    log.debug("Unable to retrieve the Root via 
RepositoryCallback; ContentRepository not available.");
+                    log.error("Unable to retrieve the Root via 
RepositoryCallback; ContentRepository not available.");
                 }
             } catch (IOException | UnsupportedCallbackException | 
PrivilegedActionException e) {
                 onError();

Modified: 
jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/GuestLoginModule.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/GuestLoginModule.java?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/GuestLoginModule.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/GuestLoginModule.java
 Thu Nov 28 15:37:42 2019
@@ -24,6 +24,7 @@ import javax.security.auth.Subject;
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
 import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginException;
 import javax.security.auth.spi.LoginModule;
 
 import 
org.apache.jackrabbit.oak.spi.security.authentication.callback.CredentialsCallback;
@@ -132,7 +133,18 @@ public final class GuestLoginModule impl
 
     @Override
     public boolean logout() {
-        return authenticationSucceeded();
+        if (authenticationSucceeded()) {
+            if (!subject.isReadOnly()) {
+                subject.getPublicCredentials().remove(guestCredentials);
+                
subject.getPrincipals().remove(EveryonePrincipal.getInstance());
+            } else {
+                log.debug("Cannot destroy GuestCredentials");
+            }
+            return true;
+        } else {
+            // ignore this login module
+            return false;
+        }
     }
 
     private boolean authenticationSucceeded() {

Modified: 
jackrabbit/oak/trunk/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModuleTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModuleTest.java?rev=1870559&r1=1870558&r2=1870559&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModuleTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModuleTest.java
 Thu Nov 28 15:37:42 2019
@@ -18,7 +18,6 @@ package org.apache.jackrabbit.oak.spi.se
 
 import java.io.IOException;
 import java.security.Principal;
-import java.security.PrivilegedActionException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -28,7 +27,10 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 
 import javax.jcr.Credentials;
+import javax.jcr.GuestCredentials;
 import javax.jcr.SimpleCredentials;
+import javax.security.auth.DestroyFailedException;
+import javax.security.auth.Destroyable;
 import javax.security.auth.Subject;
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
@@ -37,12 +39,12 @@ import javax.security.auth.login.LoginEx
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import 
org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
 import org.apache.jackrabbit.api.security.principal.PrincipalManager;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.api.AuthInfo;
 import org.apache.jackrabbit.oak.api.ContentRepository;
 import org.apache.jackrabbit.oak.api.ContentSession;
-import org.apache.jackrabbit.oak.api.Descriptors;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
@@ -66,8 +68,8 @@ import org.apache.jackrabbit.oak.stats.S
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.junit.Test;
-import org.mockito.Mockito;
 
+import static 
org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule.SHARED_KEY_CREDENTIALS;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -75,41 +77,50 @@ import static org.junit.Assert.assertNot
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
 
 public class AbstractLoginModuleTest {
 
-    private static AbstractLoginModule initLoginModule(Class<?> 
supportedCredentials, Map<String, ?> sharedState) {
+    private static AbstractLoginModule initLoginModule(@NotNull Class<?> 
supportedCredentials, @NotNull  Map<String, ?> sharedState) {
         AbstractLoginModule lm = new TestLoginModule(supportedCredentials);
-        lm.initialize(new Subject(), null, sharedState, null);
+        initialize(lm, new Subject(), null, sharedState);
         return lm;
     }
 
-    private static AbstractLoginModule initLoginModule(Class<?> 
supportedCredentials, CallbackHandler cbh) {
-        AbstractLoginModule lm = new TestLoginModule(supportedCredentials);
-        lm.initialize(new Subject(), cbh, Collections.<String, 
Object>emptyMap(), null);
+    private static AbstractLoginModule initLoginModule(@Nullable 
CallbackHandler cbh) {
+        AbstractLoginModule lm = new TestLoginModule(TestCredentials.class);
+        initialize(lm, new Subject(), cbh, Collections.emptyMap());
         return lm;
     }
 
-    private static AbstractLoginModule initLoginModule(Class<?> 
supportedCredentials, CallbackHandler cbh, LoginModuleMonitor monitor) {
-        AbstractLoginModule lm = new TestLoginModule(supportedCredentials, 
monitor);
-        lm.initialize(new Subject(), cbh, Collections.emptyMap(), null);
+    private static AbstractLoginModule initLoginModule(@NotNull 
CallbackHandler cbh, @NotNull LoginModuleMonitor monitor) {
+        AbstractLoginModule lm = new TestLoginModule(TestCredentials.class, 
monitor);
+        initialize(lm, new Subject(), cbh, Collections.emptyMap());
         return lm;
     }
 
-    private static AbstractLoginModule initLoginModule(Subject subject, 
CallbackHandler cbh, LoginModuleMonitor monitor) {
-        AbstractLoginModule lm = new TestLoginModule(TestCredentials.class, 
monitor);
-        lm.initialize(subject, cbh, Collections.emptyMap(), null);
+    private static AbstractLoginModule initLoginModule(@NotNull Subject 
subject) {
+        AbstractLoginModule lm = new TestLoginModule(TestCredentials.class, 
null);
+        initialize(lm, subject, mock(CallbackHandler.class), 
Collections.emptyMap());
         return lm;
     }
 
+    private static void initialize(@NotNull AbstractLoginModule loginModule, 
@NotNull Subject subject, @Nullable CallbackHandler cbh, @NotNull Map<String, 
?> sharedState) {
+        loginModule.initialize(subject, cbh, sharedState, null);
+    }
+
+    private static ContentRepository mockContentRepository(@Nullable 
ContentSession contentSession) throws Exception {
+        ContentSession cs = (contentSession == null) ? 
mock(ContentSession.class) : contentSession;
+        Root r = 
when(mock(Root.class).getContentSession()).thenReturn(cs).getMock();
+        when(cs.getLatestRoot()).thenReturn(r);
+        return when(mock(ContentRepository.class).login(null, 
null)).thenReturn(cs).getMock();
+    }
+
     @Test
     public void testInitializeWithOptions() {
         AbstractLoginModule lm = new TestLoginModule(TestCredentials.class);
@@ -126,16 +137,16 @@ public class AbstractLoginModuleTest {
     }
 
     @Test
-    public void testLogout() {
+    public void testLogout() throws Exception {
         AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, ImmutableMap.of());
 
         assertFalse(loginModule.logout());
     }
 
     @Test
-    public void testLogoutSuccessClearsSubject() {
+    public void testLogoutSuccessClearsSubject() throws Exception {
         Subject subject = new Subject(false, ImmutableSet.<Principal>of(new 
PrincipalImpl("pName")), ImmutableSet.of(new TestCredentials()), 
ImmutableSet.of());
-        AbstractLoginModule loginModule = initLoginModule(subject, null, null);
+        AbstractLoginModule loginModule = initLoginModule(subject);
 
         assertTrue(loginModule.logout());
 
@@ -144,9 +155,9 @@ public class AbstractLoginModuleTest {
     }
 
     @Test
-    public void testLogoutSuccessReadOnlySubject() {
+    public void testLogoutSuccessReadOnlySubject() throws Exception {
         Subject subject = new Subject(true, ImmutableSet.<Principal>of(new 
PrincipalImpl("pName")), ImmutableSet.of(new TestCredentials()), 
ImmutableSet.of());
-        AbstractLoginModule loginModule = initLoginModule(subject, null, null);
+        AbstractLoginModule loginModule = initLoginModule(subject);
 
         assertTrue(loginModule.logout());
 
@@ -155,16 +166,16 @@ public class AbstractLoginModuleTest {
     }
 
     @Test
-    public void testLogoutSubjectWithoutCredentials() {
+    public void testLogoutSubjectWithoutCredentials() throws Exception {
         Subject subject = new Subject(false, ImmutableSet.<Principal>of(new 
PrincipalImpl("pName")), ImmutableSet.of("stringNotCredentials"), 
ImmutableSet.of());
-        AbstractLoginModule loginModule = initLoginModule(subject, null, null);
+        AbstractLoginModule loginModule = initLoginModule(subject);
         loginModule.logout();
 
         assertFalse(subject.getPublicCredentials().isEmpty());
         assertFalse(subject.getPrincipals().isEmpty());
 
         subject = new Subject(false, ImmutableSet.<Principal>of(new 
PrincipalImpl("pName")), ImmutableSet.of(), ImmutableSet.of());
-        loginModule = initLoginModule(subject, null, null);
+        loginModule = initLoginModule(subject);
         loginModule.logout();
 
         assertTrue(subject.getPublicCredentials().isEmpty());
@@ -172,9 +183,9 @@ public class AbstractLoginModuleTest {
     }
 
     @Test
-    public void testLogoutSubjectWithoutPrincipals() {
+    public void testLogoutSubjectWithoutPrincipals() throws Exception {
         Subject subject = new Subject(false, ImmutableSet.<Principal>of(), 
ImmutableSet.of(new TestCredentials()), ImmutableSet.of());
-        AbstractLoginModule loginModule = initLoginModule(subject, null, null);
+        AbstractLoginModule loginModule = initLoginModule(subject);
         loginModule.logout();
 
         assertFalse(subject.getPublicCredentials().isEmpty());
@@ -182,18 +193,164 @@ public class AbstractLoginModuleTest {
     }
 
     @Test
+    public void testLogoutCPIgnored() throws LoginException {
+        AbstractLoginModule loginModule = initLoginModule(new Subject());
+        assertFalse(loginModule.logout(null, null));
+    }
+
+    @Test
+    public void testLogoutCPSuccess() throws LoginException {
+        PrincipalProvider pp = new TestPrincipalProvider("user");
+        Principal p = pp.getPrincipal("user");
+        Principal foreignPrincipal = TestPrincipalProvider.UNKNOWN;
+
+        String userId = TestPrincipalProvider.getIDFromPrincipal(p);
+        Set<? extends Principal> principals = pp.getPrincipals(userId);
+        Set<Principal> all = 
ImmutableSet.<Principal>builder().add(p).add(foreignPrincipal).addAll(principals).build();
+
+
+        AuthInfo authInfo = new AuthInfoImpl(userId, null, all);
+        Credentials foreign1 = new GuestCredentials();
+        Credentials foreign2 = new TokenCredentials("token");
+
+        Subject subject = new Subject(false,
+                ImmutableSet.of(foreignPrincipal, p),
+                ImmutableSet.of(authInfo, foreign1, foreign2), 
ImmutableSet.of());
+
+        TestLoginModule loginModule = new 
TestLoginModule(SimpleCredentials.class);
+        loginModule.initialize(subject, new TestCallbackHandler(pp), 
Collections.emptyMap(), null);
+
+        assertTrue(loginModule.logout(ImmutableSet.of(authInfo), principals));
+
+        Set publicCreds = subject.getPublicCredentials();
+        assertFalse(publicCreds.contains(authInfo));
+        assertTrue(publicCreds.contains(foreign1));
+        assertTrue(publicCreds.contains(foreign2));
+
+        assertFalse(subject.getPrincipals().contains(p));
+        assertTrue(Collections.disjoint(subject.getPrincipals(), principals));
+        assertTrue(subject.getPrincipals().contains(foreignPrincipal));
+    }
+
+    @Test
+    public void testLogoutCPDestroyable() throws Exception {
+        Credentials creds = mock(TestCredentials.class, 
withSettings().extraInterfaces(Destroyable.class));
+        Credentials foreign1 = new GuestCredentials();
+        Credentials foreign2 = new TokenCredentials("token");
+
+        Subject subject = new Subject(true,
+                ImmutableSet.<Principal>of(new PrincipalImpl("pName")),
+                ImmutableSet.of(creds, foreign1, foreign2), ImmutableSet.of());
+
+        AbstractLoginModule loginModule = initLoginModule(subject);
+        assertTrue(loginModule.logout(ImmutableSet.of(creds), 
Collections.emptySet()));
+
+        verify(((Destroyable) creds), times(1)).destroy();
+    }
+
+    @Test(expected = LoginException.class)
+    public void testLogoutCPDestroyFails() throws Exception {
+        Credentials creds = mock(TestCredentials.class, 
withSettings().extraInterfaces(Destroyable.class));
+        doThrow(new 
DestroyFailedException()).when((Destroyable)creds).destroy();
+
+        Subject subject = new Subject(true, ImmutableSet.of(), 
ImmutableSet.of(creds), ImmutableSet.of());
+        AbstractLoginModule loginModule = initLoginModule(subject);
+        loginModule.logout(ImmutableSet.of(creds), null);
+    }
+
+    @Test
+    public void testLogoutCPNotDestroyable() throws LoginException {
+        Credentials creds = new TestCredentials();
+        Subject subject = new Subject(true,
+                ImmutableSet.of(),
+                ImmutableSet.of(creds), ImmutableSet.of());
+
+        TestLoginModule loginModule = (TestLoginModule) 
initLoginModule(subject);
+        assertTrue(loginModule.logout(null, Collections.emptySet()));
+    }
+
+    @Test
+    public void testLogoutCPNullParams() throws LoginException {
+        Credentials creds = new TestCredentials();
+        Principal unknownPrincipal = TestPrincipalProvider.UNKNOWN;
+        TestPrincipalProvider pp = new TestPrincipalProvider("principal");
+
+        Subject subject = new Subject(false, 
ImmutableSet.of(unknownPrincipal), ImmutableSet.of(creds), ImmutableSet.of());
+
+        TestLoginModule loginModule = new 
TestLoginModule(SimpleCredentials.class);
+        loginModule.initialize(subject, new TestCallbackHandler(pp), 
Collections.emptyMap(), null);
+
+        assertFalse(loginModule.logout(null, null));
+        // subject must not be altered by logout
+        assertTrue(subject.getPublicCredentials().contains(creds));
+        assertTrue(subject.getPrincipals().contains(unknownPrincipal));
+    }
+
+    @Test
+    public void testLogoutCPMissingCredentials() throws LoginException {
+        Credentials creds = new TestCredentials();
+        Principal unknownPrincipal = TestPrincipalProvider.UNKNOWN;
+        TestPrincipalProvider pp = new TestPrincipalProvider("principal");
+        Principal p = pp.getPrincipal("principal");
+
+        Subject subject = new Subject(false, ImmutableSet.of(unknownPrincipal, 
p), ImmutableSet.of(creds), ImmutableSet.of());
+
+        TestLoginModule loginModule = new 
TestLoginModule(SimpleCredentials.class);
+        loginModule.initialize(subject, new TestCallbackHandler(pp), 
Collections.emptyMap(), null);
+
+        assertTrue(loginModule.logout(null, ImmutableSet.of(p)));
+
+        // only credentials/principals passed to logout must be removed
+        assertTrue(subject.getPublicCredentials().contains(creds));
+        assertTrue(subject.getPrincipals().contains(unknownPrincipal));
+        assertFalse(subject.getPrincipals().contains(p));
+    }
+
+    @Test
+    public void testLogoutCPMissingCredentialsReadOnly() throws LoginException 
{
+        Credentials creds = new TestCredentials();
+        Principal unknownPrincipal = TestPrincipalProvider.UNKNOWN;
+        TestPrincipalProvider pp = new TestPrincipalProvider("principal");
+        Principal p = pp.getPrincipal("principal");
+
+        Set<? extends Principal> principals = 
ImmutableSet.of(unknownPrincipal, p);
+        AuthInfo authInfo = new AuthInfoImpl(null, null, principals);
+        Subject subject = new Subject(true, principals, ImmutableSet.of(creds, 
authInfo), ImmutableSet.of());
+
+        TestLoginModule loginModule = new 
TestLoginModule(SimpleCredentials.class);
+        loginModule.initialize(subject, new TestCallbackHandler(pp), 
Collections.emptyMap(), null);
+
+        assertTrue(loginModule.logout(ImmutableSet.of(creds, authInfo), 
Collections.singleton(p)));
+        // read-only subject must not be altered by logout
+        assertTrue(subject.getPublicCredentials().contains(creds));
+        assertTrue(subject.getPrincipals().contains(unknownPrincipal));
+        assertTrue(subject.getPrincipals().contains(p));
+    }
+
+    @Test
+    public void testLogoutCPMissingPrincipals() throws LoginException {
+        Credentials creds = new TestCredentials();
+        Principal preExisting = new PrincipalImpl("pName");
+
+        Subject subject = new Subject(false, ImmutableSet.of(preExisting), 
ImmutableSet.of(creds), ImmutableSet.of());
+        TestLoginModule loginModule = (TestLoginModule) 
initLoginModule(subject);
+        assertTrue(loginModule.logout(ImmutableSet.of(creds), null));
+
+        assertFalse(subject.getPublicCredentials().contains(creds));
+        assertTrue(subject.getPrincipals().contains(preExisting));
+    }
+
+    @Test
     public void testAbort() throws LoginException {
         AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, ImmutableMap.of());
 
         assertTrue(loginModule.abort());
-
-        loginModule.login();
-        assertTrue(loginModule.abort());
     }
 
     @Test
-    public void testAbortWithFailedSystemLogout() throws LoginException {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new TestCallbackHandler(new 
TestContentRepository(), null, null));
+    public void testAbortWithFailedSystemLogout() throws Exception {
+        ContentRepository cr = mockContentRepository(null);
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler(cr, null));
 
         // trigger creation of system-session
         loginModule.getRoot();
@@ -202,29 +359,31 @@ public class AbstractLoginModuleTest {
 
     @Test
     public void testClearStateWithSessionCloseFailing() throws Exception {
-        TestContentRepository cr = new TestContentRepository();
-        doThrow(IOException.class).when(cr.cs).close();
+        ContentSession cs = mock(ContentSession.class);
+        ContentRepository cr = mockContentRepository(cs);
+        doThrow(IOException.class).when(cs).close();
 
         LoginModuleStats stats = newLoginModuleStats();
-        CallbackHandler cbh = new TestCallbackHandler(cr, 
mock(SecurityProvider.class), null);
+        CallbackHandler cbh = new TestCallbackHandler(cr, 
mock(SecurityProvider.class));
 
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, cbh, stats);
+        AbstractLoginModule loginModule = initLoginModule(cbh, stats);
         loginModule.getRoot();
         loginModule.clearState();
         assertEquals(1, stats.getLoginErrors());
-        verify(cr.cs, times(1)).close();
+        verify(cs, times(1)).close();
     }
 
     @Test
     public void testCloseSystemSession() throws Exception {
-        TestContentRepository cr = new TestContentRepository();
+        ContentSession cs = mock(ContentSession.class);
+        ContentRepository cr = mockContentRepository(cs);
 
-        CallbackHandler cbh = new TestCallbackHandler(cr, 
mock(SecurityProvider.class), null);
+        CallbackHandler cbh = new TestCallbackHandler(cr, 
mock(SecurityProvider.class));
 
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, cbh);
+        AbstractLoginModule loginModule = initLoginModule(cbh);
         loginModule.getRoot();
         loginModule.closeSystemSession();
-        verify(cr.cs, times(1)).close();
+        verify(cs, times(1)).close();
     }
 
     @Test
@@ -244,18 +403,18 @@ public class AbstractLoginModuleTest {
     public void testGetSharedCredentials() {
         Map<String, Object> sharedState = new HashMap<>();
 
-        sharedState.put(AbstractLoginModule.SHARED_KEY_CREDENTIALS, new 
TestCredentials());
+        sharedState.put(SHARED_KEY_CREDENTIALS, new TestCredentials());
         AbstractLoginModule lm = initLoginModule(TestCredentials.class, 
sharedState);
         assertTrue(lm.getSharedCredentials() instanceof TestCredentials);
 
-        sharedState.put(AbstractLoginModule.SHARED_KEY_CREDENTIALS, new 
SimpleCredentials("test", "test".toCharArray()));
+        sharedState.put(SHARED_KEY_CREDENTIALS, new SimpleCredentials("test", 
"test".toCharArray()));
         lm = initLoginModule(TestCredentials.class, sharedState);
         assertTrue(lm.getSharedCredentials() instanceof SimpleCredentials);
 
         lm = initLoginModule(SimpleCredentials.class, sharedState);
         assertTrue(lm.getSharedCredentials() instanceof SimpleCredentials);
 
-        sharedState.put(AbstractLoginModule.SHARED_KEY_CREDENTIALS, "no 
credentials object");
+        sharedState.put(SHARED_KEY_CREDENTIALS, "no credentials object");
         lm = initLoginModule(TestCredentials.class, sharedState);
         assertNull(lm.getSharedCredentials());
 
@@ -267,17 +426,17 @@ public class AbstractLoginModuleTest {
     @Test
     public void testGetCredentialsFromSharedState() {
         Map<String, Credentials> sharedState = new HashMap<>();
+        sharedState.put(SHARED_KEY_CREDENTIALS, new TestCredentials());
 
-        sharedState.put(AbstractLoginModule.SHARED_KEY_CREDENTIALS, new 
TestCredentials());
         AbstractLoginModule lm = initLoginModule(TestCredentials.class, 
sharedState);
         assertTrue(lm.getCredentials() instanceof TestCredentials);
 
         SimpleCredentials sc = new SimpleCredentials("test", 
"test".toCharArray());
-        sharedState.put(AbstractLoginModule.SHARED_KEY_CREDENTIALS, sc);
+        sharedState.put(SHARED_KEY_CREDENTIALS, sc);
         lm = initLoginModule(TestCredentials.class, sharedState);
         assertNull(lm.getCredentials());
 
-        sharedState.put(AbstractLoginModule.SHARED_KEY_CREDENTIALS, sc);
+        sharedState.put(SHARED_KEY_CREDENTIALS, sc);
         lm = initLoginModule(SimpleCredentials.class, sharedState);
         assertTrue(lm.getCredentials() instanceof SimpleCredentials);
 
@@ -289,8 +448,6 @@ public class AbstractLoginModuleTest {
     @Test
     public void testGetCredentialsFromSubject() {
         Subject subject = new Subject();
-
-
         subject.getPublicCredentials().add(new TestCredentials());
 
         AbstractLoginModule lm = new TestLoginModule(TestCredentials.class);
@@ -305,7 +462,7 @@ public class AbstractLoginModuleTest {
         subject.getPublicCredentials().add(new SimpleCredentials("userid", new 
char[0]));
 
         AbstractLoginModule lm = new TestLoginModule(TestCredentials.class);
-        lm.initialize(subject, null, ImmutableMap.<String, Object>of(), null);
+        lm.initialize(subject, null, Collections.emptyMap(), null);
 
         assertNull(lm.getCredentials());
     }
@@ -320,17 +477,18 @@ public class AbstractLoginModuleTest {
             }
         };
 
-        AbstractLoginModule lm = initLoginModule(TestCredentials.class, cbh);
+        AbstractLoginModule lm = initLoginModule(cbh);
         assertTrue(lm.getCredentials() instanceof TestCredentials);
 
-        lm = initLoginModule(SimpleCredentials.class, cbh);
+        lm = new TestLoginModule(SimpleCredentials.class);
+        lm.initialize(new Subject(), cbh, Collections.emptyMap(), null);
         assertNull(lm.getCredentials());
     }
 
     @Test
     public void testGetCredentialsIOException() {
         LoginModuleMonitor monitor = mock(LoginModuleMonitor.class);
-        AbstractLoginModule lm = initLoginModule(TestCredentials.class, new 
ThrowingCallbackHandler(true), monitor);
+        AbstractLoginModule lm = initLoginModule(new 
ThrowingCallbackHandler(true), monitor);
         assertNull(lm.getCredentials());
         verify(monitor, times(1)).loginError();
     }
@@ -338,7 +496,7 @@ public class AbstractLoginModuleTest {
     @Test
     public void testGetCredentialsUnsupportedCallbackException() {
         LoginModuleMonitor monitor = mock(LoginModuleMonitor.class);
-        AbstractLoginModule lm = initLoginModule(TestCredentials.class, new 
ThrowingCallbackHandler(false), monitor);
+        AbstractLoginModule lm = initLoginModule(new 
ThrowingCallbackHandler(false), monitor);
         assertNull(lm.getCredentials());
         verify(monitor, times(1)).loginError();
     }
@@ -352,7 +510,7 @@ public class AbstractLoginModuleTest {
                 }
             }
         };
-        AbstractLoginModule lm = initLoginModule(TestCredentials.class, cbh);
+        AbstractLoginModule lm = initLoginModule(cbh);
         assertNull(lm.getCredentials());
     }
 
@@ -385,15 +543,15 @@ public class AbstractLoginModuleTest {
 
     @Test
     public void testIncompleteRepositoryCallback() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new TestCallbackHandler());
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler());
 
         assertNull(loginModule.getSecurityProvider());
         assertNull(loginModule.getRoot());
     }
 
     @Test
-    public void testGetRoot() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new TestCallbackHandler(new 
TestContentRepository(), null, null));
+    public void testGetRoot() throws Exception {
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler(mockContentRepository(null), null));
 
         Root root = loginModule.getRoot();
         assertNotNull(root);
@@ -410,7 +568,7 @@ public class AbstractLoginModuleTest {
     @Test
     public void testGetRootIOException() {
         LoginModuleStats stats = newLoginModuleStats();
-        AbstractLoginModule lm = initLoginModule(TestCredentials.class, new 
ThrowingCallbackHandler(true), stats);
+        AbstractLoginModule lm = initLoginModule(new 
ThrowingCallbackHandler(true), stats);
         assertNull(lm.getRoot());
         assertEquals(1, stats.getLoginErrors());
     }
@@ -418,20 +576,20 @@ public class AbstractLoginModuleTest {
     @Test
     public void testGetRootUnsupportedCallbackException() {
         LoginModuleStats stats = newLoginModuleStats();
-        AbstractLoginModule lm = initLoginModule(TestCredentials.class, new 
ThrowingCallbackHandler(false), stats);
+        AbstractLoginModule lm = initLoginModule(new 
ThrowingCallbackHandler(false), stats);
         assertNull(lm.getRoot());
         assertEquals(1, stats.getLoginErrors());
     }
 
     @Test
     public void testGetRootMissingCallbackHandler() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, null, null);
+        AbstractLoginModule loginModule = initLoginModule((CallbackHandler) 
null);
         assertNull(loginModule.getRoot());
     }
 
     @Test
     public void testGetSecurityProvider() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new TestCallbackHandler(null, new 
OpenSecurityProvider(), null));
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler(null, new OpenSecurityProvider()));
 
         SecurityProvider securityProvider = loginModule.getSecurityProvider();
         assertNotNull(securityProvider);
@@ -441,27 +599,27 @@ public class AbstractLoginModuleTest {
 
     @Test
     public void testGetSecurityProviderIOException() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new ThrowingCallbackHandler(true));
+        AbstractLoginModule loginModule = initLoginModule(new 
ThrowingCallbackHandler(true));
 
         assertNull(loginModule.getSecurityProvider());
     }
 
     @Test
     public void testGetSecurityProviderUnsupportedCallbackException() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new ThrowingCallbackHandler(false));
+        AbstractLoginModule loginModule = initLoginModule(new 
ThrowingCallbackHandler(false));
 
         assertNull(loginModule.getRoot());
     }
 
     @Test
     public void testGetSecurityProviderMissingCallbackHandler() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, null, null);
+        AbstractLoginModule loginModule = initLoginModule((CallbackHandler) 
null);
         assertNull(loginModule.getSecurityProvider());
     }
 
     @Test
     public void testGetWhiteboardFromCallback() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new TestCallbackHandler(new 
DefaultWhiteboard()));
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler(new DefaultWhiteboard()));
 
         Whiteboard wb = loginModule.getWhiteboard();
         assertNotNull(wb);
@@ -472,34 +630,34 @@ public class AbstractLoginModuleTest {
 
     @Test
     public void testGetWhiteboardFromIncompleteCallback() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new TestCallbackHandler());
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler());
 
         assertNull(loginModule.getWhiteboard());
     }
 
     @Test
     public void testGetWhiteboardIOException() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new ThrowingCallbackHandler(true));
+        AbstractLoginModule loginModule = initLoginModule(new 
ThrowingCallbackHandler(true));
 
         assertNull(loginModule.getWhiteboard());
     }
 
     @Test
     public void testGetWhiteboardUnsupportedCallbackException() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new ThrowingCallbackHandler(false));
+        AbstractLoginModule loginModule = initLoginModule(new 
ThrowingCallbackHandler(false));
 
         assertNull(loginModule.getWhiteboard());
     }
 
     @Test
     public void testGetWhiteBoardMissingCallbackHandler() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, null, null);
+        AbstractLoginModule loginModule = initLoginModule((CallbackHandler) 
null);
         assertNull(loginModule.getWhiteboard());
     }
 
     @Test
     public void testGetUserManagerFromCallback() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new 
TestCallbackHandler(mock(UserManager.class)));
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler(mock(UserManager.class)));
 
         UserManager userManager = loginModule.getUserManager();
         assertNotNull(userManager);
@@ -509,21 +667,21 @@ public class AbstractLoginModuleTest {
 
     @Test
     public void testGetUserManagerFromIncompleteCallback() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new TestCallbackHandler());
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler());
 
         assertNull(loginModule.getUserManager());
     }
 
     @Test
     public void testGetUserManagerIOException() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new ThrowingCallbackHandler(true));
+        AbstractLoginModule loginModule = initLoginModule(new 
ThrowingCallbackHandler(true));
 
         assertNull(loginModule.getUserManager());
     }
 
     @Test
     public void testGetUserManagerUnsupportedCallbackException() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new ThrowingCallbackHandler(false));
+        AbstractLoginModule loginModule = initLoginModule(new 
ThrowingCallbackHandler(false));
 
         assertNull(loginModule.getUserManager());
     }
@@ -537,14 +695,14 @@ public class AbstractLoginModuleTest {
         UserConfiguration uc = 
when(mock(UserConfiguration.class).getUserManager(r, 
NamePathMapper.DEFAULT)).thenReturn(um).getMock();
         SecurityProvider sp = 
when(mock(SecurityProvider.class).getConfiguration(UserConfiguration.class)).thenReturn(uc).getMock();
 
-        CallbackHandler cbh = new TestCallbackHandler(cp, sp, null);
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, cbh, null);
+        CallbackHandler cbh = new TestCallbackHandler(cp, sp);
+        AbstractLoginModule loginModule = initLoginModule(cbh);
         assertEquals(um, loginModule.getUserManager());
     }
 
     @Test
     public void testGetUserManagerMissingCallbackHandler() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, null, null);
+        AbstractLoginModule loginModule = initLoginModule((CallbackHandler) 
null);
         assertNull(loginModule.getUserManager());
     }
 
@@ -552,8 +710,8 @@ public class AbstractLoginModuleTest {
     public void testGetUserManagerMissingRoot() throws Exception {
         ContentRepository cp = when(mock(ContentRepository.class).login(null, 
null)).thenReturn(mock(ContentSession.class)).getMock();
 
-        CallbackHandler cbh = new TestCallbackHandler(cp, 
mock(SecurityProvider.class), null);
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, cbh, null);
+        CallbackHandler cbh = new TestCallbackHandler(cp, 
mock(SecurityProvider.class));
+        AbstractLoginModule loginModule = initLoginModule(cbh);
         assertNull(loginModule.getUserManager());
     }
 
@@ -562,14 +720,14 @@ public class AbstractLoginModuleTest {
         ContentSession cs = 
when(mock(ContentSession.class).getLatestRoot()).thenReturn(mock(Root.class)).getMock();
         ContentRepository cp = when(mock(ContentRepository.class).login(null, 
null)).thenReturn(cs).getMock();
 
-        CallbackHandler cbh = new TestCallbackHandler(cp, null, null);
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, cbh, null);
+        CallbackHandler cbh = new TestCallbackHandler(cp, null);
+        AbstractLoginModule loginModule = initLoginModule(cbh);
         assertNull(loginModule.getUserManager());
     }
 
     @Test
     public void testGetPrincipalProviderFromCallback() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new TestCallbackHandler(new 
TestPrincipalProvider()));
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler(new TestPrincipalProvider()));
 
         assertNotNull(loginModule.getPrincipalProvider());
 
@@ -581,21 +739,21 @@ public class AbstractLoginModuleTest {
 
     @Test
     public void testGetPrincipalProviderFromIncompleteCallback() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new TestCallbackHandler());
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler());
 
         assertNull(loginModule.getPrincipalProvider());
     }
 
     @Test
     public void testGetPrincipalProviderIOException() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new ThrowingCallbackHandler(true));
+        AbstractLoginModule loginModule = initLoginModule(new 
ThrowingCallbackHandler(true));
 
         assertNull(loginModule.getPrincipalProvider());
     }
 
     @Test
     public void testGetPrincipalProviderUnsupportedCallbackException() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new ThrowingCallbackHandler(false));
+        AbstractLoginModule loginModule = initLoginModule(new 
ThrowingCallbackHandler(false));
 
         assertNull(loginModule.getPrincipalProvider());
     }
@@ -609,14 +767,14 @@ public class AbstractLoginModuleTest {
         PrincipalConfiguration pc = 
when(mock(PrincipalConfiguration.class).getPrincipalProvider(r, 
NamePathMapper.DEFAULT)).thenReturn(pp).getMock();
         SecurityProvider sp = 
when(mock(SecurityProvider.class).getConfiguration(PrincipalConfiguration.class)).thenReturn(pc).getMock();
 
-        CallbackHandler cbh = new TestCallbackHandler(cp, sp, null);
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, cbh, null);
+        CallbackHandler cbh = new TestCallbackHandler(cp, sp);
+        AbstractLoginModule loginModule = initLoginModule(cbh);
         assertEquals(pp, loginModule.getPrincipalProvider());
     }
 
     @Test
     public void testGetPrincipalProviderMissingCallbackHandler() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, null, null);
+        AbstractLoginModule loginModule = initLoginModule((CallbackHandler) 
null);
         assertNull(loginModule.getPrincipalProvider());
     }
 
@@ -624,8 +782,8 @@ public class AbstractLoginModuleTest {
     public void testGetPrincipalProviderMissingRoot() throws Exception {
         ContentRepository cp = when(mock(ContentRepository.class).login(null, 
null)).thenReturn(mock(ContentSession.class)).getMock();
 
-        CallbackHandler cbh = new TestCallbackHandler(cp, 
mock(SecurityProvider.class), null);
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, cbh, null);
+        CallbackHandler cbh = new TestCallbackHandler(cp, 
mock(SecurityProvider.class));
+        AbstractLoginModule loginModule = initLoginModule(cbh);
         assertNull(loginModule.getPrincipalProvider());
     }
 
@@ -634,8 +792,8 @@ public class AbstractLoginModuleTest {
         ContentSession cs = 
when(mock(ContentSession.class).getLatestRoot()).thenReturn(mock(Root.class)).getMock();
         ContentRepository cp = when(mock(ContentRepository.class).login(null, 
null)).thenReturn(cs).getMock();
 
-        CallbackHandler cbh = new TestCallbackHandler(cp, null, null);
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, cbh, null);
+        CallbackHandler cbh = new TestCallbackHandler(cp, null);
+        AbstractLoginModule loginModule = initLoginModule(cbh);
         assertNull(loginModule.getPrincipalProvider());
     }
 
@@ -643,7 +801,7 @@ public class AbstractLoginModuleTest {
     public void testGetPrincipals() {
         PrincipalProvider principalProvider = new TestPrincipalProvider();
 
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new 
TestCallbackHandler(principalProvider));
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler(principalProvider));
 
         Principal principal = 
principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_NOT_GROUP).next();
         String userId = TestPrincipalProvider.getIDFromPrincipal(principal);
@@ -656,7 +814,7 @@ public class AbstractLoginModuleTest {
 
     @Test
     public void testGetPrincipalsMissingProvider() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new TestCallbackHandler());
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler());
 
         Set<? extends Principal> principals = 
loginModule.getPrincipals("userId");
         assertTrue(principals.isEmpty());
@@ -666,7 +824,7 @@ public class AbstractLoginModuleTest {
     public void testGetPrincipalsFromPrincipal() {
         PrincipalProvider principalProvider = new TestPrincipalProvider();
 
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new 
TestCallbackHandler(principalProvider));
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler(principalProvider));
 
         Principal principal = 
principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_NOT_GROUP).next();
         Set<Principal> expected = new HashSet<>();
@@ -681,7 +839,7 @@ public class AbstractLoginModuleTest {
 
     @Test
     public void testGetPrincipalsFromPrincipalMissingProvider() {
-        AbstractLoginModule loginModule = 
initLoginModule(TestCredentials.class, new TestCallbackHandler());
+        AbstractLoginModule loginModule = initLoginModule(new 
TestCallbackHandler());
 
         Set<? extends Principal> principals = loginModule.getPrincipals(new 
PrincipalImpl("principalName"));
         assertTrue(principals.isEmpty());
@@ -722,7 +880,7 @@ public class AbstractLoginModuleTest {
             }
         };
 
-        AbstractLoginModule lm = initLoginModule(TestCredentials.class, cbh, 
null);
+        AbstractLoginModule lm = initLoginModule(cbh);
         assertTrue(lm.getLoginModuleMonitor() instanceof LoginModuleStats);
         lm.onError();
         assertEquals(1, stats.getLoginErrors());
@@ -731,7 +889,7 @@ public class AbstractLoginModuleTest {
     @Test
     public void testNullLoginModuleMonitor() {
         LoginModuleStats stats = newLoginModuleStats();
-        AbstractLoginModule lm = initLoginModule(TestCredentials.class, null, 
null);
+        AbstractLoginModule lm = initLoginModule((CallbackHandler) null);
         assertNull(lm.getLoginModuleMonitor());
         lm.onError();
         assertEquals(0, stats.getLoginErrors());
@@ -740,7 +898,7 @@ public class AbstractLoginModuleTest {
     @Test
     public void testErrorOnGetLoginModuleMonitor() {
         LoginModuleStats stats = newLoginModuleStats();
-        AbstractLoginModule lm = initLoginModule(TestCredentials.class, new 
ThrowingCallbackHandler(true), null);
+        AbstractLoginModule lm = initLoginModule(new 
ThrowingCallbackHandler(true));
 
         assertNull(lm.getLoginModuleMonitor());
         lm.onError();
@@ -749,19 +907,18 @@ public class AbstractLoginModuleTest {
 
     
//--------------------------------------------------------------------------
 
-    private final class TestCredentials implements Credentials {}
+    private class TestCredentials implements Credentials {}
 
     private static final class TestLoginModule extends AbstractLoginModule {
 
         private final Class supportedCredentialsClass;
+        private final LoginModuleMonitor mon;
 
-        private LoginModuleMonitor mon;
-
-        private TestLoginModule(Class supportedCredentialsClass) {
-            this.supportedCredentialsClass = supportedCredentialsClass;
+        private TestLoginModule(@NotNull Class supportedCredentialsClass) {
+            this(supportedCredentialsClass, null);
         }
 
-        private TestLoginModule(Class supportedCredentialsClass, 
LoginModuleMonitor mon) {
+        private TestLoginModule(@NotNull Class supportedCredentialsClass, 
@Nullable LoginModuleMonitor mon) {
             this.supportedCredentialsClass = supportedCredentialsClass;
             this.mon = mon;
         }
@@ -774,12 +931,12 @@ public class AbstractLoginModuleTest {
 
         @Override
         public boolean login() {
-            return true;
+            throw new UnsupportedOperationException();
         }
 
         @Override
         public boolean commit() {
-            return true;
+            throw new UnsupportedOperationException();
         }
 
         @Override
@@ -799,7 +956,6 @@ public class AbstractLoginModuleTest {
         private PrincipalProvider principalProvider = null;
         private ContentRepository contentRepository = null;
         private SecurityProvider securityProvider = null;
-        private String workspaceName = null;
 
         private TestCallbackHandler() {
         }
@@ -816,7 +972,7 @@ public class AbstractLoginModuleTest {
             this.principalProvider = principalProvider;
         }
 
-        private TestCallbackHandler(@Nullable ContentRepository 
contentRepository, @Nullable SecurityProvider securityProvider, @Nullable 
String workspaceName) {
+        private TestCallbackHandler(@Nullable ContentRepository 
contentRepository, @Nullable SecurityProvider securityProvider) {
             this.contentRepository = contentRepository;
             this.securityProvider = securityProvider;
         }
@@ -836,33 +992,11 @@ public class AbstractLoginModuleTest {
                     RepositoryCallback rcb = (RepositoryCallback) cb;
                     rcb.setContentRepository(contentRepository);
                     rcb.setSecurityProvider(securityProvider);
-                    rcb.setWorkspaceName(workspaceName);
                     rcb.setLoginModuleMonitor(LoginModuleMonitor.NOOP);
                 } else {
                     throw new UnsupportedCallbackException(cb);
                 }
             }
-
-        }
-
-    }
-
-    private final class TestContentRepository implements ContentRepository {
-
-        private ContentSession cs = Mockito.mock(ContentSession.class);
-
-        @NotNull
-        @Override
-        public ContentSession login(@Nullable Credentials credentials, 
@Nullable String workspaceName) {
-            
Mockito.when(cs.getLatestRoot()).thenReturn(Mockito.mock(Root.class));
-            return cs;
-
-        }
-
-        @NotNull
-        @Override
-        public Descriptors getDescriptors() {
-            throw new UnsupportedOperationException();
         }
     }
 }


Reply via email to