Java tutorial
/* * Copyright (C) 2007-2018 Crafter Software Corporation. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package org.craftercms.studio.impl.v1.service.security; import org.apache.commons.lang3.StringUtils; import org.craftercms.studio.api.v1.constant.DmConstants; import org.craftercms.studio.api.v1.constant.StudioConstants; import org.craftercms.studio.api.v1.dal.Group; import org.craftercms.studio.api.v1.dal.SiteFeed; import org.craftercms.studio.api.v1.dal.User; import org.craftercms.studio.api.v1.exception.SiteNotFoundException; import org.craftercms.studio.api.v1.exception.security.AuthenticationSystemException; import org.craftercms.studio.api.v1.exception.security.BadCredentialsException; import org.craftercms.studio.api.v1.exception.security.GroupAlreadyExistsException; import org.craftercms.studio.api.v1.exception.security.GroupNotFoundException; import org.craftercms.studio.api.v1.exception.security.UserAlreadyExistsException; import org.craftercms.studio.api.v1.exception.security.UserNotFoundException; import org.craftercms.studio.api.v1.log.Logger; import org.craftercms.studio.api.v1.log.LoggerFactory; import org.craftercms.studio.api.v1.service.activity.ActivityService; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.ldap.AuthenticationException; import org.springframework.ldap.CommunicationException; import org.springframework.ldap.core.AuthenticatedLdapEntryContextMapper; import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.LdapEntryIdentification; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.query.LdapQuery; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.craftercms.studio.api.v1.util.StudioConfiguration.*; import static org.springframework.ldap.query.LdapQueryBuilder.query; public class DbWithLdapExtensionSecurityProvider extends DbSecurityProvider { private final static Logger logger = LoggerFactory.getLogger(DbWithLdapExtensionSecurityProvider.class); @Override public String authenticate(String username, String password) throws BadCredentialsException, AuthenticationSystemException { // Mapper for user data if user is successfully authenticated AuthenticatedLdapEntryContextMapper<User> mapper = new AuthenticatedLdapEntryContextMapper<User>() { @Override public User mapWithContext(DirContext dirContext, LdapEntryIdentification ldapEntryIdentification) { try { // User entry - extract attributes DirContextOperations dirContextOperations = (DirContextOperations) dirContext .lookup(ldapEntryIdentification.getRelativeName()); Attributes attributes = dirContextOperations.getAttributes(); String emailAttribName = studioConfiguration.getProperty(SECURITY_LDAP_USER_ATTRIBUTE_EMAIL); String firstNameAttribName = studioConfiguration .getProperty(SECURITY_LDAP_USER_ATTRIBUTE_FIRST_NAME); String lastNameAttribName = studioConfiguration .getProperty(SECURITY_LDAP_USER_ATTRIBUTE_LAST_NAME); String siteIdAttribName = studioConfiguration.getProperty(SECURITY_LDAP_USER_ATTRIBUTE_SITE_ID); String groupNameAttribName = studioConfiguration .getProperty(SECURITY_LDAP_USER_ATTRIBUTE_GROUP_NAME); Attribute emailAttrib = attributes.get(emailAttribName); Attribute firstNameAttrib = attributes.get(firstNameAttribName); Attribute lastNameAttrib = attributes.get(lastNameAttribName); Attribute siteIdAttrib = attributes.get(siteIdAttribName); Attribute groupNameAttrib = attributes.get(groupNameAttribName); User user = new User(); user.setGroups(new ArrayList<>()); user.setActive(1); user.setUsername(username); if (emailAttrib != null && emailAttrib.get() != null) { user.setEmail(emailAttrib.get().toString()); } else { logger.error("No LDAP attribute " + emailAttribName + " found for username " + username + ". User will not be imported into DB."); return null; } if (firstNameAttrib != null && firstNameAttrib.get() != null) { user.setFirstname(firstNameAttrib.get().toString()); } else { logger.warn("No LDAP attribute " + firstNameAttribName + " found for username " + username); } if (lastNameAttrib != null && lastNameAttrib.get() != null) { user.setLastname(lastNameAttrib.get().toString()); } else { logger.warn("No LDAP attribute " + lastNameAttribName + " found for username " + username); } if (siteIdAttrib != null && siteIdAttrib.get() != null) { Map<String, Object> params = new HashMap<>(); NamingEnumeration siteIdValues = siteIdAttrib.getAll(); while (siteIdValues.hasMore()) { Object siteIdObj = siteIdValues.next(); if (siteIdObj != null) { String[] siteIdAndGroupName = extractSiteIdAndGroupNameFromAttributeValue( siteIdObj.toString()); if (siteIdAndGroupName.length > 0) { params.put("siteId", siteIdAndGroupName[0]); SiteFeed siteFeed = siteFeedMapper.getSite(params); if (siteFeed != null) { // Add groups, first the one that's specific to the site if (siteIdAndGroupName.length > 1) { addGroupToUser(user, siteIdAndGroupName[1], siteFeed); } extractGroupsFromAttribute(user, groupNameAttribName, groupNameAttrib, siteFeed); } else { logger.warn("Not site found for ID " + siteIdAndGroupName[0]); } } } } } else { String defaultSiteId = studioConfiguration.getProperty(SECURITY_LDAP_DEFAULT_SITE_ID); logger.debug("Assigning user " + username + " to default site " + defaultSiteId); Map<String, Object> params = new HashMap<>(); params.put("siteId", defaultSiteId); SiteFeed siteFeed = siteFeedMapper.getSite(params); if (siteFeed != null) { extractGroupsFromAttribute(user, groupNameAttribName, groupNameAttrib, siteFeed); } else { logger.warn("No site found for default site ID " + defaultSiteId); } } return user; } catch (NamingException e) { logger.error("Error getting details from LDAP for username " + username, e); return null; } } }; // Create ldap query to authenticate user LdapQuery ldapQuery = query().where(studioConfiguration.getProperty(SECURITY_LDAP_USER_ATTRIBUTE_USERNAME)) .is(username); User user; try { user = ldapTemplate.authenticate(ldapQuery, password, mapper); } catch (EmptyResultDataAccessException e) { logger.info("User " + username + " not found with external security provider. Trying to authenticate against studio database"); // When user not found try to authenticate against studio database return super.authenticate(username, password); } catch (CommunicationException e) { logger.info("Failed to connect with external security provider. " + "Trying to authenticate against studio database"); // When user not found try to authenticate against studio database return super.authenticate(username, password); } catch (AuthenticationException e) { logger.error("Authentication failed with the LDAP system", e); throw new BadCredentialsException(); } catch (Exception e) { logger.error("Authentication failed with the LDAP system", e); throw new AuthenticationSystemException("Authentication failed with the LDAP system", e); } if (user != null) { // When user authenticated against LDAP, upsert user data into studio database if (super.userExists(username)) { try { boolean success = updateUserInternal(user.getUsername(), user.getFirstname(), user.getLastname(), user.getEmail()); if (success) { ActivityService.ActivityType activityType = ActivityService.ActivityType.UPDATED; Map<String, String> extraInfo = new HashMap<>(); extraInfo.put(DmConstants.KEY_CONTENT_TYPE, StudioConstants.CONTENT_TYPE_USER); activityService.postActivity(getSystemSite(), user.getUsername(), user.getUsername(), activityType, ActivityService.ActivitySource.API, extraInfo); } } catch (UserNotFoundException e) { logger.error( "Error updating user " + username + " with data from external authentication provider", e); throw new AuthenticationSystemException( "Error updating user " + username + " with data from external authentication provider", e); } } else { try { boolean success = createUser(user.getUsername(), password, user.getFirstname(), user.getLastname(), user.getEmail(), true); if (success) { ActivityService.ActivityType activityType = ActivityService.ActivityType.CREATED; Map<String, String> extraInfo = new HashMap<>(); extraInfo.put(DmConstants.KEY_CONTENT_TYPE, StudioConstants.CONTENT_TYPE_USER); activityService.postActivity(getSystemSite(), user.getUsername(), user.getUsername(), activityType, ActivityService.ActivitySource.API, extraInfo); } } catch (UserAlreadyExistsException e) { logger.error("Error adding user " + username + " from external authentication provider", e); throw new AuthenticationSystemException( "Error adding user " + username + " from external authentication provider", e); } } for (Group group : user.getGroups()) { try { upsertUserGroup(group.getSite(), group.getName(), user.getUsername()); } catch (GroupAlreadyExistsException | SiteNotFoundException | UserNotFoundException | UserAlreadyExistsException | GroupNotFoundException e) { logger.error("Failed to upsert user groups data from LDAP", e); } } String token = createToken(user); storeSessionTicket(token); storeSessionUsername(username); return token; } else { logger.error("Failed to retrieve LDAP user details"); throw new AuthenticationSystemException("Failed to retrieve LDAP user details"); } } private String extractGroupNameFromAttributeValue(String groupAttributeValue) { Pattern pattern = Pattern .compile(studioConfiguration.getProperty(SECURITY_LDAP_USER_ATTRIBUTE_GROUP_NAME_REGEX)); Matcher matcher = pattern.matcher(groupAttributeValue); if (matcher.matches()) { int index = Integer .parseInt(studioConfiguration.getProperty(SECURITY_LDAP_USER_ATTRIBUTE_GROUP_NAME_MATCH_INDEX)); return matcher.group(index); } return StringUtils.EMPTY; } private String[] extractSiteIdAndGroupNameFromAttributeValue(String siteIdAttributeValue) { Pattern pattern = Pattern .compile(studioConfiguration.getProperty(SECURITY_LDAP_USER_ATTRIBUTE_SITE_ID_REGEX)); Matcher matcher = pattern.matcher(siteIdAttributeValue); if (matcher.matches()) { int siteIdIndex = Integer .parseInt(studioConfiguration.getProperty(SECURITY_LDAP_USER_ATTRIBUTE_SITE_ID_MATCH_INDEX)); int groupNameIndex = Integer.parseInt( studioConfiguration.getProperty(SECURITY_LDAP_USER_ATTRIBUTE_SITE_ID_GROUP_NAME_MATCH_INDEX)); String siteName = matcher.group(siteIdIndex); String groupName = null; if (groupNameIndex <= matcher.groupCount()) { groupName = matcher.group(groupNameIndex); } if (groupName != null) { return new String[] { siteName, groupName }; } else { return new String[] { siteName }; } } return new String[0]; } private void extractGroupsFromAttribute(User user, String groupNameAttribName, Attribute groupNameAttrib, SiteFeed siteFeed) throws NamingException { if (groupNameAttrib != null && groupNameAttrib.size() > 0) { NamingEnumeration groupAttribValues = groupNameAttrib.getAll(); while (groupAttribValues.hasMore()) { Object groupNameObj = groupAttribValues.next(); if (groupNameObj != null) { String groupName = extractGroupNameFromAttributeValue(groupNameObj.toString()); if (StringUtils.isNotEmpty(groupName)) { addGroupToUser(user, groupName, siteFeed); } } } } else { logger.debug("No LDAP attribute " + groupNameAttribName + " found for username " + user.getUsername()); } } private void addGroupToUser(User user, String groupName, SiteFeed siteFeed) { Group group = new Group(); group.setName(groupName); group.setExternallyManaged(1); group.setDescription("Externally managed group"); group.setSiteId(siteFeed.getId()); group.setSite(siteFeed.getSiteId()); user.getGroups().add(group); } protected boolean updateUserInternal(String username, String firstName, String lastName, String email) throws UserNotFoundException { if (!userExists(username)) { throw new UserNotFoundException(); } else { Map<String, Object> params = new HashMap<>(); params.put("username", username); params.put("firstname", firstName); params.put("lastname", lastName); params.put("email", email); params.put("externallyManaged", 1); securityMapper.updateUser(params); return true; } } protected boolean upsertUserGroup(String siteId, String groupName, String username) throws GroupAlreadyExistsException, SiteNotFoundException, UserNotFoundException, UserAlreadyExistsException, GroupNotFoundException { if (!groupExists(siteId, groupName)) { createGroup(groupName, "Externally managed group", siteId, true); } if (!userExistsInGroup(siteId, groupName, username)) { boolean success = addUserToGroup(siteId, groupName, username); if (success) { ActivityService.ActivityType activityType = ActivityService.ActivityType.ADD_USER_TO_GROUP; Map<String, String> extraInfo = new HashMap<>(); extraInfo.put(DmConstants.KEY_CONTENT_TYPE, StudioConstants.CONTENT_TYPE_USER); activityService.postActivity(siteId, "LDAP", username + " > " + groupName, activityType, ActivityService.ActivitySource.API, extraInfo); } } return true; } public String getSystemSite() { return studioConfiguration.getProperty(CONFIGURATION_GLOBAL_SYSTEM_SITE); } public LdapTemplate getLdapTemplate() { return ldapTemplate; } public void setLdapTemplate(LdapTemplate ldapTemplate) { this.ldapTemplate = ldapTemplate; } public ActivityService getActivityService() { return activityService; } public void setActivityService(ActivityService activityService) { this.activityService = activityService; } protected LdapTemplate ldapTemplate; protected ActivityService activityService; }