package org.apereo.cas.authentication;
import org.apereo.cas.services.DefaultRegisteredServiceMultifactorPolicy;
import org.apereo.cas.services.MultifactorAuthenticationProvider;
import org.apereo.cas.services.RegexRegisteredService;
import org.junit.Before;
import org.junit.Test;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* @author Daniel Frett
* @since 5.0.0
*/
public class DefaultMultifactorTriggerSelectionStrategyTests {
private static final String MFA_INVALID = "mfaInvalid";
private static final String MFA_PROVIDER_ID_1 = "mfa-id1";
private static final String MFA_PROVIDER_ID_2 = "mfa-id2";
private static final MultifactorAuthenticationProvider MFA_PROVIDER_1 = mock(MultifactorAuthenticationProvider.class);
private static final MultifactorAuthenticationProvider MFA_PROVIDER_2 = mock(MultifactorAuthenticationProvider.class);
private static final Set<MultifactorAuthenticationProvider> VALID_PROVIDERS = Stream.of(MFA_PROVIDER_1, MFA_PROVIDER_2).collect(Collectors.toSet());
private static final Set<MultifactorAuthenticationProvider> NO_PROVIDERS = Collections.emptySet();
private static final String REQUEST_PARAM = "authn_method";
private static final String RS_ATTR_1 = "rs_attr1";
private static final String RS_ATTR_2 = "rs_attr2";
private static final String RS_ATTR_3 = "rs_attr3";
private static final String RS_ATTRS_12 = RS_ATTR_1 + ',' + RS_ATTR_2;
private static final String P_ATTR_1 = "principal_attr_1";
private static final String P_ATTR_2 = "principal_attr_2";
private static final String P_ATTRS_12 = P_ATTR_1 + ',' + P_ATTR_2;
private static final String VALUE_1 = "enforce_1";
private static final String VALUE_2 = "enforce_2";
private static final String VALUE_PATTERN = "^enforce.*$";
private static final String VALUE_NOMATCH = "noop";
private DefaultMultifactorTriggerSelectionStrategy strategy;
@Before
public void setUp() {
strategy = new DefaultMultifactorTriggerSelectionStrategy(P_ATTRS_12, REQUEST_PARAM);
when(MFA_PROVIDER_1.getId()).thenReturn(MFA_PROVIDER_ID_1);
when(MFA_PROVIDER_2.getId()).thenReturn(MFA_PROVIDER_ID_2);
}
@Test
public void verifyNoProviders() throws Exception {
assertThat(strategy.resolve(NO_PROVIDERS, mockRequest(MFA_PROVIDER_ID_1), mockService(MFA_PROVIDER_ID_1), null).isPresent(),
is(false));
}
@Test
public void verifyRequestParameterTrigger() throws Exception {
// opt-in parameter only
assertThat(strategy.resolve(VALID_PROVIDERS, mockRequest(MFA_PROVIDER_ID_1), null, null).orElse(null), is(MFA_PROVIDER_ID_1));
assertThat(strategy.resolve(VALID_PROVIDERS, mockRequest(MFA_PROVIDER_ID_2), null, null).orElse(null), is(MFA_PROVIDER_ID_2));
assertThat(strategy.resolve(VALID_PROVIDERS, mockRequest(MFA_INVALID), null, null).isPresent(), is(false));
}
@Test
public void verifyRegisteredServiceTrigger() throws Exception {
// regular RegisteredService trigger
assertThat(strategy.resolve(VALID_PROVIDERS, null, mockService(MFA_PROVIDER_ID_1), null).orElse(null), is(MFA_PROVIDER_ID_1));
assertThat(strategy.resolve(VALID_PROVIDERS, null, mockService(MFA_PROVIDER_ID_2), null).orElse(null), is(MFA_PROVIDER_ID_2));
assertThat(strategy.resolve(VALID_PROVIDERS, null, mockService(MFA_INVALID, MFA_PROVIDER_ID_1, MFA_PROVIDER_ID_2), null).get(),
is(MFA_PROVIDER_ID_1));
assertThat(strategy.resolve(VALID_PROVIDERS, null, mockService(MFA_INVALID), null).isPresent(), is(false));
// Principal attribute activated RegisteredService trigger - direct match
assertThat(strategy.resolve(VALID_PROVIDERS, null, mockPrincipalService(MFA_PROVIDER_ID_1, RS_ATTR_1, VALUE_1),
CoreAuthenticationTestUtils.mockPrincipal(RS_ATTR_1, VALUE_1)).orElse(null),
is(MFA_PROVIDER_ID_1));
assertThat(strategy.resolve(VALID_PROVIDERS, null, mockPrincipalService(MFA_PROVIDER_ID_1, RS_ATTR_1, VALUE_1),
CoreAuthenticationTestUtils.mockPrincipal(RS_ATTR_1, VALUE_2)).orElse(null),
nullValue());
// Principal attribute activated RegisteredService trigger - multiple attrs
assertThat(strategy.resolve(VALID_PROVIDERS, null, mockPrincipalService(MFA_PROVIDER_ID_1, RS_ATTRS_12, VALUE_1),
CoreAuthenticationTestUtils.mockPrincipal(RS_ATTR_1, VALUE_1)).orElse(null),
is(MFA_PROVIDER_ID_1));
assertThat(strategy.resolve(VALID_PROVIDERS, null, mockPrincipalService(MFA_PROVIDER_ID_1, RS_ATTRS_12, VALUE_1),
CoreAuthenticationTestUtils.mockPrincipal(RS_ATTR_2, VALUE_1)).orElse(null),
is(MFA_PROVIDER_ID_1));
assertThat(strategy.resolve(VALID_PROVIDERS, null, mockPrincipalService(MFA_PROVIDER_ID_1, RS_ATTRS_12, VALUE_1),
CoreAuthenticationTestUtils.mockPrincipal(RS_ATTR_3, VALUE_1)).orElse(null),
nullValue());
// Principal attribute activated RegisteredService trigger - pattern value
assertThat(strategy.resolve(VALID_PROVIDERS, null, mockPrincipalService(MFA_PROVIDER_ID_1, RS_ATTRS_12,
VALUE_PATTERN), CoreAuthenticationTestUtils.mockPrincipal(RS_ATTR_2, VALUE_1)).orElse(null),
is(MFA_PROVIDER_ID_1));
assertThat(strategy.resolve(VALID_PROVIDERS, null, mockPrincipalService(MFA_PROVIDER_ID_1, RS_ATTRS_12,
VALUE_PATTERN), CoreAuthenticationTestUtils.mockPrincipal(RS_ATTR_2, VALUE_2)).orElse(null),
is(MFA_PROVIDER_ID_1));
assertThat(strategy.resolve(VALID_PROVIDERS, null, mockPrincipalService(MFA_PROVIDER_ID_1, RS_ATTRS_12, VALUE_PATTERN),
CoreAuthenticationTestUtils.mockPrincipal(RS_ATTR_2, VALUE_NOMATCH)).isPresent(), is(false));
}
@Test
public void verifyPrincipalAttributeTrigger() throws Exception {
// Principal attribute trigger
assertThat(strategy.resolve(VALID_PROVIDERS, null, null, CoreAuthenticationTestUtils.mockPrincipal(P_ATTR_1, MFA_PROVIDER_ID_1)).orElse(null),
is(MFA_PROVIDER_ID_1));
assertThat(strategy.resolve(VALID_PROVIDERS, null, null, CoreAuthenticationTestUtils.mockPrincipal(P_ATTR_1, MFA_PROVIDER_ID_2)).orElse(null),
is(MFA_PROVIDER_ID_2));
assertThat(strategy.resolve(VALID_PROVIDERS, null, null, CoreAuthenticationTestUtils.mockPrincipal(P_ATTR_1, MFA_INVALID)).isPresent(), is(false));
}
@Test
public void verifyMultipleTriggers() throws Exception {
// opt-in overrides everything
assertThat(strategy.resolve(VALID_PROVIDERS, mockRequest(MFA_PROVIDER_ID_1), mockService(MFA_PROVIDER_ID_2),
CoreAuthenticationTestUtils.mockPrincipal(P_ATTR_1, MFA_PROVIDER_ID_2)).orElse(null),
is(MFA_PROVIDER_ID_1));
// RegisteredService overrides Principal attribute
assertThat(strategy.resolve(VALID_PROVIDERS, mockRequest(MFA_INVALID), mockService(MFA_PROVIDER_ID_1),
CoreAuthenticationTestUtils.mockPrincipal(P_ATTR_1, MFA_PROVIDER_ID_2)).orElse(null),
is(MFA_PROVIDER_ID_1));
}
private static HttpServletRequest mockRequest() {
return mock(HttpServletRequest.class);
}
private static HttpServletRequest mockRequest(final String provider) {
final HttpServletRequest request = mockRequest();
when(request.getParameter(REQUEST_PARAM)).thenReturn(provider);
return request;
}
private static RegexRegisteredService mockService(final String... providers) {
final DefaultRegisteredServiceMultifactorPolicy policy = new DefaultRegisteredServiceMultifactorPolicy();
policy.setMultifactorAuthenticationProviders(Stream.of(providers).collect(Collectors.toCollection(LinkedHashSet::new)));
final RegexRegisteredService service = new RegexRegisteredService();
service.setMultifactorPolicy(policy);
return service;
}
private static RegexRegisteredService mockPrincipalService(final String provider, final String attrName, final String attrValue) {
final RegexRegisteredService service = mockService(provider);
final DefaultRegisteredServiceMultifactorPolicy policy = (DefaultRegisteredServiceMultifactorPolicy) service.getMultifactorPolicy();
policy.setPrincipalAttributeNameTrigger(attrName);
policy.setPrincipalAttributeValueToMatch(attrValue);
return service;
}
}