blob: fd14c6016aa5377fb4c99621b223166cd520b94b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.spi.security.authentication;
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.Root;
import org.apache.jackrabbit.oak.namepath.NamePathMapper;
import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
import org.apache.jackrabbit.oak.spi.security.authentication.callback.CredentialsCallback;
import org.apache.jackrabbit.oak.spi.security.authentication.callback.PrincipalProviderCallback;
import org.apache.jackrabbit.oak.spi.security.authentication.callback.RepositoryCallback;
import org.apache.jackrabbit.oak.spi.security.authentication.callback.SecurityProviderCallback;
import org.apache.jackrabbit.oak.spi.security.authentication.callback.UserManagerCallback;
import org.apache.jackrabbit.oak.spi.security.authentication.callback.WhiteboardCallback;
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.principal.PrincipalProvider;
import org.apache.jackrabbit.oak.spi.security.principal.TestPrincipalProvider;
import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
import org.apache.jackrabbit.oak.spi.whiteboard.DefaultWhiteboard;
import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.Test;
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;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import java.io.IOException;
import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
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;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
public class AbstractLoginModuleTest {
private final LoginModuleMonitor monitor = mock(LoginModuleMonitor.class);
private static AbstractLoginModule initLoginModule(@NotNull Class<?> supportedCredentials, @NotNull Map<String, ?> sharedState) {
AbstractLoginModule lm = new TestLoginModule(supportedCredentials);
initialize(lm, new Subject(), null, sharedState);
return lm;
}
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(@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(@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);
Map<String, String> options = ImmutableMap.of("key", "value");
lm.initialize(new Subject(), null, Collections.emptyMap(), options);
assertNotSame(options, lm.options);
assertEquals(options, lm.options);
ConfigurationParameters options2 = ConfigurationParameters.of(options);
lm.initialize(new Subject(), null, Collections.emptyMap(), options2);
assertSame(options2, lm.options);
}
@Test
public void testLogout() throws Exception {
AbstractLoginModule loginModule = initLoginModule(TestCredentials.class, ImmutableMap.of());
assertFalse(loginModule.logout());
}
@Test
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);
assertTrue(loginModule.logout());
assertTrue(subject.getPublicCredentials().isEmpty());
assertTrue(subject.getPrincipals().isEmpty());
}
@Test
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);
assertTrue(loginModule.logout());
assertFalse(subject.getPublicCredentials().isEmpty());
assertFalse(subject.getPrincipals().isEmpty());
}
@Test
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);
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);
loginModule.logout();
assertTrue(subject.getPublicCredentials().isEmpty());
assertFalse(subject.getPrincipals().isEmpty());
}
@Test
public void testLogoutSubjectWithoutPrincipals() throws Exception {
Subject subject = new Subject(false, ImmutableSet.of(), ImmutableSet.of(new TestCredentials()), ImmutableSet.of());
AbstractLoginModule loginModule = initLoginModule(subject);
loginModule.logout();
assertFalse(subject.getPublicCredentials().isEmpty());
assertTrue(subject.getPrincipals().isEmpty());
}
@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");
assertNotNull(p);
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<Object> 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");
assertNotNull(p);
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());
}
@Test
public void testAbortWithFailedSystemLogout() throws Exception {
ContentRepository cr = mockContentRepository(null);
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler(cr, null));
// trigger creation of system-session
loginModule.getRoot();
assertTrue(loginModule.abort());
}
@Test
public void testClearStateWithSessionCloseFailing() throws Exception {
ContentSession cs = mock(ContentSession.class);
ContentRepository cr = mockContentRepository(cs);
doThrow(IOException.class).when(cs).close();
CallbackHandler cbh = new TestCallbackHandler(cr, mock(SecurityProvider.class));
AbstractLoginModule loginModule = initLoginModule(cbh, monitor);
loginModule.getRoot();
loginModule.clearState();
verify(monitor).loginError();
verifyNoMoreInteractions(monitor);
verify(cs, times(1)).close();
}
@Test
public void testCloseSystemSession() throws Exception {
ContentSession cs = mock(ContentSession.class);
ContentRepository cr = mockContentRepository(cs);
CallbackHandler cbh = new TestCallbackHandler(cr, mock(SecurityProvider.class));
AbstractLoginModule loginModule = initLoginModule(cbh);
loginModule.getRoot();
loginModule.closeSystemSession();
verify(cs, times(1)).close();
}
@Test
public void testGetSharedLoginName() {
Map<String, String> sharedState = new HashMap<>();
sharedState.put(AbstractLoginModule.SHARED_KEY_LOGIN_NAME, "test");
AbstractLoginModule lm = initLoginModule(TestCredentials.class, sharedState);
assertEquals("test", lm.getSharedLoginName());
sharedState.clear();
lm = initLoginModule(TestCredentials.class, sharedState);
assertNull(lm.getSharedLoginName());
}
@Test
public void testGetSharedCredentials() {
Map<String, Object> sharedState = new HashMap<>();
sharedState.put(SHARED_KEY_CREDENTIALS, new TestCredentials());
AbstractLoginModule lm = initLoginModule(TestCredentials.class, sharedState);
assertTrue(lm.getSharedCredentials() instanceof TestCredentials);
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(SHARED_KEY_CREDENTIALS, "no credentials object");
lm = initLoginModule(TestCredentials.class, sharedState);
assertNull(lm.getSharedCredentials());
sharedState.clear();
lm = initLoginModule(TestCredentials.class, sharedState);
assertNull(lm.getSharedCredentials());
}
@Test
public void testGetCredentialsFromSharedState() {
Map<String, Credentials> sharedState = new HashMap<>();
sharedState.put(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(SHARED_KEY_CREDENTIALS, sc);
lm = initLoginModule(TestCredentials.class, sharedState);
assertNull(lm.getCredentials());
sharedState.put(SHARED_KEY_CREDENTIALS, sc);
lm = initLoginModule(SimpleCredentials.class, sharedState);
assertTrue(lm.getCredentials() instanceof SimpleCredentials);
sharedState.clear();
lm = initLoginModule(TestCredentials.class, sharedState);
assertNull(lm.getCredentials());
}
@Test
public void testGetCredentialsFromSubject() {
Subject subject = new Subject();
subject.getPublicCredentials().add(new TestCredentials());
AbstractLoginModule lm = new TestLoginModule(TestCredentials.class);
lm.initialize(subject, null, ImmutableMap.of(), null);
assertTrue(lm.getCredentials() instanceof TestCredentials);
}
@Test
public void testGetCredentialsFromSubjectWrongClass() {
Subject subject = new Subject();
subject.getPublicCredentials().add(new SimpleCredentials("userid", new char[0]));
AbstractLoginModule lm = new TestLoginModule(TestCredentials.class);
lm.initialize(subject, null, Collections.emptyMap(), null);
assertNull(lm.getCredentials());
}
@Test
public void testGetCredentialsFromCallbackHandler() {
CallbackHandler cbh = callbacks -> {
for (Callback cb : callbacks) {
if (cb instanceof CredentialsCallback) {
((CredentialsCallback) cb).setCredentials(new TestCredentials());
}
}
};
AbstractLoginModule lm = initLoginModule(cbh);
assertTrue(lm.getCredentials() instanceof TestCredentials);
lm = new TestLoginModule(SimpleCredentials.class);
lm.initialize(new Subject(), cbh, Collections.emptyMap(), null);
assertNull(lm.getCredentials());
}
@Test
public void testGetCredentialsIOException() {
AbstractLoginModule lm = initLoginModule(new ThrowingCallbackHandler(true), monitor);
assertNull(lm.getCredentials());
verify(monitor, times(1)).loginError();
verifyNoMoreInteractions(monitor);
}
@Test
public void testGetCredentialsUnsupportedCallbackException() {
AbstractLoginModule lm = initLoginModule(new ThrowingCallbackHandler(false), monitor);
assertNull(lm.getCredentials());
verify(monitor, times(1)).loginError();
verifyNoMoreInteractions(monitor);
}
@Test
public void testGetCredentialsCallbackReturnsNull() {
CallbackHandler cbh = callbacks -> {
for (Callback cb : callbacks) {
if (cb instanceof CredentialsCallback) {
((CredentialsCallback) cb).setCredentials(null);
}
}
};
AbstractLoginModule lm = initLoginModule(cbh);
assertNull(lm.getCredentials());
}
@Test
public void testGetSharedPreAuthLoginEmptySharedState() {
AbstractLoginModule loginModule = initLoginModule(TestCredentials.class, ImmutableMap.of());
assertNull(loginModule.getSharedPreAuthLogin());
}
@Test
public void testGetSharedPreAuthLogin() {
Map<String, PreAuthenticatedLogin> sharedState = new HashMap<>();
AbstractLoginModule loginModule = initLoginModule(TestCredentials.class, sharedState);
PreAuthenticatedLogin preAuthenticatedLogin = new PreAuthenticatedLogin("userId");
sharedState.put(AbstractLoginModule.SHARED_KEY_PRE_AUTH_LOGIN, preAuthenticatedLogin);
assertSame(preAuthenticatedLogin, loginModule.getSharedPreAuthLogin());
}
@Test
public void testGetSharedPreAuthLoginWrongEntry() {
Map<String, String> sharedState = new HashMap<>();
AbstractLoginModule loginModule = initLoginModule(TestCredentials.class, sharedState);
sharedState.put(AbstractLoginModule.SHARED_KEY_PRE_AUTH_LOGIN, "wrongType");
assertNull(loginModule.getSharedPreAuthLogin());
}
@Test
public void testIncompleteRepositoryCallback() {
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler());
assertNull(loginModule.getSecurityProvider());
assertNull(loginModule.getRoot());
}
@Test
public void testGetRoot() throws Exception {
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler(mockContentRepository(null), null));
Root root = loginModule.getRoot();
assertNotNull(root);
// root is stored as field -> second access returns the same object
assertSame(root, loginModule.getRoot());
}
@Test
public void testGetRootIOException() {
AbstractLoginModule lm = initLoginModule(new ThrowingCallbackHandler(true), monitor);
assertNull(lm.getRoot());
verify(monitor).loginError();
verifyNoMoreInteractions(monitor);
}
@Test
public void testGetRootUnsupportedCallbackException() {
AbstractLoginModule lm = initLoginModule(new ThrowingCallbackHandler(false), monitor);
assertNull(lm.getRoot());
verify(monitor).loginError();
verifyNoMoreInteractions(monitor);
}
@Test
public void testGetRootMissingCallbackHandler() {
AbstractLoginModule loginModule = initLoginModule((CallbackHandler) null);
assertNull(loginModule.getRoot());
}
@Test
public void testGetSecurityProvider() {
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler(null, new OpenSecurityProvider()));
SecurityProvider securityProvider = loginModule.getSecurityProvider();
assertNotNull(securityProvider);
// securityProvider is stored as field -> second access returns the same object
assertSame(securityProvider, loginModule.getSecurityProvider());
}
@Test
public void testGetSecurityProviderIOException() {
AbstractLoginModule loginModule = initLoginModule(new ThrowingCallbackHandler(true));
assertNull(loginModule.getSecurityProvider());
}
@Test
public void testGetSecurityProviderUnsupportedCallbackException() {
AbstractLoginModule loginModule = initLoginModule(new ThrowingCallbackHandler(false));
assertNull(loginModule.getRoot());
}
@Test
public void testGetSecurityProviderMissingCallbackHandler() {
AbstractLoginModule loginModule = initLoginModule((CallbackHandler) null);
assertNull(loginModule.getSecurityProvider());
}
@Test
public void testGetWhiteboardFromCallback() {
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler(new DefaultWhiteboard()));
Whiteboard wb = loginModule.getWhiteboard();
assertNotNull(wb);
// whiteboard is stored as field -> second access returns the same object
assertSame(wb, loginModule.getWhiteboard());
}
@Test
public void testGetWhiteboardFromIncompleteCallback() {
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler());
assertNull(loginModule.getWhiteboard());
}
@Test
public void testGetWhiteboardIOException() {
AbstractLoginModule loginModule = initLoginModule(new ThrowingCallbackHandler(true));
assertNull(loginModule.getWhiteboard());
}
@Test
public void testGetWhiteboardUnsupportedCallbackException() {
AbstractLoginModule loginModule = initLoginModule(new ThrowingCallbackHandler(false));
assertNull(loginModule.getWhiteboard());
}
@Test
public void testGetWhiteBoardMissingCallbackHandler() {
AbstractLoginModule loginModule = initLoginModule((CallbackHandler) null);
assertNull(loginModule.getWhiteboard());
}
@Test
public void testGetUserManagerFromCallback() {
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler(mock(UserManager.class)));
UserManager userManager = loginModule.getUserManager();
assertNotNull(userManager);
// usermanager is stored as field -> second access returns the same object
assertSame(userManager, loginModule.getUserManager());
}
@Test
public void testGetUserManagerFromIncompleteCallback() {
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler());
assertNull(loginModule.getUserManager());
}
@Test
public void testGetUserManagerIOException() {
AbstractLoginModule loginModule = initLoginModule(new ThrowingCallbackHandler(true));
assertNull(loginModule.getUserManager());
}
@Test
public void testGetUserManagerUnsupportedCallbackException() {
AbstractLoginModule loginModule = initLoginModule(new ThrowingCallbackHandler(false));
assertNull(loginModule.getUserManager());
}
@Test
public void testGetUserManagerWithRepositoryCallbackHandler() throws Exception {
Root r = mock(Root.class);
ContentSession cs = when(mock(ContentSession.class).getLatestRoot()).thenReturn(r).getMock();
ContentRepository cp = when(mock(ContentRepository.class).login(null, null)).thenReturn(cs).getMock();
UserManager um = mock(UserManager.class);
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);
AbstractLoginModule loginModule = initLoginModule(cbh);
assertEquals(um, loginModule.getUserManager());
}
@Test
public void testGetUserManagerMissingCallbackHandler() {
AbstractLoginModule loginModule = initLoginModule((CallbackHandler) null);
assertNull(loginModule.getUserManager());
}
@Test
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));
AbstractLoginModule loginModule = initLoginModule(cbh);
assertNull(loginModule.getUserManager());
}
@Test
public void testGetUserManagerMissingSecurityProvider() throws Exception {
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);
AbstractLoginModule loginModule = initLoginModule(cbh);
assertNull(loginModule.getUserManager());
}
@Test
public void testGetPrincipalProviderFromCallback() {
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler(new TestPrincipalProvider()));
assertNotNull(loginModule.getPrincipalProvider());
PrincipalProvider principalProvider = loginModule.getPrincipalProvider();
assertNotNull(principalProvider);
// principalProvider is stored as field -> second access returns the same object
assertSame(principalProvider, loginModule.getPrincipalProvider());
}
@Test
public void testGetPrincipalProviderFromIncompleteCallback() {
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler());
assertNull(loginModule.getPrincipalProvider());
}
@Test
public void testGetPrincipalProviderIOException() {
AbstractLoginModule loginModule = initLoginModule(new ThrowingCallbackHandler(true));
assertNull(loginModule.getPrincipalProvider());
}
@Test
public void testGetPrincipalProviderUnsupportedCallbackException() {
AbstractLoginModule loginModule = initLoginModule(new ThrowingCallbackHandler(false));
assertNull(loginModule.getPrincipalProvider());
}
@Test
public void testGetPrincipalProviderWithRepositoryCallbackHandler() throws Exception {
Root r = mock(Root.class);
ContentSession cs = when(mock(ContentSession.class).getLatestRoot()).thenReturn(r).getMock();
ContentRepository cp = when(mock(ContentRepository.class).login(null, null)).thenReturn(cs).getMock();
PrincipalProvider pp = mock(PrincipalProvider.class);
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);
AbstractLoginModule loginModule = initLoginModule(cbh);
assertEquals(pp, loginModule.getPrincipalProvider());
}
@Test
public void testGetPrincipalProviderMissingCallbackHandler() {
AbstractLoginModule loginModule = initLoginModule((CallbackHandler) null);
assertNull(loginModule.getPrincipalProvider());
}
@Test
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));
AbstractLoginModule loginModule = initLoginModule(cbh);
assertNull(loginModule.getPrincipalProvider());
}
@Test
public void testGetPrincipalProviderMissingSecurityProvider() throws Exception {
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);
AbstractLoginModule loginModule = initLoginModule(cbh);
assertNull(loginModule.getPrincipalProvider());
}
@Test
public void testGetPrincipals() {
PrincipalProvider principalProvider = new TestPrincipalProvider();
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler(principalProvider));
Principal principal = principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_NOT_GROUP).next();
String userId = TestPrincipalProvider.getIDFromPrincipal(principal);
Set<? extends Principal> principals = loginModule.getPrincipals(userId);
assertFalse(principals.isEmpty());
assertEquals(principalProvider.getPrincipals(userId), principals);
}
@Test
public void testGetPrincipalsMissingProvider() {
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler());
Set<? extends Principal> principals = loginModule.getPrincipals("userId");
assertTrue(principals.isEmpty());
}
@Test
public void testGetPrincipalsFromPrincipal() {
PrincipalProvider principalProvider = new TestPrincipalProvider();
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler(principalProvider));
Principal principal = principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_NOT_GROUP).next();
Set<Principal> expected = new HashSet<>();
expected.add(principal);
expected.addAll(principalProvider.getMembershipPrincipals(principal));
Set<? extends Principal> principals = loginModule.getPrincipals(principal);
assertFalse(principals.isEmpty());
assertEquals(expected, principals);
}
@Test
public void testGetPrincipalsFromPrincipalMissingProvider() {
AbstractLoginModule loginModule = initLoginModule(new TestCallbackHandler());
Set<? extends Principal> principals = loginModule.getPrincipals(new PrincipalImpl("principalName"));
assertTrue(principals.isEmpty());
}
@Test
public void testSetAuthInfo() {
Subject subject = new Subject();
AuthInfo authInfo = new AuthInfoImpl("userid", null, null);
AbstractLoginModule.setAuthInfo(authInfo, subject);
Set<AuthInfo> fromSubject = subject.getPublicCredentials(AuthInfo.class);
assertEquals(1, fromSubject.size());
assertSame(authInfo, fromSubject.iterator().next());
}
@Test
public void testSetAuthInfoPreExisting() {
Subject subject = new Subject();
subject.getPublicCredentials().add(new AuthInfoImpl(null, null, null));
AuthInfo authInfo = new AuthInfoImpl("userid", null, null);
AbstractLoginModule.setAuthInfo(authInfo, subject);
Set<AuthInfo> fromSubject = subject.getPublicCredentials(AuthInfo.class);
assertEquals(1, fromSubject.size());
assertSame(authInfo, fromSubject.iterator().next());
}
@Test
public void testOnError() {
CallbackHandler cbh = callbacks -> {
for (Callback cb : callbacks) {
if (cb instanceof RepositoryCallback) {
((RepositoryCallback) cb).setLoginModuleMonitor(monitor);
}
}
};
AbstractLoginModule lm = initLoginModule(cbh);
assertSame(monitor, lm.getLoginModuleMonitor());
lm.onError();
verify(monitor).loginError();
verifyNoMoreInteractions(monitor);
}
@Test
public void testLoginModuleMonitorMissingCallback() {
AbstractLoginModule lm = initLoginModule((CallbackHandler) null);
assertSame(LoginModuleMonitor.NOOP, lm.getLoginModuleMonitor());
lm.onError();
verifyNoInteractions(monitor);
}
@Test
public void testErrorOnGetLoginModuleMonitor() {
AbstractLoginModule lm = initLoginModule(new ThrowingCallbackHandler(true));
assertSame(LoginModuleMonitor.NOOP, lm.getLoginModuleMonitor());
lm.onError();
verifyNoInteractions(monitor);
}
//--------------------------------------------------------------------------
private static class TestCredentials implements Credentials {}
private static final class TestLoginModule extends AbstractLoginModule {
private final Class<?> supportedCredentialsClass;
private final LoginModuleMonitor mon;
private TestLoginModule(@NotNull Class<?> supportedCredentialsClass) {
this(supportedCredentialsClass, null);
}
private TestLoginModule(@NotNull Class<?> supportedCredentialsClass, @Nullable LoginModuleMonitor mon) {
this.supportedCredentialsClass = supportedCredentialsClass;
this.mon = mon;
}
@NotNull
@Override
protected Set<Class> getSupportedCredentials() {
return Collections.singleton(supportedCredentialsClass);
}
@Override
public boolean login() {
throw new UnsupportedOperationException();
}
@Override
public boolean commit() {
throw new UnsupportedOperationException();
}
@Override
protected @NotNull LoginModuleMonitor getLoginModuleMonitor() {
if (mon != null) {
return mon;
} else {
return super.getLoginModuleMonitor();
}
}
}
private static final class TestCallbackHandler implements CallbackHandler {
private Whiteboard whiteboard = null;
private UserManager userManager = null;
private PrincipalProvider principalProvider = null;
private ContentRepository contentRepository = null;
private SecurityProvider securityProvider = null;
private TestCallbackHandler() {
}
private TestCallbackHandler(@NotNull Whiteboard whiteboard) {
this.whiteboard = whiteboard;
}
private TestCallbackHandler(@NotNull UserManager userManager) {
this.userManager = userManager;
}
private TestCallbackHandler(@NotNull PrincipalProvider principalProvider) {
this.principalProvider = principalProvider;
}
private TestCallbackHandler(@Nullable ContentRepository contentRepository, @Nullable SecurityProvider securityProvider) {
this.contentRepository = contentRepository;
this.securityProvider = securityProvider;
}
@Override
public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
for (Callback cb : callbacks) {
if (cb instanceof WhiteboardCallback) {
((WhiteboardCallback) cb).setWhiteboard(whiteboard);
} else if (cb instanceof PrincipalProviderCallback) {
((PrincipalProviderCallback) cb).setPrincipalProvider(principalProvider);
} else if (cb instanceof UserManagerCallback) {
((UserManagerCallback) cb).setUserManager(userManager);
} else if (cb instanceof SecurityProviderCallback) {
((SecurityProviderCallback) cb).setSecurityProvider(securityProvider);
} else if (cb instanceof RepositoryCallback) {
RepositoryCallback rcb = (RepositoryCallback) cb;
rcb.setContentRepository(contentRepository);
rcb.setSecurityProvider(securityProvider);
rcb.setLoginModuleMonitor(LoginModuleMonitor.NOOP);
} else {
throw new UnsupportedCallbackException(cb);
}
}
}
}
}