From d324747a5715e76d7ec92c8215bc1662751665d2 Mon Sep 17 00:00:00 2001 From: Alex Borodin Date: Wed, 21 Feb 2018 13:47:44 +0100 Subject: [PATCH] feat: make SSOAutoLogin actually work add possibility to map organizations from external org codes to Liferay organizations. Create Liferay organizations and users when necessary, (re-)assign user to org when necessary. --- .../portlets/signup/SignupPortletUtils.java | 3 - .../users/HttpServletRequestAdapter.java | 51 ++++++ .../sw360/portal/users/LandingPageAction.java | 54 ++++++ .../portal/users/OrganizationHelper.java | 159 ++++++++++++++++++ .../portal/users/PortletRequestAdapter.java | 53 ++++++ .../sw360/portal/users/RequestAdapter.java | 22 +++ .../sw360/portal/users/SSOAutoLogin.java | 93 +++++++--- .../sw360/portal/users/UserPortletUtils.java | 72 ++++---- .../src/main/resources/orgmapping.properties | 28 +++ .../src/main/resources/portal.properties | 7 +- .../src/main/resources/sw360.properties | 9 +- 11 files changed, 484 insertions(+), 67 deletions(-) create mode 100644 frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/HttpServletRequestAdapter.java create mode 100644 frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/LandingPageAction.java create mode 100644 frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/OrganizationHelper.java create mode 100644 frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/PortletRequestAdapter.java create mode 100644 frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/RequestAdapter.java create mode 100644 frontend/sw360-portlet/src/main/resources/orgmapping.properties diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/signup/SignupPortletUtils.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/signup/SignupPortletUtils.java index 0615cf3c5..4994f4c00 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/signup/SignupPortletUtils.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/signup/SignupPortletUtils.java @@ -10,13 +10,10 @@ */ package org.eclipse.sw360.portal.portlets.signup; -import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; -import org.eclipse.sw360.datahandler.thrift.components.Component; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.portal.common.PortletUtils; import javax.portlet.PortletRequest; -import java.util.HashSet; /** * Signup portlet utils diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/HttpServletRequestAdapter.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/HttpServletRequestAdapter.java new file mode 100644 index 000000000..91b52ce27 --- /dev/null +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/HttpServletRequestAdapter.java @@ -0,0 +1,51 @@ +/* + * Copyright Siemens AG, 2018. Part of the SW360 Portal Project. + * + * SPDX-License-Identifier: EPL-1.0 + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.sw360.portal.users; + +import com.liferay.portal.kernel.exception.PortalException; +import com.liferay.portal.kernel.exception.SystemException; +import com.liferay.portal.kernel.servlet.SessionMessages; +import com.liferay.portal.service.ServiceContext; +import com.liferay.portal.service.ServiceContextFactory; + +import javax.servlet.http.HttpServletRequest; +import java.util.Optional; +import java.util.function.Consumer; + +class HttpServletRequestAdapter implements RequestAdapter { + // this constant is supposed to be defined in WebKeys according to docs found online, but it's not + private static final String COMPANY_ID = "COMPANY_ID"; + + private HttpServletRequest request; + + HttpServletRequestAdapter(HttpServletRequest request) { + this.request = request; + } + + @Override + public long getCompanyId() { + return (long) request.getAttribute(COMPANY_ID); + } + + @Override + public Consumer getErrorMessagesConsumer() { + return msg -> SessionMessages.add(request, "request_processed", msg); + } + + @Override + public Optional getServiceContext() { + try { + return Optional.of(ServiceContextFactory.getInstance(request)); + } catch (PortalException | SystemException e ) { + return Optional.empty(); + } + } +} diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/LandingPageAction.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/LandingPageAction.java new file mode 100644 index 000000000..7b503ab9e --- /dev/null +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/LandingPageAction.java @@ -0,0 +1,54 @@ +/* + * Copyright Siemens AG, 2018. Part of the SW360 Portal Project. + * + * SPDX-License-Identifier: EPL-1.0 + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.sw360.portal.users; + +import com.liferay.portal.kernel.events.Action; +import com.liferay.portal.kernel.events.ActionException; +import com.liferay.portal.kernel.util.PropsUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static com.google.common.base.Strings.isNullOrEmpty; + +/** + * @author alex.borodin@evosoft.com + */ +public class LandingPageAction extends Action { + + private static final Logger log = LoggerFactory.getLogger(LandingPageAction.class); + private static final String DEFAULT_LANDING_PAGE_PATH_PROPERTY = "default.landing.page.path"; + private static final String DEFAULT_LANDING_PAGE_PATH = "/group/guest/home"; + + private static String landingPage; + + static { + landingPage = PropsUtil.get(DEFAULT_LANDING_PAGE_PATH_PROPERTY); + if (landingPage == null){ + landingPage = DEFAULT_LANDING_PAGE_PATH; + } + log.info("loaded landing page path from properties: " + landingPage); + } + + @Override + public void run(HttpServletRequest request, HttpServletResponse response) throws ActionException { + if (isNullOrEmpty(request.getContextPath())) { + try { + response.sendRedirect(landingPage); + } catch (IOException e) { + throw new ActionException(e); + } + } + } +} diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/OrganizationHelper.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/OrganizationHelper.java new file mode 100644 index 000000000..5f4e1f2b9 --- /dev/null +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/OrganizationHelper.java @@ -0,0 +1,159 @@ +/* + * Copyright Bosch Software Innovations GmbH, 2016. + * Copyright Siemens AG, 2016-2018. + * Part of the SW360 Portal Project. + * + * SPDX-License-Identifier: EPL-1.0 + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.sw360.portal.users; + +import com.liferay.portal.NoSuchOrganizationException; +import com.liferay.portal.kernel.bean.PortalBeanLocatorUtil; +import com.liferay.portal.kernel.exception.PortalException; +import com.liferay.portal.kernel.exception.SystemException; +import com.liferay.portal.model.*; +import com.liferay.portal.security.membershippolicy.OrganizationMembershipPolicyUtil; +import com.liferay.portal.service.OrganizationLocalService; +import com.liferay.portal.service.OrganizationLocalServiceUtil; +import com.liferay.portal.service.UserLocalServiceUtil; +import org.apache.log4j.Logger; +import org.eclipse.sw360.datahandler.common.CommonUtils; + +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.LongStream; + +/** + * Helper class to map organization from identity provider to sw360 internal org names and create + * organizations in Liferay if necessary + * + * Adapted from https://github.com/sw360/ldapOrganizationMappingExt/blob/master/ldapAdapterEXT-ext-impl/src/main/java/com/bosch/osmi/sw360/ldapAdapter/CustomPortalLDAPImporterImpl.java + */ +public class OrganizationHelper { + private static final String MAPPING_KEYS_PREFIX = "mapping."; + private static final String MAPPING_VALUES_SUFFIX = ".target"; + private static boolean matchPrefix = false; + private static List> sortedOrganizationMappings; + private static boolean customMappingEnabled = false; + private static Logger log = Logger.getLogger(OrganizationHelper.class); + private static final String MATCH_PREFIX_KEY = "match.prefix"; + private static final String ENABLE_CUSTOM_MAPPING_KEY = "enable.custom.mapping"; + private static final String PROPERTIES_FILE_PATH = "/orgmapping.properties"; + + static { + loadOrganizationHelperSettings(); + } + + public void reassignUserToOrganizationIfNecessary(User user, Organization organization) throws PortalException, SystemException { + if (organization != null && userIsNotInOrganization(user, organization.getOrganizationId())) { + removeUserFromOtherOrganizations(user); + log.debug("OrganizationHelper adds user " + user.getEmailAddress() + " to the organization " + organization.getName()); + UserLocalServiceUtil.addOrganizationUsers(organization.getOrganizationId(), Collections.singletonList(user)); + } + } + + private boolean userIsNotInOrganization(User user, long organizationId) throws SystemException, PortalException { + return LongStream.of(user.getOrganizationIds()).noneMatch(x -> x == organizationId); + } + + private void removeUserFromOtherOrganizations(User user) throws SystemException, PortalException { + long[] usersOrganizations = user.getOrganizationIds(); + for (long usersOrganization : usersOrganizations) { + log.info("remove user " + user.getEmailAddress() + " from organization with id: " + usersOrganization); + UserLocalServiceUtil.deleteOrganizationUser(usersOrganization, user); + } + } + + public Organization addOrGetOrganization(String organizationName, long companyId) throws PortalException, SystemException { + Organization organization; + try { + organization = OrganizationLocalServiceUtil.getOrganization(companyId, organizationName); + log.info(String.format("Organization %s already exists", organizationName)); + } catch (NoSuchOrganizationException e) { + User defaultUser = UserLocalServiceUtil.loadGetDefaultUser(companyId); + organization = addOrganization(organizationName, defaultUser); + } + return organization; + } + + private Organization addOrganization(String organizationName, User user) throws SystemException { + log.info("OrganizationHelper adds the organization " + organizationName); + Organization organization = null; + try { + OrganizationLocalService organizationLocalService = (OrganizationLocalService) PortalBeanLocatorUtil.locate(OrganizationLocalService.class.getName()); + organization = organizationLocalService + .addOrganization( + user.getUserId(), + OrganizationConstants.DEFAULT_PARENT_ORGANIZATION_ID, + organizationName, + OrganizationConstants.TYPE_REGULAR_ORGANIZATION, + RegionConstants.DEFAULT_REGION_ID, + CountryConstants.DEFAULT_COUNTRY_ID, + ListTypeConstants.ORGANIZATION_STATUS_DEFAULT, + "Automatically created during LDAP import", + false, + null + ); + + OrganizationMembershipPolicyUtil.verifyPolicy(organization); + + log.info("added organization with name: " + organization.getName() + " and id:" + organization.getOrganizationId()); + } catch (PortalException e) { + log.error("A creator or parent organization with the primary key could not be found or the organization's information was invalid", e); + } + return organization; + } + + public String mapOrganizationName(String name) { + if (!customMappingEnabled) { + return name; + } + + final Predicate> matcher; + if (matchPrefix) { + matcher = e -> name.startsWith(e.getKey()); + } else { // match complete name + matcher = e -> name.equals(e.getKey()); + } + return sortedOrganizationMappings.stream() + .filter(matcher) + .findFirst() + .map(Map.Entry::getValue) + .orElse(name); + } + + private static void loadOrganizationHelperSettings() { + log.info("begin initialization"); + Properties sw360Properties = CommonUtils.loadProperties(OrganizationHelper.class, PROPERTIES_FILE_PATH); + matchPrefix = Boolean.parseBoolean(sw360Properties.getProperty(MATCH_PREFIX_KEY, "false")); + customMappingEnabled = Boolean.parseBoolean(sw360Properties.getProperty(ENABLE_CUSTOM_MAPPING_KEY, "false")); + + List mappingSourceKeys = sw360Properties + .keySet() + .stream() + .filter(p -> ((String) p).startsWith(MAPPING_KEYS_PREFIX) && !((String) p).endsWith(MAPPING_VALUES_SUFFIX)) + .collect(Collectors.toList()); + + Map tempOrgMappings = new HashMap<>(); + for (Object sourceKey : mappingSourceKeys) { + String sourceOrg = sw360Properties.getProperty((String) sourceKey); + String targetOrg = sw360Properties.getProperty(sourceKey + MAPPING_VALUES_SUFFIX); + if (sourceOrg != null && targetOrg != null && sourceOrg.length() > 0 && targetOrg.length() > 0) { + tempOrgMappings.put(sourceOrg, targetOrg); + } + } + sortedOrganizationMappings = tempOrgMappings + .entrySet() + .stream() + .sorted(Comparator.comparingInt((Map.Entry o) -> o.getKey().length()) + .reversed()) + .collect(Collectors.toList()); + log.info(String.format("initialized with %d mappings", sortedOrganizationMappings.size())); + } +} diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/PortletRequestAdapter.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/PortletRequestAdapter.java new file mode 100644 index 000000000..3c21639fe --- /dev/null +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/PortletRequestAdapter.java @@ -0,0 +1,53 @@ +/* + * Copyright Siemens AG, 2018. Part of the SW360 Portal Project. + * + * SPDX-License-Identifier: EPL-1.0 + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.sw360.portal.users; + +import com.liferay.portal.kernel.exception.PortalException; +import com.liferay.portal.kernel.exception.SystemException; +import com.liferay.portal.kernel.servlet.SessionMessages; +import com.liferay.portal.kernel.util.WebKeys; +import com.liferay.portal.service.ServiceContext; +import com.liferay.portal.service.ServiceContextFactory; +import com.liferay.portal.theme.ThemeDisplay; + +import javax.portlet.PortletRequest; +import javax.servlet.http.HttpServletRequest; +import java.util.Optional; +import java.util.function.Consumer; + +class PortletRequestAdapter implements RequestAdapter { + + private PortletRequest request; + + PortletRequestAdapter(PortletRequest request) { + this.request = request; + } + + @Override + public long getCompanyId() { + ThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY); + return themeDisplay.getCompanyId(); + } + + @Override + public Consumer getErrorMessagesConsumer() { + return msg -> SessionMessages.add(request, "request_processed", msg); + } + + @Override + public Optional getServiceContext() { + try { + return Optional.of(ServiceContextFactory.getInstance(request)); + } catch (PortalException | SystemException e ) { + return Optional.empty(); + } + } +} diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/RequestAdapter.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/RequestAdapter.java new file mode 100644 index 000000000..1f2715f10 --- /dev/null +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/RequestAdapter.java @@ -0,0 +1,22 @@ +/* + * Copyright Siemens AG, 2018. Part of the SW360 Portal Project. + * + * SPDX-License-Identifier: EPL-1.0 + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.sw360.portal.users; + +import com.liferay.portal.service.ServiceContext; + +import java.util.Optional; +import java.util.function.Consumer; + +interface RequestAdapter { + long getCompanyId(); + Consumer getErrorMessagesConsumer(); + Optional getServiceContext(); +} diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/SSOAutoLogin.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/SSOAutoLogin.java index 1b89bb08b..459b9a766 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/SSOAutoLogin.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/SSOAutoLogin.java @@ -10,15 +10,17 @@ */ package org.eclipse.sw360.portal.users; -import org.eclipse.sw360.datahandler.common.CommonUtils; - +import com.liferay.portal.NoSuchUserException; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.exception.SystemException; +import com.liferay.portal.model.Organization; +import com.liferay.portal.model.RoleConstants; import com.liferay.portal.model.User; import com.liferay.portal.security.auth.AutoLogin; import com.liferay.portal.security.auth.AutoLoginException; import com.liferay.portal.service.UserLocalServiceUtil; import com.liferay.portal.util.PortalUtil; +import org.eclipse.sw360.datahandler.common.CommonUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,6 +28,9 @@ import javax.servlet.http.HttpServletResponse; import java.util.Enumeration; import java.util.Properties; +import java.util.UUID; + +import static org.eclipse.sw360.datahandler.common.CommonUtils.isNullEmptyOrWhitespace; /** * Basic single-sign-on implementation, just parses email and external id from @@ -41,17 +46,28 @@ public class SSOAutoLogin implements AutoLogin { private Properties props; public static final String AUTH_EMAIL_KEY = "key.auth.email"; - public static String AUTH_EMAIL_VALUE = "EMAIL"; + public static String authEmailHeader = "EMAIL"; public static final String AUTH_EXTID_KEY = "key.auth.extid"; - public static String AUTH_EXTID_VALUE = "EXTID"; + public static String authExtidHeader = "EXTID"; + public static final String AUTH_GIVEN_NAME_KEY = "key.auth.givenname"; + public static String authGivenNameHeader = "GIVENNAME"; + public static final String AUTH_SURNAME_KEY = "key.auth.surname"; + public static String authSurnameHeader = "SURNAME"; + public static final String AUTH_DEPARTMENT_KEY = "key.auth.department"; + public static String authDepartmentHeader = "DEPARTMENT"; + + private static final OrganizationHelper orgHelper = new OrganizationHelper(); public SSOAutoLogin() { super(); Properties props = CommonUtils.loadProperties(SSOAutoLogin.class, PROPERTIES_FILE_PATH); - AUTH_EMAIL_VALUE = props.getProperty(AUTH_EMAIL_KEY, AUTH_EMAIL_VALUE); - AUTH_EXTID_VALUE = props.getProperty(AUTH_EXTID_KEY, AUTH_EXTID_VALUE); - log.info("Expecting the following header values for auto login email: '" - + AUTH_EMAIL_VALUE + "' and external ID: '" + AUTH_EXTID_VALUE + "'"); + authEmailHeader = props.getProperty(AUTH_EMAIL_KEY, authEmailHeader); + authExtidHeader = props.getProperty(AUTH_EXTID_KEY, authExtidHeader); + authGivenNameHeader = props.getProperty(AUTH_GIVEN_NAME_KEY, authGivenNameHeader); + authSurnameHeader = props.getProperty(AUTH_SURNAME_KEY, authSurnameHeader); + authDepartmentHeader = props.getProperty(AUTH_DEPARTMENT_KEY, authDepartmentHeader); + log.info(String.format("Expecting the following header values for auto login email: '%s', external ID: '%s', given name: '%s', surname: '%s', group: %s", + authEmailHeader, authExtidHeader, authGivenNameHeader, authSurnameHeader, authDepartmentHeader)); } @Override @@ -62,42 +78,63 @@ public String[] handleException(HttpServletRequest request, HttpServletResponse @Override public String[] login(HttpServletRequest request, HttpServletResponse response) throws AutoLoginException { - String emailId = request.getHeader(AUTH_EMAIL_VALUE); - String extid = request.getHeader(AUTH_EXTID_VALUE); + String emailId = request.getHeader(authEmailHeader); + String extid = request.getHeader(authExtidHeader); + String givenName = request.getHeader(authGivenNameHeader); + String surname = request.getHeader(authSurnameHeader); + String department = request.getHeader(authDepartmentHeader); - log.info("Attempting auto login for email: '" + emailId + "' and external id: '" + extid + "'"); + log.info(String.format("Attempting auto login for email: '%s', external ID: '%s', given name: '%s', surname: '%s', group: %s", + emailId, extid, givenName, surname, department)); - Enumeration headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String key = (String) headerNames.nextElement(); - String value = request.getHeader(key); - log.debug(key + ":" + value); - } + dumpHeadersToLog(request); - if (emailId == null || emailId.isEmpty() || extid == null || extid.isEmpty()) { + if (isNullEmptyOrWhitespace(emailId)) { log.error("Empty credentials, auto login impossible."); return new String[]{}; } long companyId = PortalUtil.getCompanyId(request); - User user = null; try { - user = UserLocalServiceUtil.getUserByEmailAddress(companyId, emailId); - } catch (SystemException | PortalException e) { - log.error("Exception during get user by email: '" + emailId + "' and company id: '" + companyId + "'", e); - } - // If user was found by liferay - if (user != null) { + String organizationName = orgHelper.mapOrganizationName(department); + Organization organization = orgHelper.addOrGetOrganization(organizationName, companyId); + log.info(String.format("Mapped orgcode %s to %s", department, organizationName)); + User user; + try { + user = UserLocalServiceUtil.getUserByEmailAddress(companyId, emailId); + } catch (NoSuchUserException e) { + log.error("Could not find user with email: '" + emailId + "'. Will create one with a random password."); + String password = UUID.randomUUID().toString(); + + user = UserPortletUtils.addLiferayUser(request, givenName, surname, emailId, + organizationName, RoleConstants.USER, false, extid, password, false, true); + if (user == null) { + throw new AutoLoginException("Couldn't create user for '" + emailId + "' and company id: '" + companyId + "'"); + } + log.info("Created user " + user); + } + + orgHelper.reassignUserToOrganizationIfNecessary(user, organization); + // Create a return credentials object return new String[]{ String.valueOf(user.getUserId()), user.getPassword(), // Encrypted Liferay password Boolean.TRUE.toString() // True: password is encrypted }; - } else { - log.error("Could not get user with email: '" + emailId + "'."); - return new String[]{}; + } catch (SystemException | PortalException e) { + log.error("Exception during login of user: '" + emailId + "' and company id: '" + companyId + "'", e); + throw new AutoLoginException(e); + } + } + + private void dumpHeadersToLog(HttpServletRequest request) { + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String key = (String) headerNames.nextElement(); + String value = request.getHeader(key); + log.debug(key + ":" + value); } } } diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/UserPortletUtils.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/UserPortletUtils.java index 849cc6524..aeeb2af8b 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/UserPortletUtils.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/users/UserPortletUtils.java @@ -1,5 +1,5 @@ /* - * Copyright Siemens AG, 2013-2016. Part of the SW360 Portal Project. + * Copyright Siemens AG, 2013-2018. Part of the SW360 Portal Project. * * SPDX-License-Identifier: EPL-1.0 * @@ -7,42 +7,58 @@ * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html - */package org.eclipse.sw360.portal.users; + */ +package org.eclipse.sw360.portal.users; import com.liferay.portal.NoSuchUserException; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.exception.SystemException; import com.liferay.portal.kernel.search.Indexer; import com.liferay.portal.kernel.search.IndexerRegistryUtil; -import com.liferay.portal.kernel.servlet.SessionMessages; -import com.liferay.portal.kernel.util.WebKeys; import com.liferay.portal.kernel.workflow.WorkflowConstants; import com.liferay.portal.model.Role; import com.liferay.portal.model.User; import com.liferay.portal.service.*; import com.liferay.portal.service.persistence.RoleUtil; -import com.liferay.portal.theme.ThemeDisplay; import org.eclipse.sw360.portal.common.ErrorMessages; import org.apache.log4j.Logger; import javax.portlet.PortletRequest; +import javax.servlet.http.HttpServletRequest; +import java.util.Optional; +import java.util.function.Consumer; + +import static com.google.common.base.Strings.isNullOrEmpty; /** * @author alex.borodin@evosoft.com */ public class UserPortletUtils { private static final Logger log = Logger.getLogger(UserPortletUtils.class); + private UserPortletUtils() { // Utility class with only static functions } - private static User addLiferayUser(PortletRequest request, String firstName, String lastName, String emailAddress, long organizationId, long roleId, boolean isMale, String externalId, String password, boolean passwordEncrypted, boolean activateImmediately) { - ThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY); - String screenName = firstName + lastName; - long companyId = themeDisplay.getCompanyId(); + public static User addLiferayUser(HttpServletRequest request, String firstName, String lastName, String emailAddress, String organizationName, String roleName, boolean male, String externalId, String password, boolean passwordEncrypted, boolean activateImmediately) throws SystemException, PortalException { + HttpServletRequestAdapter requestAdapter = new HttpServletRequestAdapter(request); + return addLiferayUser(requestAdapter, firstName, lastName, emailAddress, organizationName, roleName, male, externalId, password, passwordEncrypted, activateImmediately); + } + + public static User addLiferayUser(PortletRequest request, String firstName, String lastName, String emailAddress, String organizationName, String roleName, boolean male, String externalId, String password, boolean passwordEncrypted, boolean activateImmediately) throws SystemException, PortalException { + PortletRequestAdapter requestAdapter = new PortletRequestAdapter(request); + return addLiferayUser(requestAdapter, firstName, lastName, emailAddress, organizationName, roleName, male, externalId, password, passwordEncrypted, activateImmediately); + } + + private static User addLiferayUser(RequestAdapter requestAdapter, String firstName, String lastName, String emailAddress, String organizationName, String roleName, boolean male, String externalId, String password, boolean passwordEncrypted, boolean activateImmediately) throws SystemException, PortalException { + long companyId = requestAdapter.getCompanyId(); + + long organizationId = OrganizationLocalServiceUtil.getOrganizationId(companyId, organizationName); + final Role role = RoleLocalServiceUtil.getRole(companyId, roleName); + long roleId = role.getRoleId(); try { - if (userAlreadyExists(request, emailAddress, externalId, screenName, companyId)){ + if (userAlreadyExists(requestAdapter.getErrorMessagesConsumer(), emailAddress, externalId, externalId, companyId)){ return null; } } catch (PortalException | SystemException e) { @@ -52,29 +68,35 @@ private static User addLiferayUser(PortletRequest request, String firstName, Str } try { - ServiceContext serviceContext = ServiceContextFactory.getInstance(request); long[] roleIds = roleId == 0 ? new long[]{} : new long[]{roleId}; long[] organizationIds = organizationId == 0 ? new long[]{} : new long[]{organizationId}; long[] userGroupIds = null; - long currentUserId = themeDisplay.getUserId(); + Optional serviceContextOpt = requestAdapter.getServiceContext(); + final ServiceContext serviceContext; + if (!serviceContextOpt.isPresent()){ + return null; + } else { + serviceContext = serviceContextOpt.get(); + } + User defaultUser = UserLocalServiceUtil.loadGetDefaultUser(companyId); User user = UserLocalServiceUtil.addUser( - currentUserId/*creator*/, + defaultUser.getUserId()/*creator*/, companyId, false,/*autoPassword*/ password, password, false,/*autoScreenName*/ - screenName, + externalId, emailAddress, 0/*facebookId*/, externalId/*openId*/, - themeDisplay.getLocale(), + defaultUser.getLocale(), firstName, ""/*middleName*/, lastName, 0/*prefixId*/, 0/*suffixId*/, - isMale, + male, 4/*birthdayMonth*/, 12/*birthdayDay*/, 1959/*birthdayYear*/, @@ -92,12 +114,11 @@ private static User addLiferayUser(PortletRequest request, String firstName, Str user.setPasswordEncrypted(true); } - Role role = RoleLocalServiceUtil.getRole(roleId); RoleUtil.addUser(role.getRoleId(), user.getUserId()); UserLocalServiceUtil.updateUser(user); RoleLocalServiceUtil.updateRole(role); - UserLocalServiceUtil.updateStatus(user.getUserId(), activateImmediately ? WorkflowConstants.STATUS_APPROVED : WorkflowConstants.STATUS_INACTIVE); + UserLocalServiceUtil.updateStatus(user.getUserId(), activateImmediately ? WorkflowConstants.STATUS_APPROVED : WorkflowConstants.STATUS_INACTIVE, serviceContext); Indexer indexer = IndexerRegistryUtil.getIndexer(User.class); indexer.reindex(user); return user; @@ -107,7 +128,7 @@ private static User addLiferayUser(PortletRequest request, String firstName, Str } } - private static boolean userAlreadyExists(PortletRequest request, String emailAddress, String externalId, String screenName, long companyId) throws PortalException, SystemException { + private static boolean userAlreadyExists(Consumer errorMessageConsumer, String emailAddress, String externalId, String screenName, long companyId) throws PortalException, SystemException { boolean sameEmailExists = userByFieldExists(emailAddress, UserLocalServiceUtil::getUserByEmailAddress, companyId); boolean sameScreenNameExists = userByFieldExists(screenName, UserLocalServiceUtil::getUserByScreenName, companyId); boolean sameExternalIdExists = userByFieldExists(externalId, UserLocalServiceUtil::getUserByOpenId, companyId); @@ -123,7 +144,7 @@ private static boolean userAlreadyExists(PortletRequest request, String emailAdd errorMessage = ErrorMessages.EXTERNAL_ID_ALREADY_EXISTS; } log.info(errorMessage); - SessionMessages.add(request, "request_processed", errorMessage); + errorMessageConsumer.accept(errorMessage); } return alreadyExists; } @@ -137,17 +158,6 @@ private static boolean userByFieldExists(String searchParameter, UserSearchFunct return true; } - public static User addLiferayUser(PortletRequest request, String firstName, String lastName, String emailAddress, String organizationName, String roleName, boolean male, String externalId, String password, boolean passwordEncrypted, boolean activateImmediately) throws SystemException, PortalException { - ThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY); - long companyId = themeDisplay.getCompanyId(); - - long organizationId = OrganizationLocalServiceUtil.getOrganizationId(companyId, organizationName); - final Role role = RoleLocalServiceUtil.getRole(companyId, roleName); - long roleId = role.getRoleId(); - - return addLiferayUser(request, firstName, lastName, emailAddress, organizationId, roleId, male, externalId, password, passwordEncrypted, activateImmediately); - } - @FunctionalInterface interface UserSearchFunction { User apply(long companyId, String searchParameter) throws PortalException, SystemException; diff --git a/frontend/sw360-portlet/src/main/resources/orgmapping.properties b/frontend/sw360-portlet/src/main/resources/orgmapping.properties new file mode 100644 index 000000000..879b03f98 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/orgmapping.properties @@ -0,0 +1,28 @@ +# Copyright Siemens AG, 2016-2018. Part of the SW360 Portal Project. +# +# All rights reserved. This configuration file is provided to you under the +# terms and conditions of the Eclipse Distribution License v1.0 which +# accompanies this distribution, and is available at +# http://www.eclipse.org/org/documents/edl-v10.php +# + +# properties for organization mapping + +## enable extended mapping as configured below (as opposed to 1:1 mapping) +## defaults to false +enable.custom.mapping=false + +## if true, the org mapping keys must match the beginning of department name. +## otherwise, org mapping keys must match the complete department name +## when multiple prefixes match the user's department, the longest match wins +## defaults to false +match.prefix=false + +## mappings from LDAP department to organization name +## mappings are stored in the following form: +## +## mapping.some.ldap.department=SOME LDAP DEPT SOURCE +## mapping.some.ldap.department.target=DEPT TARGET +## +## The prefix "mapping." is mandatory. +## ".target" is appended to every key to find the mapped organization name diff --git a/frontend/sw360-portlet/src/main/resources/portal.properties b/frontend/sw360-portlet/src/main/resources/portal.properties index f794acc7a..757c68348 100644 --- a/frontend/sw360-portlet/src/main/resources/portal.properties +++ b/frontend/sw360-portlet/src/main/resources/portal.properties @@ -9,10 +9,13 @@ #auto.login.hooks=org.eclipse.sw360.portal.users.TestAutoLogin #auto.login.hooks=org.eclipse.sw360.portal.users.SSOAutoLogin + +#Liferay does not redirect to landing page when the login is taken over by SSOAutoLogin (above) +#activate LandingPageAction to remedy the problem login.events.post=org.eclipse.sw360.portal.users.LoginAction +#login.events.post=org.eclipse.sw360.portal.users.LoginAction,org.eclipse.sw360.portal.users.LandingPageAction servlet.service.events.pre=org.eclipse.sw360.portal.hooks.BuildInfoForVelocityProviderHook - - + # Specify the number of minutes before a session expires. This value is # always overridden by the value set in web.xml. session.timeout=30 diff --git a/frontend/sw360-portlet/src/main/resources/sw360.properties b/frontend/sw360-portlet/src/main/resources/sw360.properties index b6cd77eaa..ec6094dcd 100644 --- a/frontend/sw360-portlet/src/main/resources/sw360.properties +++ b/frontend/sw360-portlet/src/main/resources/sw360.properties @@ -22,9 +22,12 @@ ## values just used for UI, not forcing DB writes, should be in sync with fossology config # clearing.teams=org1,org2,org3 -## .key used for basic single sing on in org.eclipse.sw360.portal.users.SSOAutoLogin -# key.auth.email=HTTP_EMAIL -# key.auth.extid=HTTP_EXTID +## .key used for basic single sign on in org.eclipse.sw360.portal.users.SSOAutoLogin +# key.auth.email=EMAIL +# key.auth.extid=EXTID +# key.auth.givenname=GIVENNAME +# key.auth.surname=SURNAME +# key.auth.department=DEPARTMENT ## values just used for UI, not forcing DB writes ## list of license identifiers from http://spdx.org/licenses