From cb1e604dc616a6a799710f03b9b1c428261e4f90 Mon Sep 17 00:00:00 2001 From: Ignacio Diaz Teijido Date: Wed, 22 Jun 2011 11:11:11 +0200 Subject: [PATCH] LibrePlan - LDAP role matching FEA: ItEr74S09LdapAuhentication --- .../common/entities/Configuration.java | 2 +- .../entities/ConfigurationRolesLDAP.java | 58 ++++++ .../common/entities/LDAPConfiguration.java | 82 +++++++- .../src/main/resources/db.changelog-1.1.xml | 56 ++++++ .../common/entities/Configuration.hbm.xml | 19 +- .../business/users/entities/Users.hbm.xml | 2 +- .../web/common/ConfigurationController.java | 94 ++++++++- .../LDAPCustomAuthenticationProvider.java | 130 ++++++++++-- .../src/main/webapp/common/configuration.zul | 185 ++++++++++++------ 9 files changed, 546 insertions(+), 82 deletions(-) create mode 100644 navalplanner-business/src/main/java/org/navalplanner/business/common/entities/ConfigurationRolesLDAP.java diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/Configuration.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/Configuration.java index af421a3db..37737082c 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/Configuration.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/Configuration.java @@ -33,6 +33,7 @@ import org.navalplanner.business.common.BaseEntity; * * @author Manuel Rego Casasnovas * @author Cristina Alvarino Perez + * @author Ignacio Diaz Teijido */ public class Configuration extends BaseEntity { @@ -381,5 +382,4 @@ public class Configuration extends BaseEntity { public void setLdapConfiguration(LDAPConfiguration ldapConfiguration) { this.ldapConfiguration = ldapConfiguration; } - } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/ConfigurationRolesLDAP.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/ConfigurationRolesLDAP.java new file mode 100644 index 000000000..1165c41be --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/ConfigurationRolesLDAP.java @@ -0,0 +1,58 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2011 ComtecSF, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.common.entities; + +import org.hibernate.validator.NotEmpty; + +/** + * A class which is used to store the configuration of the matching between the + * LDAP roles and LibrePlan roles this will be used in LDAP configuration tab of + * the Configuration screen. + * + * This class is a component of LdapConfiguration class + * + * @author Ignacio Diaz Teijido + * @author Cristina Alvarino Perez + */ +public class ConfigurationRolesLDAP { + + private String roleLdap; + + private String roleLibreplan; + + @NotEmpty(message = "role ldap not specified") + public String getRoleLdap() { + return roleLdap; + } + + public void setRoleLdap(String roleLdap) { + this.roleLdap = roleLdap; + } + + @NotEmpty(message = "role libreplan not specified") + public String getRoleLibreplan() { + return roleLibreplan; + } + + public void setRoleLibreplan(String roleLibreplan) { + this.roleLibreplan = roleLibreplan; + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/LDAPConfiguration.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/LDAPConfiguration.java index 3b47988f4..53c5ebe0d 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/LDAPConfiguration.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/LDAPConfiguration.java @@ -19,15 +19,21 @@ package org.navalplanner.business.common.entities; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.users.entities.UserRole; /** * * This entity will be used to store the LDAP connection properties for * authentication * - * @author Ignacio Diaz - * @author Cristina Alvarino + * @author Ignacio Diaz Teijido + * @author Cristina Alvarino Perez * */ public class LDAPConfiguration extends BaseEntity { @@ -49,12 +55,24 @@ public class LDAPConfiguration extends BaseEntity { private String ldapPassword; + private String ldapGroupPath; + + private String ldapRoleProperty; + // LDAP passwords will be imported to DB or not private Boolean ldapSavePasswordsDB = true; // LDAP Authentication will be used or not private Boolean ldapAuthEnabled = false; + // LDAP roles will be used or not + private Boolean ldapSaveRolesDB = false; + + // A list which stores the matching between LDAP roles and LibrePlan roles + private List configurationRolesLdap = new ArrayList(); + + private Map> mapMatchingRoles = new HashMap>(); + public String getLdapUserId() { return ldapUserId; } @@ -119,4 +137,64 @@ public class LDAPConfiguration extends BaseEntity { this.ldapAuthEnabled = ldapAuthEnabled; } + public Boolean getLdapSaveRolesDB() { + return ldapSaveRolesDB; + } + + public void setLdapSaveRolesDB(Boolean ldapSaveRolesDB) { + this.ldapSaveRolesDB = ldapSaveRolesDB; + } + + public Boolean getLdapSavePasswordsDB() { + return ldapSavePasswordsDB; + } + + public String getLdapGroupPath() { + return ldapGroupPath; + } + + public void setLdapGroupPath(String ldapGroupPath) { + this.ldapGroupPath = ldapGroupPath; + } + + public String getLdapRoleProperty() { + return ldapRoleProperty; + } + + public void setLdapRoleProperty(String ldapRoleProperty) { + this.ldapRoleProperty = ldapRoleProperty; + } + + public List getConfigurationRolesLdap() { + return configurationRolesLdap; + } + + public void setConfigurationRolesLdap( + List configurationRolesLdap) { + this.configurationRolesLdap = configurationRolesLdap; + } + + public Map> getMapMatchingRoles() { + + for (UserRole role : UserRole.values()) { + + List listRolesLdap = new ArrayList(); + for (ConfigurationRolesLDAP roleLdap : this.configurationRolesLdap) { + + // if the role of librePlan is equals to role stored in + // configurationLdap, it is added to list + if (roleLdap != null + && role.name().equals(roleLdap.getRoleLibreplan())) { + listRolesLdap.add(roleLdap.getRoleLdap()); + } + } + mapMatchingRoles.put(role.name(), listRolesLdap); + } + + return mapMatchingRoles; + } + + public void setMapMatchingRoles(Map> mapMatchingRoles) { + this.mapMatchingRoles = mapMatchingRoles; + } } diff --git a/navalplanner-business/src/main/resources/db.changelog-1.1.xml b/navalplanner-business/src/main/resources/db.changelog-1.1.xml index 9ec92432a..224717a38 100644 --- a/navalplanner-business/src/main/resources/db.changelog-1.1.xml +++ b/navalplanner-business/src/main/resources/db.changelog-1.1.xml @@ -9,36 +9,42 @@ + Add new column to store ldap port + Add new column to store ldap base + Add new column to store ldap userdn + Add new column to store ldap password + Add new column to store ldap userid + Add new column to store ldap passwords in database @@ -46,6 +52,7 @@ + Add new column to store ldap authentication enabled @@ -60,12 +67,14 @@ + Delete constraint not null for user password + + + + + + Add new column to store ldap roles in database + + + + + + + + + Add table to store the matching between LDAP roles and LibrePlan roles + + + + + + + + + + + + + + + + + + Add new column to store ldap group path + + + + + Add new column to store ldap role property + + + + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/common/entities/Configuration.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/common/entities/Configuration.hbm.xml index 5076bf49b..ca7121bed 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/common/entities/Configuration.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/common/entities/Configuration.hbm.xml @@ -65,6 +65,7 @@ + @@ -72,9 +73,25 @@ + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/users/entities/Users.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/users/entities/Users.hbm.xml index 8ee2d6026..c567af68e 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/users/entities/Users.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/users/entities/Users.hbm.xml @@ -27,7 +27,7 @@ - + diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationController.java index 3ba93b843..2f028531a 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationController.java @@ -24,6 +24,7 @@ package org.navalplanner.web.common; import static org.navalplanner.web.I18nHelper._; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.HashMap; @@ -31,15 +32,18 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.navalplanner.business.calendars.entities.BaseCalendar; import org.navalplanner.business.common.entities.Configuration; +import org.navalplanner.business.common.entities.ConfigurationRolesLDAP; import org.navalplanner.business.common.entities.EntityNameEnum; import org.navalplanner.business.common.entities.EntitySequence; import org.navalplanner.business.common.entities.LDAPConfiguration; import org.navalplanner.business.common.entities.ProgressType; import org.navalplanner.business.common.exceptions.ValidationException; +import org.navalplanner.business.users.entities.UserRole; import org.navalplanner.web.common.components.bandboxsearch.BandboxSearch; import org.springframework.ldap.core.DistinguishedName; import org.springframework.ldap.core.LdapTemplate; @@ -78,7 +82,8 @@ import org.zkoss.zul.api.Window; * * @author Manuel Rego Casasnovas * @author Susana Montes Pedreira - * @author Cristina Alavrino Perez + * @author Cristina Alavarino Perez + * @author Ignacio Diaz Teijido */ public class ConfigurationController extends GenericForwardComposer { @@ -111,6 +116,12 @@ public class ConfigurationController extends GenericForwardComposer { private Map mapOpenedGroups = new HashMap(); + private Component ldapRoles; + + private UserRole roles; + + private Grid configurationRoles; + @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); @@ -137,6 +148,7 @@ public class ConfigurationController extends GenericForwardComposer { scenariosVisible .setTooltiptext(_("Scenarios must be enabled as more elements than master exist")); } + showLdapRoles(); } private void initializeProgressTypeList() { @@ -801,4 +813,84 @@ public class ConfigurationController extends GenericForwardComposer { public void setLdapConfiguration(LDAPConfiguration ldapConfiguration) { configurationModel.setLdapConfiguration(ldapConfiguration); } + + public void showLdapRoles() { + ldapRoles.setVisible(configurationModel.getLdapConfiguration() + .getLdapSaveRolesDB()); + } + + public RowRenderer getAllUserRolesRenderer() { + return new RowRenderer() { + @Override + public void render(Row row, Object data) throws Exception { + + final UserRole role = (UserRole) data; + row.appendChild(new Label(role.getDisplayName())); + + final Textbox tempTextbox = new Textbox(); + Textbox textbox = Util.bind(tempTextbox, new Util.Getter() { + @Override + public String get() { + List listRoles = configurationModel. + getLdapConfiguration().getMapMatchingRoles().get(role.name()); + return StringUtils.join(listRoles, ";"); + } + }, new Util.Setter() { + @Override + public void set(String value) { + Map> mapRoles = configurationModel + .getLdapConfiguration() + .getMapMatchingRoles(); + mapRoles.put(role.name(), + Arrays + .asList(StringUtils.split(value))); + // Add the list of roles to the configuration + // getting the values from map + List oldRoles = configurationModel + .getLdapConfiguration() + .getConfigurationRolesLdap(); + List newRoles = new ArrayList(); + for (String roleLdap : mapRoles.get(role.name())) { + ConfigurationRolesLDAP configurationRoleLdap = new ConfigurationRolesLDAP(); + configurationRoleLdap.setRoleLdap(roleLdap); + configurationRoleLdap.setRoleLibreplan(role + .name()); + newRoles.add(configurationRoleLdap); + } + for (ConfigurationRolesLDAP oldRole : oldRoles) { + boolean isRoleAdded = false; + if (oldRole.getRoleLibreplan().equals( + role.name())) + isRoleAdded = true; + for (ConfigurationRolesLDAP addedRole : newRoles) { + if (addedRole.getRoleLibreplan() + .equals(oldRole + .getRoleLibreplan()) + && addedRole.getRoleLdap() + .equals(oldRole + .getRoleLdap())) { + isRoleAdded = true; + } + } + if (!isRoleAdded) + newRoles.add(oldRole); + } + configurationModel.getLdapConfiguration() + .setConfigurationRolesLdap(newRoles); + + } + }); + textbox.setWidth("300px"); + row.appendChild(textbox); + } + }; + } + + public UserRole[] getRoles() { + return roles.values(); + } + + public void setRoles(UserRole roles) { + this.roles = roles; + } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/users/services/LDAPCustomAuthenticationProvider.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/users/services/LDAPCustomAuthenticationProvider.java index db2df9e35..a6020a0b9 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/users/services/LDAPCustomAuthenticationProvider.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/users/services/LDAPCustomAuthenticationProvider.java @@ -18,15 +18,27 @@ */ package org.navalplanner.web.users.services; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; + import org.navalplanner.business.common.IAdHocTransactionService; import org.navalplanner.business.common.IOnTransaction; import org.navalplanner.business.common.daos.IConfigurationDAO; +import org.navalplanner.business.common.entities.ConfigurationRolesLDAP; import org.navalplanner.business.common.entities.LDAPConfiguration; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.users.daos.IUserDAO; import org.navalplanner.business.users.entities.User; import org.navalplanner.business.users.entities.UserRole; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DistinguishedName; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.filter.EqualsFilter; @@ -180,40 +192,66 @@ public class LDAPCustomAuthenticationProvider extends userNavalplan.setPassword(encodedPassword); userNavalplan.setNavalplanUser(false); userNavalplan.setDisabled(false); - userNavalplan.addRole(UserRole.ROLE_ADMINISTRATION); + List roles = getMatchedRoles( + configuration, ldapTemplate, username); + for (String role : roles) { + userNavalplan.addRole(UserRole.valueOf( + UserRole.class, role)); + } transactionService - .runOnTransaction(new IOnTransaction() { + .runOnTransaction(new IOnTransaction() { @Override - public Object execute() { + public Void execute() { userDAO.save(userNavalplan); - return true; + return null; } }); } else { // user exists in NavalPlan + // We must match the LDAPRoles with + // LibrePlanRoles + // The user must have the roles from LDAP + if (configuration.getLdapSaveRolesDB()) { + List roles = getMatchedRoles( + configuration, ldapTemplate, username); + for (String role : roles) { + if (!user.getRoles().contains( + UserRole.valueOf(UserRole.class, + role))) { + user.addRole(UserRole.valueOf( + UserRole.class, role)); + } + } + Set oldRoles = new HashSet(); + oldRoles.addAll(user.getRoles()); + for (UserRole role : oldRoles) { + if (!roles.contains(role.name())) { + user.removeRole(role); + } + } + } if (configuration.isLdapSavePasswordsDB()) { // We must test if user had password in - // database, - // because the configuration + // database, because the configuration // of importing passwords could be changed after - // the - // import of the user - // so the password could be null in database. + // the import of the user so the password could + // be null in database. + if (null == user.getPassword() || !(user.getPassword() .equals(encodedPassword))) { user.setPassword(encodedPassword); - final User userNavalplan = user; - transactionService - .runOnTransaction(new IOnTransaction() { - @Override - public Object execute() { - userDAO.save(userNavalplan); - return true; - } - }); } } + final User userNavalplan = user; + transactionService + .runOnTransaction(new IOnTransaction() { + @Override + public Void execute() { + userDAO.save(userNavalplan); + return null; + } + }); } // Gets and returns user from DB once authenticated // against @@ -259,6 +297,62 @@ public class LDAPCustomAuthenticationProvider extends .getPassword())); } + private List getMatchedRoles(LDAPConfiguration configuration, + LdapTemplate ldapTemplate, String username) { + + final LDAPConfiguration ldapConfig = configuration; + String groupsPath = configuration.getLdapGroupPath(); + String roleProperty = configuration.getLdapRoleProperty(); + List rolesLdap = configuration + .getConfigurationRolesLdap(); + + List rolesReturn = new ArrayList(); + + if (null == groupsPath || groupsPath.isEmpty()) { + // The LDAP has a node strategy for groups, + // we must check the roleProperty in user node. + for (ConfigurationRolesLDAP roleLDAP : rolesLdap) { + // We must make a search for each role-matching in nodes + List resultsSearch = ldapTemplate.search( + DistinguishedName.EMPTY_PATH, + new EqualsFilter( + roleProperty, roleLDAP.getRoleLdap()) + .toString(), new AttributesMapper() { + + @Override + public Object mapFromAttributes( + Attributes attributes) + throws NamingException { + return attributes.get(ldapConfig + .getLdapUserId()); + } + }); + for (Object resultsIter : resultsSearch) { + Attribute atrib = (Attribute) resultsIter; + if (atrib.contains(username)) { + rolesReturn.add(roleLDAP.getRoleLibreplan()); + } + } + } + } else { + // The LDAP has a branch strategy for groups + // we must check if the user is in one of the groups. + + for (ConfigurationRolesLDAP roleLdap : rolesLdap) { + // We must make a search for each role matching + DirContextAdapter adapter = (DirContextAdapter) ldapTemplate + .lookup(roleLdap.getRoleLdap() + "," + groupsPath); + if (adapter.attributeExists(roleProperty)) { + Attributes atrs = adapter.getAttributes(); + if (atrs.get(roleProperty).contains(username)) { + rolesReturn.add(roleLdap.getRoleLibreplan()); + } + } + } + } + return rolesReturn; + } + public DBPasswordEncoderService getPasswordEncoderService() { return passwordEncoderService; } diff --git a/navalplanner-webapp/src/main/webapp/common/configuration.zul b/navalplanner-webapp/src/main/webapp/common/configuration.zul index 04ec5ec68..3087de166 100644 --- a/navalplanner-webapp/src/main/webapp/common/configuration.zul +++ b/navalplanner-webapp/src/main/webapp/common/configuration.zul @@ -250,64 +250,133 @@ - - - - - - - - - - - - - - - - - - - - - - - - - -