85276: Store and retrieve the Authentication method in the JWT

This commit is contained in:
Yana De Pauw
2021-12-01 15:33:13 +01:00
parent dcfa74a2c3
commit 16e704a285
14 changed files with 200 additions and 4 deletions

View File

@@ -216,4 +216,12 @@ public interface AuthenticationMethod {
* @return The authentication method name * @return The authentication method name
*/ */
public String getName(); public String getName();
/**
* Get whether the authentication method is being used.
* @param context The DSpace context
* @param request The current request
* @return whether the authentication method is being used.
*/
public boolean isUsed(Context context, HttpServletRequest request);
} }

View File

@@ -193,4 +193,17 @@ public class AuthenticationServiceImpl implements AuthenticationService {
public Iterator<AuthenticationMethod> authenticationMethodIterator() { public Iterator<AuthenticationMethod> authenticationMethodIterator() {
return getAuthenticationMethodStack().iterator(); return getAuthenticationMethodStack().iterator();
} }
public String getAuthenticationMethod(final Context context, final HttpServletRequest request) {
final Iterator<AuthenticationMethod> authenticationMethodIterator = authenticationMethodIterator();
while (authenticationMethodIterator.hasNext()) {
final AuthenticationMethod authenticationMethod = authenticationMethodIterator.next();
if (authenticationMethod.isUsed(context, request)) {
return authenticationMethod.getName();
}
}
return null;
}
} }

View File

@@ -273,4 +273,9 @@ public class IPAuthentication implements AuthenticationMethod {
public String getName() { public String getName() {
return "ip"; return "ip";
} }
@Override
public boolean isUsed(final Context context, final HttpServletRequest request) {
return false;
}
} }

View File

