Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ public boolean isAdmin(IamAccount account) {
return account.getAuthorities().contains(ROLE_ADMIN);
}

public boolean isPreAuthenticated() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return isPreAuthenticated(auth);
}

public boolean isPreAuthenticated(Authentication auth) {
if (auth == null || auth.getAuthorities().isEmpty()) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha

String requestURL = req.getRequestURL().toString();

if (!accountUtils.isAuthenticated() || isNull(session) || requestURL.endsWith(AUP_API_PATH)) {
if (!accountUtils.isAuthenticated() || isNull(session) || requestURL.endsWith(AUP_API_PATH)
|| accountUtils.isPreAuthenticated()) {
chain.doFilter(request, response);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,20 @@
import static it.infn.mw.iam.test.ext_authn.oidc.OidcTestConfig.TEST_OIDC_ISSUER;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;


import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Map;
import java.util.random.RandomGenerator;

import org.hamcrest.Matchers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.http.HttpEntity;
Expand All @@ -46,6 +51,8 @@
import it.infn.mw.iam.IamLoginService;
import it.infn.mw.iam.authn.ExternalAuthenticationRegistrationInfo;
import it.infn.mw.iam.authn.ExternalAuthenticationRegistrationInfo.ExternalAuthenticationType;
import it.infn.mw.iam.persistence.model.IamAup;
import it.infn.mw.iam.persistence.repository.IamAupRepository;
import it.infn.mw.iam.test.util.annotation.IamRandomPortIntegrationTest;
import it.infn.mw.iam.test.util.oidc.CodeRequestHolder;
import it.infn.mw.iam.test.util.oidc.MockRestTemplateFactory;
Expand All @@ -55,6 +62,9 @@
webEnvironment = WebEnvironment.RANDOM_PORT)
class OidcExternalAuthenticationTests extends OidcExternalAuthenticationTestsSupport {

@Autowired
private IamAupRepository aupRepo;

@BeforeEach
void setup() {
MockRestTemplateFactory tf = (MockRestTemplateFactory) restTemplateFactory;
Expand Down Expand Up @@ -236,6 +246,61 @@ void testOidcUserRedirectToMfaVerifyPageIfMfaIsActive()
assertThat(response.getHeaders().getLocation().toString(), equalTo(mfaVerifyPageURL()));
}

@Test
void testOidcUserRedirectToMfaVerifyPageIfMfaIsActiveEvenIfAupPending()
throws JOSEException, JsonProcessingException, RestClientException {
createDefaultAup();
RestTemplate rt = noRedirectRestTemplate();
ResponseEntity<String> response = rt.getForEntity(openidConnectLoginURL(), String.class);

checkAuthorizationEndpointRedirect(response);
HttpHeaders requestHeaders = new HttpHeaders();

String sessionCookie = extractSessionCookie(response);
requestHeaders.add("Cookie", sessionCookie);

CodeRequestHolder ru = buildCodeRequest(sessionCookie, response);

String tokenResponse = mockOidcProvider.prepareTokenResponse(TEST_OIDC_CLIENT_ID, "test-with-mfa", ru.nonce);

prepareSuccessResponse(tokenResponse);

response = rt.postForEntity(openidConnectLoginURL(), ru.requestEntity, String.class);
verifyMockServerCalls();

assertThat(response.getStatusCode(), equalTo(HttpStatus.FOUND));
assertNotNull(response.getHeaders().getLocation());

assertThat(response.getHeaders().getLocation().toString(), equalTo(mfaVerifyPageURL()));

HttpHeaders followHeaders = new HttpHeaders();
followHeaders.add("Cookie", sessionCookie);
HttpEntity<Void> followEntity = new HttpEntity<>(followHeaders);

ResponseEntity<String> redirectedResponse = rt.exchange(mfaVerifyPageURL(), HttpMethod.GET, followEntity,
String.class);

assertThat(redirectedResponse.getStatusCode(), equalTo(HttpStatus.OK));
assertThat(redirectedResponse.getBody(),
containsString("For your security, please enter a TOTP from your authenticator"));

aupRepo.deleteAll();
}

private void createDefaultAup() {
IamAup aup = new IamAup();

aup.setCreationTime(new Date());
aup.setLastUpdateTime(new Date());
aup.setName("default-aup" + RandomGenerator.getDefault().nextInt());
aup.setUrl("http://default-aup.org/");
aup.setDescription("AUP description");
aup.setSignatureValidityInDays(0L);
aup.setAupRemindersInDays("30,15,1");

aupRepo.saveDefaultAup(aup);
}

@Test
void testOidcUserRedirectToHomeIfMfaIsActiveAndAcrPresentInIdToken()
throws JOSEException, JsonProcessingException, RestClientException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,39 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;

import java.util.Date;
import java.util.random.RandomGenerator;

import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.opensaml.saml2.core.AuthnRequest;
import org.opensaml.saml2.core.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import it.infn.mw.iam.persistence.model.IamAup;
import it.infn.mw.iam.persistence.repository.IamAupRepository;
import it.infn.mw.iam.test.util.annotation.IamMockMvcIntegrationTest;
import it.infn.mw.iam.test.util.saml.SamlUtils;

@ExtendWith(SpringExtension.class)
@IamMockMvcIntegrationTest
class SamlExternalAuthenticationTests extends SamlAuthenticationTestSupport {

@Autowired
private IamAupRepository aupRepo;

private static final String URL_DASHBOARD = "/dashboard";
private static final String URL_VERIFY = "/iam/verify";
private static final String VIEW_VERIFY_MFA = "iam/verify-mfa";


@Test
void testSuccessfulExternalUnregisteredUserAuthentication() throws Throwable {

MockHttpSession session = (MockHttpSession) mvc.perform(get(samlDefaultIdpLoginUrl()))
.andExpect(status().isOk())
.andReturn()
.getRequest()
.getSession();
MockHttpSession session = performInitialLogin();

AuthnRequest authnRequest = getAuthnRequestFromSession(session);

Expand Down Expand Up @@ -87,12 +94,7 @@ void testSuccessfulExternalUnregisteredUserAuthentication() throws Throwable {
@Test
void testExternalAuthenticationFailureRedirectsToLoginPage() throws Throwable {

MockHttpSession session =
(MockHttpSession) mvc.perform(MockMvcRequestBuilders.get(samlDefaultIdpLoginUrl()))
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn()
.getRequest()
.getSession();
MockHttpSession session = performInitialLogin();

AuthnRequest authnRequest = getAuthnRequestFromSession(session);

Expand All @@ -116,52 +118,78 @@ void testExternalAuthenticationFailureRedirectsToLoginPage() throws Throwable {
@Test
void testRegisteredUserWithMfaGetsRedirectedToMfaVerify() throws Throwable {

MockHttpSession session = (MockHttpSession) mvc.perform(get(samlDefaultIdpLoginUrl()))
.andExpect(status().isOk())
.andReturn()
.getRequest()
.getSession();
MockHttpSession session = performInitialLogin();

AuthnRequest authnRequest = getAuthnRequestFromSession(session);
Response response = buildMfaTest1Response(authnRequest);
session = postMfaResponseAndExpectRedirect(response, authnRequest, session, URL_VERIFY);

Response r = buildMfaTest1Response(authnRequest);

session = (MockHttpSession) mvc
.perform(post(authnRequest.getAssertionConsumerServiceURL())
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("SAMLResponse", SamlUtils.signAndSerializeToBase64(r))
.session(session))
.andExpect(redirectedUrl("/iam/verify"))
.andReturn()
.getRequest()
.getSession();

mvc.perform(get("/iam/verify").session(session))
mvc.perform(get(URL_VERIFY).session(session))
.andExpect(status().isOk())
.andExpect(view().name("iam/verify-mfa"));
.andExpect(view().name(VIEW_VERIFY_MFA));
}

@Test
void testRedirectionToDashboardIfRemoteIdpPerformsMfa() throws Throwable {

MockHttpSession session = (MockHttpSession) mvc.perform(get(samlDefaultIdpLoginUrl()))
.andExpect(status().isOk())
.andReturn()
.getRequest()
.getSession();
MockHttpSession session = performInitialLogin();

AuthnRequest authnRequest = getAuthnRequestFromSession(session);

Response r = buildMfaTest2Response(authnRequest);
Response response = buildMfaTest2Response(authnRequest);
postMfaResponseAndExpectRedirect(response, authnRequest, session, URL_DASHBOARD);
}

mvc
.perform(post(authnRequest.getAssertionConsumerServiceURL())
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("SAMLResponse", SamlUtils.signAndSerializeToBase64(r))
.session(session))
.andExpect(redirectedUrl("/dashboard"))
.andReturn()
.getRequest()
.getSession();
@Test
void testRegisteredUserWithMfaGetsRedirectedToMfaVerifyEvenIfAupPending() throws Throwable {
createDefaultAup();
MockHttpSession session = performInitialLogin();

AuthnRequest authnRequest = getAuthnRequestFromSession(session);
Response response = buildMfaTest1Response(authnRequest);
session = postMfaResponseAndExpectRedirect(response, authnRequest, session, URL_VERIFY);

mvc.perform(get(URL_VERIFY).session(session))
.andExpect(status().isOk())
.andExpect(view().name(VIEW_VERIFY_MFA));

aupRepo.deleteAll();
}

private MockHttpSession performInitialLogin() throws Exception {
return (MockHttpSession) mvc.perform(get(samlDefaultIdpLoginUrl()))
.andExpect(status().isOk())
.andReturn()
.getRequest()
.getSession();
}

private MockHttpSession postMfaResponseAndExpectRedirect(Response response, AuthnRequest authnRequest,
MockHttpSession session, String expectedUrl) throws Throwable {
String encodedSaml = SamlUtils.signAndSerializeToBase64(response);

return (MockHttpSession) mvc.perform(
post(authnRequest.getAssertionConsumerServiceURL())
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("SAMLResponse", encodedSaml)
.session(session))
.andExpect(redirectedUrl(expectedUrl))
.andReturn()
.getRequest()
.getSession();
}

private void createDefaultAup() {
IamAup aup = new IamAup();

aup.setCreationTime(new Date());
aup.setLastUpdateTime(new Date());
aup.setName("default-aup" + RandomGenerator.getDefault().nextInt());
aup.setUrl("http://default-aup.org/");
aup.setDescription("AUP description");
aup.setSignatureValidityInDays(0L);
aup.setAupRemindersInDays("30,15,1");

aupRepo.saveDefaultAup(aup);
}
}
Loading