package org.apereo.cas.authentication;
import org.apereo.cas.authentication.policy.AllAuthenticationPolicy;
import org.apereo.cas.authentication.policy.AnyAuthenticationPolicy;
import org.apereo.cas.authentication.policy.RequiredHandlerAuthenticationPolicy;
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
import org.apereo.cas.authentication.principal.Principal;
import org.apereo.cas.authentication.principal.PrincipalResolver;
import org.apereo.cas.authentication.principal.Service;
import org.apereo.cas.services.RegisteredService;
import org.apereo.cas.services.ServicesManager;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.test.annotation.DirtiesContext;
import javax.security.auth.login.FailedLoginException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* Unit test for {@link PolicyBasedAuthenticationManager}.
*
* @author Marvin S. Addison
* @since 4.0.0
*/
@DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
public class PolicyBasedAuthenticationManagerTests {
private static final String HANDLER_A = "HandlerA";
private static final String HANDLER_B = "HandlerB";
@Rule
public ExpectedException thrown = ExpectedException.none();
private final AuthenticationTransaction transaction = AuthenticationTransaction.wrap(CoreAuthenticationTestUtils.getService(),
mock(Credential.class), mock(Credential.class));
@Test
public void verifyAuthenticateAnySuccess() throws Exception {
final Map<AuthenticationHandler, PrincipalResolver> map = new HashMap<>();
map.put(newMockHandler(true), null);
map.put(newMockHandler(false), null);
final PolicyBasedAuthenticationManager manager = new PolicyBasedAuthenticationManager(getAuthenticationExecutionPlan(map), mockServicesManager());
final Authentication auth = manager.authenticate(transaction);
assertEquals(1, auth.getSuccesses().size());
assertEquals(2, auth.getCredentials().size());
}
@Test
public void verifyAuthenticateAnyButTryAllSuccess() throws Exception {
final Map<AuthenticationHandler, PrincipalResolver> map = new HashMap<>();
map.put(newMockHandler(true), null);
map.put(newMockHandler(false), null);
final PolicyBasedAuthenticationManager manager = new PolicyBasedAuthenticationManager(getAuthenticationExecutionPlan(map),
mockServicesManager(), new AnyAuthenticationPolicy(true));
final Authentication auth = manager.authenticate(transaction);
assertEquals(1, auth.getSuccesses().size());
assertEquals(1, auth.getFailures().size());
assertEquals(2, auth.getCredentials().size());
}
protected ServicesManager mockServicesManager() {
final ServicesManager svc = mock(ServicesManager.class);
final RegisteredService reg = CoreAuthenticationTestUtils.getRegisteredService();
when(svc.findServiceBy(any(Service.class))).thenReturn(reg);
when(svc.getAllServices()).thenReturn(Collections.singletonList(reg));
return svc;
}
@Test
public void verifyAuthenticateAnyFailure() throws Exception {
final Map<AuthenticationHandler, PrincipalResolver> map = new LinkedHashMap<>();
map.put(newMockHandler(false), null);
map.put(newMockHandler(false), null);
final PolicyBasedAuthenticationManager manager = new PolicyBasedAuthenticationManager(getAuthenticationExecutionPlan(map), mockServicesManager());
this.thrown.expect(AuthenticationException.class);
this.thrown.expectMessage("2 errors, 0 successes");
manager.authenticate(transaction);
fail("Should have thrown authentication exception");
}
@Test
public void verifyAuthenticateAllSuccess() throws Exception {
final Map<AuthenticationHandler, PrincipalResolver> map = new LinkedHashMap<>();
map.put(newMockHandler(true), null);
map.put(newMockHandler(true), null);
final PolicyBasedAuthenticationManager manager = new PolicyBasedAuthenticationManager(getAuthenticationExecutionPlan(map),
mockServicesManager(), new AllAuthenticationPolicy());
final Authentication auth = manager.authenticate(transaction);
assertEquals(2, auth.getSuccesses().size());
assertEquals(0, auth.getFailures().size());
assertEquals(2, auth.getCredentials().size());
}
@Test
public void verifyAuthenticateAllFailure() throws Exception {
final Map<AuthenticationHandler, PrincipalResolver> map = new LinkedHashMap<>();
map.put(newMockHandler(false), null);
map.put(newMockHandler(false), null);
final PolicyBasedAuthenticationManager manager = new PolicyBasedAuthenticationManager(getAuthenticationExecutionPlan(map),
mockServicesManager(),
new AllAuthenticationPolicy());
this.thrown.expect(AuthenticationException.class);
this.thrown.expectMessage("2 errors, 0 successes");
manager.authenticate(transaction);
fail("Should have thrown authentication exception");
}
@Test
public void verifyAuthenticateRequiredHandlerSuccess() throws Exception {
final Map<AuthenticationHandler, PrincipalResolver> map = new LinkedHashMap<>();
map.put(newMockHandler(HANDLER_A, true), null);
map.put(newMockHandler(HANDLER_B, false), null);
final PolicyBasedAuthenticationManager manager = new PolicyBasedAuthenticationManager(getAuthenticationExecutionPlan(map),
null, new RequiredHandlerAuthenticationPolicy(HANDLER_A));
final Authentication auth = manager.authenticate(transaction);
assertEquals(1, auth.getSuccesses().size());
assertEquals(2, auth.getCredentials().size());
}
@Test
public void verifyAuthenticateRequiredHandlerFailure() throws Exception {
final Map<AuthenticationHandler, PrincipalResolver> map = new LinkedHashMap<>();
map.put(newMockHandler(HANDLER_A, true), null);
map.put(newMockHandler(HANDLER_B, false), null);
final PolicyBasedAuthenticationManager manager = new PolicyBasedAuthenticationManager(getAuthenticationExecutionPlan(map),
mockServicesManager(), new RequiredHandlerAuthenticationPolicy(HANDLER_B));
this.thrown.expect(AuthenticationException.class);
this.thrown.expectMessage("1 errors, 1 successes");
manager.authenticate(transaction);
fail("Should have thrown AbstractAuthenticationException");
}
@Test
public void verifyAuthenticateRequiredHandlerTryAllSuccess() throws Exception {
final Map<AuthenticationHandler, PrincipalResolver> map = new LinkedHashMap<>();
map.put(newMockHandler(HANDLER_A, true), null);
map.put(newMockHandler(HANDLER_B, false), null);
final PolicyBasedAuthenticationManager manager = new PolicyBasedAuthenticationManager(getAuthenticationExecutionPlan(map),
mockServicesManager(), new RequiredHandlerAuthenticationPolicy(HANDLER_A, true));
final Authentication auth = manager.authenticate(transaction);
assertEquals(1, auth.getSuccesses().size());
assertEquals(1, auth.getFailures().size());
assertEquals(2, auth.getCredentials().size());
}
/**
* Creates a new mock authentication handler that either successfully validates all credentials or fails to
* validate all credentials.
*
* @param success True to authenticate all credentials, false to fail all credentials.
* @return New mock authentication handler instance.
* @throws Exception On errors.
*/
private static AuthenticationHandler newMockHandler(final boolean success) throws Exception {
return newMockHandler("MockAuthenticationHandler" + System.nanoTime(), success);
}
/**
* Creates a new named mock authentication handler that either successfully validates all credentials or fails to
* validate all credentials.
*
* @param name Authentication handler name.
* @param success True to authenticate all credentials, false to fail all credentials.
* @return New mock authentication handler instance.
* @throws Exception On errors.
*/
private static AuthenticationHandler newMockHandler(final String name, final boolean success) throws Exception {
final AuthenticationHandler mock = mock(AuthenticationHandler.class);
when(mock.getName()).thenReturn(name);
when(mock.supports(any(Credential.class))).thenReturn(true);
if (success) {
final Principal p = new DefaultPrincipalFactory().createPrincipal("nobody");
final HandlerResult result = new DefaultHandlerResult(mock, mock(CredentialMetaData.class), p);
when(mock.authenticate(any(Credential.class))).thenReturn(result);
} else {
when(mock.authenticate(any(Credential.class))).thenThrow(new FailedLoginException());
}
return mock;
}
private AuthenticationEventExecutionPlan getAuthenticationExecutionPlan(final Map<AuthenticationHandler, PrincipalResolver> map) {
final DefaultAuthenticationEventExecutionPlan plan = new DefaultAuthenticationEventExecutionPlan();
plan.registerAuthenticationHandlerWithPrincipalResolver(map);
return plan;
}
}