@@ -83,6 +83,9 @@ public class LDAPAuthentication
protected ConfigurationService configurationService protected ConfigurationService configurationService
= DSpaceServicesFactory.getInstance().getConfigurationService(); = DSpaceServicesFactory.getInstance().getConfigurationService();
private static final String LDAP_AUTHENTICATED = "ldap.authenticated";
/** /**
* Let a real auth method return true if it wants. * Let a real auth method return true if it wants.
* *
@@ -261,6 +264,7 @@ public class LDAPAuthentication
if (ldap.ldapAuthenticate(dn, password, context)) { if (ldap.ldapAuthenticate(dn, password, context)) {
context.setCurrentUser(eperson); context.setCurrentUser(eperson);
request.getSession().setAttribute(LDAP_AUTHENTICATED, true);
// assign user to groups based on ldap dn // assign user to groups based on ldap dn
assignGroups(dn, ldap.ldapGroup, context); assignGroups(dn, ldap.ldapGroup, context);
@@ -311,6 +315,8 @@ public class LDAPAuthentication
context.dispatchEvents(); context.dispatchEvents();
context.restoreAuthSystemState(); context.restoreAuthSystemState();
context.setCurrentUser(eperson); context.setCurrentUser(eperson);
request.getSession().setAttribute(LDAP_AUTHENTICATED, true);
// assign user to groups based on ldap dn // assign user to groups based on ldap dn
assignGroups(dn, ldap.ldapGroup, context); assignGroups(dn, ldap.ldapGroup, context);
@@ -341,6 +347,8 @@ public class LDAPAuthentication
ePersonService.update(context, eperson); ePersonService.update(context, eperson);
context.dispatchEvents(); context.dispatchEvents();
context.setCurrentUser(eperson); context.setCurrentUser(eperson);
request.getSession().setAttribute(LDAP_AUTHENTICATED, true);
// assign user to groups based on ldap dn // assign user to groups based on ldap dn
assignGroups(dn, ldap.ldapGroup, context); assignGroups(dn, ldap.ldapGroup, context);
@@ -734,4 +742,14 @@ public class LDAPAuthentication
} }
} }
} }
@Override
public boolean isUsed(final Context context, final HttpServletRequest request) {
if (request != null &&
context.getCurrentUser() != null &&
request.getSession().getAttribute(LDAP_AUTHENTICATED) != null) {
return true;
}
return false;
}
} }

View File

@@ -51,6 +51,9 @@ public class PasswordAuthentication
*/ */
private static final Logger log = LogManager.getLogger(); private static final Logger log = LogManager.getLogger();
private static final String PASSWORD_AUTHENTICATED = "password.authenticated";
/** /**
* Look to see if this email address is allowed to register. * Look to see if this email address is allowed to register.
@@ -216,6 +219,7 @@ public class PasswordAuthentication
.checkPassword(context, eperson, password)) { .checkPassword(context, eperson, password)) {
// login is ok if password matches: // login is ok if password matches:
context.setCurrentUser(eperson); context.setCurrentUser(eperson);
request.getSession().setAttribute(PASSWORD_AUTHENTICATED, true);
log.info(LogHelper.getHeader(context, "authenticate", "type=PasswordAuthentication")); log.info(LogHelper.getHeader(context, "authenticate", "type=PasswordAuthentication"));
return SUCCESS; return SUCCESS;
} else { } else {
@@ -247,4 +251,15 @@ public class PasswordAuthentication
public String getName() { public String getName() {
return "password"; return "password";
} }
@Override
public boolean isUsed(final Context context, final HttpServletRequest request) {
if (request != null &&
context.getCurrentUser() != null &&
request.getSession().getAttribute(PASSWORD_AUTHENTICATED) != null) {
return true;
}
return false;
}
} }

View File

@@ -1283,5 +1283,14 @@ public class ShibAuthentication implements AuthenticationMethod {
} }
@Override
public boolean isUsed(final Context context, final HttpServletRequest request) {
if (request != null &&
context.getCurrentUser() != null &&
request.getSession().getAttribute("shib.authenticated") != null) {
return true;
}
return false;
}
} }

View File

@@ -128,6 +128,8 @@ public class X509Authentication implements AuthenticationMethod {
protected ConfigurationService configurationService = protected ConfigurationService configurationService =
DSpaceServicesFactory.getInstance().getConfigurationService(); DSpaceServicesFactory.getInstance().getConfigurationService();
private static final String X509_AUTHENTICATED = "x509.authenticated";
/** /**
* Initialization: Set caPublicKey and/or keystore. This loads the * Initialization: Set caPublicKey and/or keystore. This loads the
@@ -544,6 +546,7 @@ public class X509Authentication implements AuthenticationMethod {
context.dispatchEvents(); context.dispatchEvents();
context.restoreAuthSystemState(); context.restoreAuthSystemState();
context.setCurrentUser(eperson); context.setCurrentUser(eperson);
request.getSession().setAttribute(X509_AUTHENTICATED, true);
setSpecialGroupsFlag(request, email); setSpecialGroupsFlag(request, email);
return SUCCESS; return SUCCESS;
} else { } else {
@@ -563,6 +566,7 @@ public class X509Authentication implements AuthenticationMethod {
log.info(LogHelper.getHeader(context, "login", log.info(LogHelper.getHeader(context, "login",
"type=x509certificate")); "type=x509certificate"));
context.setCurrentUser(eperson); context.setCurrentUser(eperson);
request.getSession().setAttribute(X509_AUTHENTICATED, true);
setSpecialGroupsFlag(request, email); setSpecialGroupsFlag(request, email);
return SUCCESS; return SUCCESS;
} }
@@ -594,4 +598,14 @@ public class X509Authentication implements AuthenticationMethod {
public String getName() { public String getName() {
return "x509"; return "x509";
} }
@Override
public boolean isUsed(final Context context, final HttpServletRequest request) {
if (request != null &&
context.getCurrentUser() != null &&
request.getSession().getAttribute(X509_AUTHENTICATED) != null) {
return true;
}
return false;
}
} }

View File

@@ -168,4 +168,13 @@ public interface AuthenticationService {
*/ */
public Iterator<AuthenticationMethod> authenticationMethodIterator(); public Iterator<AuthenticationMethod> authenticationMethodIterator();
/**
* Retrieves the currently used authentication method name based on the context and the request
*
* @param context A valid DSpace context.
* @param request The request that started this operation, or null if not applicable.
* @return the currently used authentication method name
*/
public String getAuthenticationMethod(Context context, HttpServletRequest request);
} }

