package org.gcube.portal.oidc.lr62; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; import java.util.Calendar; import java.util.Locale; import java.util.UUID; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.gcube.oidc.URLEncodedContextMapper; import org.gcube.oidc.rest.JWTToken; import org.gcube.oidc.rest.OpenIdConnectRESTHelper; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.exception.SystemException; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.util.LocaleUtil; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.model.User; import com.liferay.portal.security.auth.BaseAutoLogin; import com.liferay.portal.service.ServiceContext; import com.liferay.portal.service.UserLocalServiceUtil; import com.liferay.portal.util.PortalUtil; import com.liferay.util.PwdGenerator; public class OpenIdConnectAutoLogin extends BaseAutoLogin { private static final Log log = LogFactoryUtil.getLog(OpenIdConnectAutoLogin.class); 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 { JWTToken token = JWTTokenUtil.getOIDCFromRequest(request); if (token == null) { if (log.isTraceEnabled()) { log.trace("OIDC token is null. Can't perform auto login"); } return null; } else { if (log.isDebugEnabled()) { log.debug("Perform auto login with OIDC token " + token.getTokenEssentials()); } } LiferayOpenIdConnectConfiguration configuration = LiferayOpenIdConnectConfiguration.getConfiguration(request); long companyId = PortalUtil.getCompanyId(request); long groupId = PortalUtil.getScopeGroupId(request); String portalURL = PortalUtil.getPortalURL(request, true); User user = createOrUpdateUser(token, companyId, groupId, portalURL, configuration); if (user != null) { log.info("Applying sites and roles strategy"); try { UserSitesToGroupsAndRolesMapper mapper = new UserSitesToGroupsAndRolesMapper( user, new URLEncodedContextMapper( token.getResourceNameToAccessRolesMap(Arrays.asList(JWTToken.ACCOUNT_RESOURCE)))); mapper.map(); } catch (Throwable t) { // TODO: to be removed when tested in depth log.error("Applying strategy", t); } 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"); return null; } } 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(); User user = null; try { user = UserLocalServiceUtil.fetchUserByScreenName(companyId, username); if (user == null) { // 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) { 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.warn("Unexisting user will not be created according to configuration"); return null; } } else if (log.isDebugEnabled()) { log.debug("User found by its openId, other info will be updated"); } } boolean updateUser = false; if (user != null) { 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 != null && !family.equals(user.getLastName())) { if (log.isTraceEnabled()) { log.trace("Last name is changed"); } user.setLastName(family); updateUser = true; } 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); } try { byte[] userAvatar = OpenIdConnectRESTHelper.getUserAvatar(configuration.getAvatarURL(), token); if (userAvatar != null && userAvatar.length > 0) { if (ASSURE_AVATAR_FORMAT) { 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(); 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(); if (log.isDebugEnabled()) { log.debug("Reading converted image from the BAOS"); } userAvatar = baos.toByteArray(); } else { log.warn("Buffered image read is null!"); } } if (log.isDebugEnabled()) { log.debug("Saving the retrieved avatar as user's portrait"); } UserLocalServiceUtil.updatePortrait(user.getUserId(), userAvatar); } 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) { log.error("Cannot save/update/delete user's portrait", t); } } catch (SystemException | PortalException e) { throw new RuntimeException(e); } return user; } public static User addUser(long companyId, long groupId, String portalURL, String emailAddress, String firstName, String lastName, String openid, String username) throws SystemException, PortalException { Locale locale = LocaleUtil.getMostRelevantLocale(); long creatorUserId = 0; boolean autoPassword = false; String password1 = PwdGenerator.getPassword(); String password2 = password1; boolean autoScreenName = username == null; String screenName = StringPool.BLANK; if (autoScreenName) { if (log.isDebugEnabled()) { log.debug("Screen name will be auto-generated"); } } else { if (log.isDebugEnabled()) { log.debug("Screen name will be set to: " + username); } screenName = username; } long facebookId = 0; String openId = openid; String middleName = StringPool.BLANK; int prefixId = 0; int suffixId = 0; boolean male = true; int birthdayMonth = Calendar.JANUARY; int birthdayDay = 1; int birthdayYear = 1970; String jobTitle = StringPool.BLANK; long[] groupIds = null; long[] organizationIds = null; long[] roleIds = null; long[] userGroupIds = null; boolean sendEmail = false; ServiceContext serviceContext = new ServiceContext(); serviceContext.setScopeGroupId(groupId); serviceContext.setPortalURL(portalURL); User user = UserLocalServiceUtil.addUser(creatorUserId, companyId, autoPassword, password1, password2, autoScreenName, screenName, emailAddress, facebookId, openId, locale, firstName, middleName, lastName, prefixId, suffixId, male, birthdayMonth, birthdayDay, birthdayYear, jobTitle, groupIds, organizationIds, roleIds, userGroupIds, sendEmail, serviceContext); // No password user.setPasswordReset(false); // email is already verified by oidc provider user.setEmailAddressVerified(true); // No reminder query at first login. user.setReminderQueryQuestion("x"); user.setReminderQueryAnswer("y"); UserLocalServiceUtil.updateUser(user); return user; } }