Compare commits

...

7 Commits

5 changed files with 120 additions and 39 deletions

View File

@ -2,6 +2,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
# Changelog for "oidc-enrollment-hook"
## [v1.2.0]
- Now an UMA token is issued also after the login in the `PostLoginAction` to assure token's presence for the context without have to wait the next HTTP call and the Valve's intervention, in the case when it is not necessary the redirect to an origin URI after the login. (#20591)
## [v1.1.3]
- Now user reconciliation/identification from OIDC token after the login is performed no more checking by using the email address but by using the User's username, the Liferay `screenname`. (#20827) (#20840)
## [v1.1.2]
- Added some info of the user is about to create in the logs expecially for screen name (auto-generated or externally provided) (#20413). Restored per-session token removal. Logs revised. (#20445)

View File

@ -1,6 +1,6 @@
# Event Publisher Library
# OIDC Enrollment Hook
**Event Publisher library** is a [Liferay](https://liferay.com) 6.2 hook that and basically implements OIDC portal login in the gCube framework
**OIDC Enrollment Hook** is a [Liferay](https://liferay.com) 6.2 hook that basically implements OIDC portal login in the gCube framework
## Structure of the project

31
pom.xml
View File

@ -1,17 +1,21 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>maven-parent</artifactId>
<groupId>org.gcube.tools</groupId>
<version>1.1.0</version>
<relativePath />
</parent>
<groupId>org.gcube.portal</groupId>
<artifactId>oidc-enrollment-hook</artifactId>
<packaging>war</packaging>
<version>1.1.2</version>
<version>1.2.0</version>
<properties>
<liferay.version>6.2.5</liferay.version>
<liferay.maven.plugin.version>6.2.10.12</liferay.maven.plugin.version>
@ -20,6 +24,7 @@
<liferay.app.server.lib.global.dir>/Users/themaxx/Development/Server/liferay-portal-6.2-ce-ga6/tomcat-7.0.62/lib/ext</liferay.app.server.lib.global.dir>
<liferay.app.server.portal.dir>/Users/themaxx/Development/Server/liferay-portal-6.2-ce-ga6/tomcat-7.0.62/webapps/ROOT</liferay.app.server.portal.dir>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
@ -31,11 +36,13 @@
</dependency>
</dependencies>
</dependencyManagement>
<scm>
<connection>scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</connection>
<developerConnection>scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</developerConnection>
<url>https://code-repo.d4science.org/gCubeSystem/${project.artifactId}</url>
</scm>
<scm>
<connection>scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</connection>
<developerConnection>scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</developerConnection>
<url>https://code-repo.d4science.org/gCubeSystem/${project.artifactId}</url>
</scm>
<dependencies>
<dependency>
<groupId>org.gcube.common</groupId>
@ -49,6 +56,16 @@
<version>[1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT)</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.gcube.common.portal</groupId>
<artifactId>portal-manager</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-scope</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.liferay.portal</groupId>
<artifactId>portal-service</artifactId>
@ -70,6 +87,7 @@
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
@ -102,4 +120,5 @@
<!-- </plugin> -->
</plugins>
</build>
</project>

View File

@ -33,8 +33,9 @@ public class OpenIdConnectAutoLogin extends BaseAutoLogin {
private static final Log log = LogFactoryUtil.getLog(OpenIdConnectAutoLogin.class);
private static boolean ASSURE_AVATAR_FORMAT = true;
private static String DEFAULT_AVATAR_FORMAT = "png";
private static final boolean ASSURE_AVATAR_FORMAT = true;
private static final String DEFAULT_AVATAR_FORMAT = "png";
private static final boolean DELETE_AVATAR_IF_NOT_FOUND_ON_SERVER = false;
@Override
public String[] doLogin(HttpServletRequest request, HttpServletResponse response) throws Exception {
@ -66,7 +67,9 @@ public class OpenIdConnectAutoLogin extends BaseAutoLogin {
// TODO: to be removed when tested in depth
log.error("Applying strategy", t);
}
log.debug("Returning logged in user's info");
if (log.isDebugEnabled()) {
log.debug("Returning logged in user's info");
}
return new String[] { String.valueOf(user.getUserId()), UUID.randomUUID().toString(), "false" };
} else {
log.warn("User is null");
@ -77,52 +80,75 @@ public class OpenIdConnectAutoLogin extends BaseAutoLogin {
public static User createOrUpdateUser(JWTToken token, long companyId, long groupId, String portalURL,
LiferayOpenIdConnectConfiguration configuration) throws Exception {
String username = token.getUserName();
String email = token.getEmail();
String given = token.getGiven();
String family = token.getFamily();
String subject = token.getSub();
String username = token.getUserName();
User user = null;
try {
boolean updateUser = false;
// Search by email first
user = UserLocalServiceUtil.fetchUserByEmailAddress(companyId, email);
user = UserLocalServiceUtil.fetchUserByScreenName(companyId, username);
if (user == null) {
log.debug("No Liferay user found with email address=" + email + ", trying with openId");
// Then search by openId, in case user has changed the email address
// Then search by openId, in case an admin changed the username on OIDC server
if (log.isDebugEnabled()) {
log.debug("No Liferay user found with username=" + username + ", trying with openId");
}
user = UserLocalServiceUtil.fetchUserByOpenId(companyId, subject);
if (user == null) {
log.debug("No Liferay user found with openid=" + subject + " and email address=" + email);
if (log.isDebugEnabled()) {
log.debug("No Liferay user found with openid=" + subject + " and username=" + username);
}
if (configuration.createUnexistingUser()) {
log.info("A new user will be created [email=" + email + ",given=" + given + ",family=" + family
+ ",subject=" + subject + ",username=" + username);
user = addUser(companyId, groupId, portalURL, email, given, family, subject, username);
} else {
log.info("User will not be created according to configuration");
log.warn("Unexisting user will not be created according to configuration");
return null;
}
} else {
log.info("User found by its openId, the email will be updated");
updateUser = true;
} else if (log.isDebugEnabled()) {
log.debug("User found by its openId, other info will be updated");
}
}
boolean updateUser = false;
if (user != null) {
log.debug("User found, updating name details with info from userinfo if changed");
if (given != user.getFirstName()) {
if (log.isDebugEnabled()) {
log.debug("User found, checking its details against userinfo for changes");
}
if (given != null && !given.equals(user.getFirstName())) {
if (log.isTraceEnabled()) {
log.trace("Given name is changed");
}
user.setFirstName(given);
updateUser = true;
}
if (family != user.getLastName()) {
if (family != null && !family.equals(user.getLastName())) {
if (log.isTraceEnabled()) {
log.trace("Last name is changed");
}
user.setLastName(family);
updateUser = true;
}
if (email != user.getEmailAddress()) {
if (email != null && !email.equals(user.getEmailAddress())) {
if (log.isTraceEnabled()) {
log.trace("Email address is changed");
}
user.setEmailAddress(email);
updateUser = true;
}
if (subject != null && !subject.equals(user.getOpenId())) {
if (log.isTraceEnabled()) {
log.trace("Setting OOID subject as openid");
}
user.setOpenId(subject);
updateUser = true;
}
}
if (updateUser) {
if (log.isDebugEnabled()) {
log.debug("Updating user's details with info from userinfo");
}
UserLocalServiceUtil.updateUser(user);
}
@ -130,25 +156,35 @@ public class OpenIdConnectAutoLogin extends BaseAutoLogin {
byte[] userAvatar = OpenIdConnectRESTHelper.getUserAvatar(configuration.getAvatarURL(), token);
if (userAvatar != null && userAvatar.length > 0) {
if (ASSURE_AVATAR_FORMAT) {
log.debug("Assuring avatar image format as: " + DEFAULT_AVATAR_FORMAT);
log.debug("Reading image stream with length: " + userAvatar.length);
if (log.isDebugEnabled()) {
log.debug("Assuring avatar image format as: " + DEFAULT_AVATAR_FORMAT);
log.debug("Reading image stream with length: " + userAvatar.length);
}
BufferedImage bi = ImageIO.read(new ByteArrayInputStream(userAvatar));
if (bi != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
log.debug("Converting avatar stream image format to: " + DEFAULT_AVATAR_FORMAT);
if (log.isDebugEnabled()) {
log.debug("Converting avatar stream image format to: " + DEFAULT_AVATAR_FORMAT);
}
ImageIO.write(bi, DEFAULT_AVATAR_FORMAT, baos);
baos.flush();
baos.close();
log.debug("Reading converted image from the BAOS");
if (log.isDebugEnabled()) {
log.debug("Reading converted image from the BAOS");
}
userAvatar = baos.toByteArray();
} else {
log.warn("Buffered image read is null!");
}
}
log.debug("Saving the retrieved avatar as user's portrait");
if (log.isDebugEnabled()) {
log.debug("Saving the retrieved avatar as user's portrait");
}
UserLocalServiceUtil.updatePortrait(user.getUserId(), userAvatar);
} else {
log.debug("Deleting the user's portrait since no avatar has been found for the user");
} else if (DELETE_AVATAR_IF_NOT_FOUND_ON_SERVER) {
if (log.isDebugEnabled()) {
log.debug("Deleting the user's portrait since no avatar has been found for the user");
}
UserLocalServiceUtil.deletePortrait(user.getUserId());
}
} catch (Throwable t) {
@ -171,9 +207,13 @@ public class OpenIdConnectAutoLogin extends BaseAutoLogin {
boolean autoScreenName = username == null;
String screenName = StringPool.BLANK;
if (autoScreenName) {
log.debug("Screen name will be auto-generated");
if (log.isDebugEnabled()) {
log.debug("Screen name will be auto-generated");
}
} else {
log.debug("Screen name will be set to: " + username);
if (log.isDebugEnabled()) {
log.debug("Screen name will be set to: " + username);
}
screenName = username;
}
long facebookId = 0;
@ -211,4 +251,4 @@ public class OpenIdConnectAutoLogin extends BaseAutoLogin {
return user;
}
}
}

View File

@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.gcube.common.portal.PortalContext;
import org.gcube.oidc.rest.JWTToken;
import com.liferay.portal.kernel.events.Action;
@ -20,6 +21,8 @@ public class PostLoginAction extends Action {
protected static final Log log = LogFactoryUtil.getLog(PostLoginAction.class);
public static boolean REQUEST_UMA_ALSO_WITH_REDIRECT = true;
@Override
public void run(HttpServletRequest request, HttpServletResponse response) throws ActionException {
if (log.isInfoEnabled()) {
@ -27,6 +30,7 @@ public class PostLoginAction extends Action {
}
JWTToken token = JWTTokenUtil.getOIDCFromRequest(request);
HttpSession session = request.getSession(false);
String redirect = (String) request.getAttribute(OpenIdConnectLoginFilter.REDIRECT_ATTRIBUTE);;
if (token != null && session != null) {
User user = (User) session.getAttribute(WebKeys.USER);
if (user != null) {
@ -38,6 +42,15 @@ public class PostLoginAction extends Action {
log.error("User object not found in session " + session.getId() + " ["
+ Integer.toHexString(session.hashCode()) + "]");
}
if (redirect == null || REQUEST_UMA_ALSO_WITH_REDIRECT) {
if (log.isDebugEnabled()) {
log.debug("Getting current infrastructure context via portal context class");
}
String currentContext = "/" + PortalContext.getConfiguration().getInfrastructureName();
OIDCUmaUtil.checkUMATicketAndProvideInThreadLocal(request, response, user, session, currentContext);
} else if (log.isDebugEnabled()) {
log.debug("UMA token will be set by the valve after the redirection to: " + redirect);
}
} else {
if (token == null) {
log.error("OIDC token object is null in request");
@ -46,18 +59,21 @@ public class PostLoginAction extends Action {
log.error("Session is null");
}
}
String redirect = (String) request.getAttribute(OpenIdConnectLoginFilter.REDIRECT_ATTRIBUTE);
if (redirect != null) {
if (log.isDebugEnabled()) {
log.debug("Redirecting to the original requested URI: " + redirect);
}
try {
// I'm not sure I can use this LR facility since it's used also by landing-page-hook.
// Indeed perhaps it should also be discussed if it takes precedence over this redirect in the case.
// session.setAttribute(WebKeys.LAST_PATH, new LastPath(null, URLDecoder.decode(redirect, "UTF-8"))
response.sendRedirect(URLDecoder.decode(redirect, "UTF-8"));
} catch (IOException e) {
new ActionException("Redirecting to original requested URI: " + redirect, e);
}
} else if (log.isTraceEnabled()) {
log.trace("No original requested URI has been found in session");
} else if (log.isDebugEnabled()) {
log.debug("No original requested URI has been found in session");
}
}