View File

@@ -98,6 +98,11 @@ public class Context implements AutoCloseable {
*/ */
private List<UUID> specialGroupsPreviousState; private List<UUID> specialGroupsPreviousState;
/**
* The currently used authentication method
*/
private String authenticationMethod;
/** /**
* Content events * Content events
*/ */
@@ -890,4 +895,11 @@ public class Context implements AutoCloseable {
currentUser = reloadEntity(currentUser); currentUser = reloadEntity(currentUser);
} }
public String getAuthenticationMethod() {
return authenticationMethod;
}
public void setAuthenticationMethod(final String authenticationMethod) {
this.authenticationMethod = authenticationMethod;
}
} }

View File

@@ -118,6 +118,7 @@ public class AuthenticationRestController implements InitializingBean {
response.setHeader("WWW-Authenticate", authenticateHeaderValue); response.setHeader("WWW-Authenticate", authenticateHeaderValue);
} }
authenticationStatusRest.setAuthenticationMethod(context.getAuthenticationMethod());
authenticationStatusRest.setProjection(projection); authenticationStatusRest.setProjection(projection);
AuthenticationStatusResource authenticationStatusResource = converter.toResource(authenticationStatusRest); AuthenticationStatusResource authenticationStatusResource = converter.toResource(authenticationStatusRest);

View File

@@ -16,6 +16,7 @@ import org.dspace.app.rest.RestResourceController;
public class AuthenticationStatusRest extends BaseObjectRest<Integer> { public class AuthenticationStatusRest extends BaseObjectRest<Integer> {
private boolean okay; private boolean okay;
private boolean authenticated; private boolean authenticated;
private String authenticationMethod;
public static final String NAME = "status"; public static final String NAME = "status";
public static final String CATEGORY = RestAddressableModel.AUTHENTICATION; public static final String CATEGORY = RestAddressableModel.AUTHENTICATION;
@@ -81,4 +82,12 @@ public class AuthenticationStatusRest extends BaseObjectRest<Integer> {
public void setOkay(boolean okay) { public void setOkay(boolean okay) {
this.okay = okay; this.okay = okay;
} }
public String getAuthenticationMethod() {
return authenticationMethod;
}
public void setAuthenticationMethod(final String authenticationMethod) {
this.authenticationMethod = authenticationMethod;
}
} }

View File

@@ -0,0 +1,54 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.security.jwt;
import java.sql.SQLException;
import java.text.ParseException;
import javax.servlet.http.HttpServletRequest;
import com.nimbusds.jwt.JWTClaimsSet;
import org.dspace.authenticate.service.AuthenticationService;
import org.dspace.core.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Provides a claim for a JSON Web Token, this claim is responsible for adding the authentication method to it
*/
@Component
public class AuthenticationMethodClaimProvider implements JWTClaimProvider {
public static final String AUTHENTICATION_METHOD = "authenticationMethod";
private static final Logger log = LoggerFactory.getLogger(AuthenticationMethodClaimProvider.class);
@Autowired
private AuthenticationService authenticationService;
public String getKey() {
return AUTHENTICATION_METHOD;
}
public Object getValue(final Context context, final HttpServletRequest request) {
if (context.getAuthenticationMethod() != null) {
return context.getAuthenticationMethod();
}
return authenticationService.getAuthenticationMethod(context, request);
}
public void parseClaim(final Context context, final HttpServletRequest request, final JWTClaimsSet jwtClaimsSet)
throws SQLException {
try {
context.setAuthenticationMethod(jwtClaimsSet.getStringClaim(AUTHENTICATION_METHOD));
} catch (ParseException e) {
log.error(e.getMessage(), e);
}
}
}

View File

@@ -107,6 +107,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true))) .andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.authenticationMethod", is("password")))
.andExpect(jsonPath("$.type", is("status"))) .andExpect(jsonPath("$.type", is("status")))
.andExpect(jsonPath("$._links.eperson.href", startsWith(REST_SERVER_URL))) .andExpect(jsonPath("$._links.eperson.href", startsWith(REST_SERVER_URL)))
@@ -136,6 +137,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true))) .andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.authenticationMethod", is("password")))
.andExpect(jsonPath("$.type", is("status"))) .andExpect(jsonPath("$.type", is("status")))
.andExpect(jsonPath("$._links.eperson.href", startsWith(REST_SERVER_URL))) .andExpect(jsonPath("$._links.eperson.href", startsWith(REST_SERVER_URL)))
@@ -159,6 +161,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(false))) .andExpect(jsonPath("$.authenticated", is(false)))
.andExpect(jsonPath("$.authenticationMethod").doesNotExist())
.andExpect(jsonPath("$.type", is("status"))) .andExpect(jsonPath("$.type", is("status")))
.andExpect(header().string("WWW-Authenticate", .andExpect(header().string("WWW-Authenticate",
"password realm=\"DSpace REST API\"")); "password realm=\"DSpace REST API\""));
@@ -213,6 +216,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true))) .andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.authenticationMethod", is("shibboleth")))
.andExpect(jsonPath("$.type", is("status"))) .andExpect(jsonPath("$.type", is("status")))
// Verify that the CSRF token has NOT been changed... status checks won't change the token // Verify that the CSRF token has NOT been changed... status checks won't change the token
// (only login/logout will) // (only login/logout will)
@@ -244,6 +248,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true))) .andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.authenticationMethod", is("shibboleth")))
.andExpect(jsonPath("$.type", is("status"))); .andExpect(jsonPath("$.type", is("status")));
//Logout, invalidating the token //Logout, invalidating the token
@@ -260,12 +265,18 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
// Verify /api/authn/shibboleth endpoint does not work // Verify /api/authn/shibboleth endpoint does not work
// NOTE: this is the same call as in testStatusShibAuthenticatedWithCookie()) // NOTE: this is the same call as in testStatusShibAuthenticatedWithCookie())
getClient().perform(get("/api/authn/shibboleth") String token = getClient().perform(get("/api/authn/shibboleth")
.header("Referer", "https://myshib.example.com") .header("Referer", "https://myshib.example.com")
.param("redirectUrl", uiURL) .param("redirectUrl", uiURL)
.requestAttr("SHIB-MAIL", eperson.getEmail()) .requestAttr("SHIB-MAIL", eperson.getEmail())
.requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff")) .requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
.andExpect(status().isUnauthorized()); .andExpect(status().isUnauthorized())
.andReturn().getResponse().getHeader("Authorization");
getClient(token).perform(get("/api/authn/status"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.authenticated", is(false)))
.andExpect(jsonPath("$.authenticationMethod").doesNotExist());
} }
// NOTE: This test is similar to testStatusShibAuthenticatedWithCookie(), but proves the same process works // NOTE: This test is similar to testStatusShibAuthenticatedWithCookie(), but proves the same process works
@@ -453,6 +464,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(false))) .andExpect(jsonPath("$.authenticated", is(false)))
.andExpect(jsonPath("$.authenticationMethod").doesNotExist())
.andExpect(jsonPath("$.type", is("status"))); .andExpect(jsonPath("$.type", is("status")));
} }
@@ -515,6 +527,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true))) .andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.authenticationMethod", is("password")))
.andExpect(jsonPath("$.type", is("status"))); .andExpect(jsonPath("$.type", is("status")));
// Logout, invalidating token // Logout, invalidating token
@@ -858,6 +871,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true))) .andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.authenticationMethod", is("shibboleth")))
.andExpect(jsonPath("$.type", is("status"))) .andExpect(jsonPath("$.type", is("status")))
.andExpect(jsonPath("$._links.eperson.href", startsWith(REST_SERVER_URL))) .andExpect(jsonPath("$._links.eperson.href", startsWith(REST_SERVER_URL)))
.andExpect(jsonPath("$._embedded.eperson", .andExpect(jsonPath("$._embedded.eperson",
@@ -901,6 +915,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true))) .andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.authenticationMethod", is("shibboleth")))
.andExpect(jsonPath("$.type", is("status"))) .andExpect(jsonPath("$.type", is("status")))
.andExpect(jsonPath("$._links.eperson.href", startsWith(REST_SERVER_URL))) .andExpect(jsonPath("$._links.eperson.href", startsWith(REST_SERVER_URL)))
.andExpect(jsonPath("$._embedded.eperson", .andExpect(jsonPath("$._embedded.eperson",
@@ -921,6 +936,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true))) .andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.authenticationMethod", is("shibboleth")))
.andExpect(jsonPath("$.type", is("status"))) .andExpect(jsonPath("$.type", is("status")))
.andExpect(jsonPath("$._links.eperson.href", startsWith(REST_SERVER_URL))) .andExpect(jsonPath("$._links.eperson.href", startsWith(REST_SERVER_URL)))
.andExpect(jsonPath("$._embedded.eperson", .andExpect(jsonPath("$._embedded.eperson",
@@ -954,6 +970,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true))) .andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.authenticationMethod", is("password")))
.andExpect(jsonPath("$.type", is("status"))); .andExpect(jsonPath("$.type", is("status")));
//Logout //Logout
@@ -965,6 +982,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(false))) .andExpect(jsonPath("$.authenticated", is(false)))
.andExpect(jsonPath("$.authenticationMethod").doesNotExist())
.andExpect(jsonPath("$.type", is("status"))); .andExpect(jsonPath("$.type", is("status")));
//Simulate that a shibboleth authentication has happened //Simulate that a shibboleth authentication has happened
@@ -979,6 +997,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true))) .andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.authenticationMethod", is("shibboleth")))
.andExpect(jsonPath("$.type", is("status"))); .andExpect(jsonPath("$.type", is("status")));
//Logout //Logout
@@ -990,6 +1009,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$.okay", is(true))) .andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(false))) .andExpect(jsonPath("$.authenticated", is(false)))
.andExpect(jsonPath("$.authenticationMethod").doesNotExist())
.andExpect(jsonPath("$.type", is("status"))); .andExpect(jsonPath("$.type", is("status")));
} }

View File

@@ -7,7 +7,9 @@
*/ */
package org.dspace.app.rest; package org.dspace.app.rest;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -47,9 +49,16 @@ public class ShibbolethRestControllerIT extends AbstractControllerIntegrationTes
// unauthenticated, but it must include some expected SHIB attributes. // unauthenticated, but it must include some expected SHIB attributes.
// SHIB-MAIL attribute is the default email header sent from Shibboleth after a successful login. // SHIB-MAIL attribute is the default email header sent from Shibboleth after a successful login.
// In this test we are simply mocking that behavior by setting it to an existing EPerson. // In this test we are simply mocking that behavior by setting it to an existing EPerson.
getClient().perform(get("/api/authn/shibboleth").requestAttr("SHIB-MAIL", eperson.getEmail())) String token = getClient().perform(get("/api/authn/shibboleth").requestAttr("SHIB-MAIL", eperson.getEmail()))
.andExpect(status().is3xxRedirection()) .andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("http://localhost:4000")); .andExpect(redirectedUrl("http://localhost:4000"))
.andReturn().getResponse().getHeader("Authorization");
getClient(token).perform(get("/api/authn/status"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.authenticationMethod", is("shibboleth")));
} }
@Test @Test