
org.dspace.authenticate.OidcAuthenticationBean Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dspace-api Show documentation
Show all versions of dspace-api Show documentation
DSpace core data model and service APIs.
The newest version!
/**
* 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.authenticate;
import static java.lang.String.format;
import static java.net.URLEncoder.encode;
import static org.apache.commons.lang.BooleanUtils.toBoolean;
import static org.apache.commons.lang3.StringUtils.isAnyBlank;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.io.UnsupportedEncodingException;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.authenticate.oidc.OidcClient;
import org.dspace.authenticate.oidc.model.OidcTokenResponseDTO;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.service.EPersonService;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* OpenID Connect Authentication for DSpace.
*
* This implementation doesn't allow/needs to register user, which may be holder
* by the openID authentication server.
*
* @link https://openid.net/developers/specs/
*
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
*/
public class OidcAuthenticationBean implements AuthenticationMethod {
public static final String OIDC_AUTH_ATTRIBUTE = "oidc";
private final static String LOGIN_PAGE_URL_FORMAT = "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s";
private static final Logger LOGGER = LogManager.getLogger();
private static final String OIDC_AUTHENTICATED = "oidc.authenticated";
@Autowired
private ConfigurationService configurationService;
@Autowired
private OidcClient oidcClient;
@Autowired
private EPersonService ePersonService;
@Override
public boolean allowSetPassword(Context context, HttpServletRequest request, String username) throws SQLException {
return false;
}
@Override
public boolean isImplicit() {
return false;
}
@Override
public boolean canSelfRegister(Context context, HttpServletRequest request, String username) throws SQLException {
return canSelfRegister();
}
@Override
public void initEPerson(Context context, HttpServletRequest request, EPerson eperson) throws SQLException {
}
@Override
public List getSpecialGroups(Context context, HttpServletRequest request) throws SQLException {
return List.of();
}
@Override
public String getName() {
return OIDC_AUTH_ATTRIBUTE;
}
@Override
public int authenticate(Context context, String username, String password, String realm, HttpServletRequest request)
throws SQLException {
if (request == null) {
LOGGER.warn("Unable to authenticate using OIDC because the request object is null.");
return BAD_ARGS;
}
if (request.getAttribute(OIDC_AUTH_ATTRIBUTE) == null) {
return NO_SUCH_USER;
}
String code = (String) request.getParameter("code");
if (StringUtils.isEmpty(code)) {
LOGGER.warn("The incoming request has not code parameter");
return NO_SUCH_USER;
}
return authenticateWithOidc(context, code, request);
}
private int authenticateWithOidc(Context context, String code, HttpServletRequest request) throws SQLException {
OidcTokenResponseDTO accessToken = getOidcAccessToken(code);
if (accessToken == null) {
LOGGER.warn("No access token retrieved by code");
return NO_SUCH_USER;
}
Map userInfo = getOidcUserInfo(accessToken.getAccessToken());
String email = getAttributeAsString(userInfo, getEmailAttribute());
if (StringUtils.isBlank(email)) {
LOGGER.warn("No email found in the user info attributes");
return NO_SUCH_USER;
}
EPerson ePerson = ePersonService.findByEmail(context, email);
if (ePerson != null) {
request.setAttribute(OIDC_AUTHENTICATED, true);
return ePerson.canLogIn() ? logInEPerson(context, ePerson) : BAD_ARGS;
}
// if self registration is disabled, warn about this failure to find a matching eperson
if (! canSelfRegister()) {
LOGGER.warn("Self registration is currently disabled for OIDC, and no ePerson could be found for email: {}",
email);
}
return canSelfRegister() ? registerNewEPerson(context, userInfo, email) : NO_SUCH_USER;
}
@Override
public String loginPageURL(Context context, HttpServletRequest request, HttpServletResponse response) {
String authorizeUrl = configurationService.getProperty("authentication-oidc.authorize-endpoint");
String clientId = configurationService.getProperty("authentication-oidc.client-id");
String clientSecret = configurationService.getProperty("authentication-oidc.client-secret");
String redirectUri = configurationService.getProperty("authentication-oidc.redirect-url");
String tokenUrl = configurationService.getProperty("authentication-oidc.token-endpoint");
String userInfoUrl = configurationService.getProperty("authentication-oidc.user-info-endpoint");
String[] defaultScopes =
new String[] {
"openid", "email", "profile"
};
String scopes = String.join(" ", configurationService.getArrayProperty("authentication-oidc.scopes",
defaultScopes));
if (isAnyBlank(authorizeUrl, clientId, redirectUri, clientSecret, tokenUrl, userInfoUrl)) {
LOGGER.error("Missing mandatory configuration properties for OidcAuthenticationBean");
// prepare a Map of the properties which can not have sane defaults, but are still required
final Map map = Map.of("authorizeUrl", authorizeUrl, "clientId", clientId, "redirectUri",
redirectUri, "clientSecret", clientSecret, "tokenUrl", tokenUrl, "userInfoUrl", userInfoUrl);
final Iterator> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
final Entry entry = iterator.next();
if (isBlank(entry.getValue())) {
LOGGER.error(" * {} is missing", entry::getKey);
}
}
return "";
}
try {
return format(LOGIN_PAGE_URL_FORMAT, authorizeUrl, clientId, scopes, encode(redirectUri, "UTF-8"));
} catch (UnsupportedEncodingException e) {
LOGGER.error(e::getMessage, e);
return "";
}
}
private int logInEPerson(Context context, EPerson ePerson) {
context.setCurrentUser(ePerson);
return SUCCESS;
}
private int registerNewEPerson(Context context, Map userInfo, String email) throws SQLException {
try {
context.turnOffAuthorisationSystem();
EPerson eperson = ePersonService.create(context);
eperson.setNetid(email);
eperson.setEmail(email);
String firstName = getAttributeAsString(userInfo, getFirstNameAttribute());
if (firstName != null) {
eperson.setFirstName(context, firstName);
}
String lastName = getAttributeAsString(userInfo, getLastNameAttribute());
if (lastName != null) {
eperson.setLastName(context, lastName);
}
eperson.setCanLogIn(true);
eperson.setSelfRegistered(true);
ePersonService.update(context, eperson);
context.setCurrentUser(eperson);
context.dispatchEvents();
return SUCCESS;
} catch (Exception ex) {
LOGGER.error("An error occurs registering a new EPerson from OIDC", ex);
return NO_SUCH_USER;
} finally {
context.restoreAuthSystemState();
}
}
private OidcTokenResponseDTO getOidcAccessToken(String code) {
try {
return oidcClient.getAccessToken(code);
} catch (Exception ex) {
LOGGER.error("An error occurs retrieving the OIDC access_token", ex);
return null;
}
}
private Map getOidcUserInfo(String accessToken) {
try {
return oidcClient.getUserInfo(accessToken);
} catch (Exception ex) {
LOGGER.error("An error occurs retrieving the OIDC user info", ex);
return Map.of();
}
}
private String getAttributeAsString(Map userInfo, String attribute) {
if (isBlank(attribute)) {
return null;
}
return userInfo.containsKey(attribute) ? String.valueOf(userInfo.get(attribute)) : null;
}
private String getEmailAttribute() {
return configurationService.getProperty("authentication-oidc.user-info.email", "email");
}
private String getFirstNameAttribute() {
return configurationService.getProperty("authentication-oidc.user-info.first-name", "given_name");
}
private String getLastNameAttribute() {
return configurationService.getProperty("authentication-oidc.user-info.last-name", "family_name");
}
private boolean canSelfRegister() {
String canSelfRegister = configurationService.getProperty("authentication-oidc.can-self-register", "true");
if (isBlank(canSelfRegister)) {
return true;
}
return toBoolean(canSelfRegister);
}
public OidcClient getOidcClient() {
return this.oidcClient;
}
public void setOidcClient(OidcClient oidcClient) {
this.oidcClient = oidcClient;
}
@Override
public boolean isUsed(final Context context, final HttpServletRequest request) {
if (request != null &&
context.getCurrentUser() != null &&
request.getAttribute(OIDC_AUTHENTICATED) != null) {
return true;
}
return false;
}
@Override
public boolean canChangePassword(Context context, EPerson ePerson, String currentPassword) {
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy