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; } }