diff --git a/navalplanner-webapp/pom.xml b/navalplanner-webapp/pom.xml index 32070b8dd..6660f8828 100644 --- a/navalplanner-webapp/pom.xml +++ b/navalplanner-webapp/pom.xml @@ -212,6 +212,32 @@ org.springframework.security spring-security-core-tiger + + + org.springframework.ldap + spring-ldap-core + 1.3.1.RELEASE + + + org.springframework.ldap + spring-ldap-core-tiger + 1.3.1.RELEASE + + + org.springframework.ldap + spring-ldap-odm + 1.3.1.RELEASE + + + org.springframework.ldap + spring-ldap-ldif-core + 1.3.1.RELEASE + + + org.springframework.ldap + spring-ldap-ldif-batch + 1.3.1.RELEASE + org.aspectj 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 new file mode 100644 index 000000000..2d79ab930 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/users/services/LDAPCustomAuthenticationProvider.java @@ -0,0 +1,108 @@ +/* + * 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.web.users.services; + +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.filter.EqualsFilter; +import org.springframework.security.AuthenticationException; +import org.springframework.security.BadCredentialsException; +import org.springframework.security.providers.UsernamePasswordAuthenticationToken; +import org.springframework.security.providers.dao.AbstractUserDetailsAuthenticationProvider; +import org.springframework.security.userdetails.UserDetails; +import org.springframework.security.userdetails.UserDetailsService; + +/** + * An extending from AbstractUserDetailsAuthenticationProvider class which is + * used to implement the authentication against LDAP. + * + * In the future this provider will implement all the process explained in + * + * + * At this time it authenticates user against LDAP and then searches it in BD to + * use the BD user in application. + * + * @author Ignacio Diaz + * @author Cristina Alvarino + * + */ +public class LDAPCustomAuthenticationProvider extends + AbstractUserDetailsAuthenticationProvider { + + // Template to search in LDAP + private LdapTemplate ldapTemplate; + + // Place in LDAP where username is + private String userId; + + private UserDetailsService userDetailsService; + + @Override + protected void additionalAuthenticationChecks(UserDetails arg0, + UsernamePasswordAuthenticationToken arg1) + throws AuthenticationException { + // No needed at this time + } + + @Override + public UserDetails retrieveUser(String username, + UsernamePasswordAuthenticationToken authentication) + throws AuthenticationException { + + // Tests if the user is in LDAP, if not, throws a new + // AuthenticationException + if (!(ldapTemplate.authenticate(DistinguishedName.EMPTY_PATH, + new EqualsFilter(userId, username).toString(), authentication + .getCredentials().toString()))) { + + throw new BadCredentialsException("User is not in LDAP."); + + } else { + // Gets and returns user from DB once authenticated against LDAP + return getUserDetailsService().loadUserByUsername(username); + } + } + + // Getters and setters + public LdapTemplate getLdapTemplate() { + return ldapTemplate; + } + + public void setLdapTemplate(LdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public void setUserDetailsService(UserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + public UserDetailsService getUserDetailsService() { + return userDetailsService; + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/users/services/LDAPUserDetailsService.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/users/services/LDAPUserDetailsService.java new file mode 100644 index 000000000..d3068c7db --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/users/services/LDAPUserDetailsService.java @@ -0,0 +1,95 @@ +/* + * 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.web.users.services; + +import static org.navalplanner.web.I18nHelper._; + +import java.util.Set; + +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.scenarios.bootstrap.PredefinedScenarios; +import org.navalplanner.business.scenarios.entities.Scenario; +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.dao.DataAccessException; +import org.springframework.security.GrantedAuthority; +import org.springframework.security.GrantedAuthorityImpl; +import org.springframework.security.userdetails.UserDetails; +import org.springframework.security.userdetails.UserDetailsService; +import org.springframework.security.userdetails.UsernameNotFoundException; +import org.springframework.transaction.annotation.Transactional; + +/** + * A Class which is used to implement the UserDetailsService interfaces + * + * At this time it takes values from authenticated user (LDAP or DB) and gets + * from DB the user properties. + * + * @author Ignacio Diaz + * @author Cristina Alvarino + * + */ +public class LDAPUserDetailsService implements UserDetailsService { + + @Autowired + private IUserDAO userDAO; + + @Override + @Transactional(readOnly = true) + public UserDetails loadUserByUsername(String loginName) + throws UsernameNotFoundException, DataAccessException { + + User user; + + try { + user = userDAO.findByLoginName(loginName); + } catch (InstanceNotFoundException e) { + throw new UsernameNotFoundException(_("User with login name " + + "'{0}': not found", loginName)); + } + + Scenario scenario = user.getLastConnectedScenario(); + if (scenario == null) { + scenario = PredefinedScenarios.MASTER.getScenario(); + } + + return new CustomUser(user.getLoginName(), user.getPassword(), + !user.isDisabled(), true, // accountNonExpired + true, // credentialsNonExpired + true, // accountNonLocked + getGrantedAuthorities(user.getAllRoles()), scenario); + } + + private GrantedAuthority[] getGrantedAuthorities(Set roles) { + + GrantedAuthority[] grantedAuthorities = new GrantedAuthority[roles + .size()]; + int i = 0; + + for (UserRole r : roles) { + grantedAuthorities[i++] = new GrantedAuthorityImpl(r.name()); + } + + return grantedAuthorities; + + } + +} diff --git a/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-security-config.xml b/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-security-config.xml index 5004d9e8f..5f3ec2d7f 100644 --- a/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-security-config.xml +++ b/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-security-config.xml @@ -1,76 +1,107 @@ - + - + - - - + + + - - - - - - - - - + + + + + + + + + - + - - + + - - + + - + - - - - - - - - + - + --> + + + - - + + - + + + + - + + diff --git a/navalplanner-webapp/src/test/java/org/navalplanner/web/test/users/services/LDAPUserDetailsServiceTest.java b/navalplanner-webapp/src/test/java/org/navalplanner/web/test/users/services/LDAPUserDetailsServiceTest.java new file mode 100644 index 000000000..5a69a15c2 --- /dev/null +++ b/navalplanner-webapp/src/test/java/org/navalplanner/web/test/users/services/LDAPUserDetailsServiceTest.java @@ -0,0 +1,125 @@ +/* + * 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.web.test.users.services; + +import static org.junit.Assert.assertEquals; +import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; +import static org.navalplanner.web.WebappGlobalNames.WEBAPP_SPRING_CONFIG_FILE; +import static org.navalplanner.web.WebappGlobalNames.WEBAPP_SPRING_SECURITY_CONFIG_FILE; +import static org.navalplanner.web.test.WebappGlobalNames.WEBAPP_SPRING_CONFIG_TEST_FILE; +import static org.navalplanner.web.test.WebappGlobalNames.WEBAPP_SPRING_SECURITY_CONFIG_TEST_FILE; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.navalplanner.business.common.IAdHocTransactionService; +import org.navalplanner.business.common.IOnTransaction; +import org.navalplanner.business.scenarios.bootstrap.IScenariosBootstrap; +import org.navalplanner.business.users.entities.UserRole; +import org.navalplanner.web.users.bootstrap.IUsersBootstrapInDB; +import org.navalplanner.web.users.bootstrap.MandatoryUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.GrantedAuthority; +import org.springframework.security.userdetails.UserDetails; +import org.springframework.security.userdetails.UserDetailsService; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * + * Tests for LDAPUserDetailsService. + * + * @author Ignacio Diaz + * @author Cristina Alvarino + * + */ + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { BUSINESS_SPRING_CONFIG_FILE, + WEBAPP_SPRING_CONFIG_FILE, WEBAPP_SPRING_CONFIG_TEST_FILE, + WEBAPP_SPRING_SECURITY_CONFIG_FILE, + WEBAPP_SPRING_SECURITY_CONFIG_TEST_FILE }) +@Transactional +public class LDAPUserDetailsServiceTest { + + @Autowired + private UserDetailsService userDetailsService; + + @Autowired + private IUsersBootstrapInDB usersBootstrap; + + @Autowired + private IScenariosBootstrap scenariosBootstrap; + + @Autowired + private IAdHocTransactionService transactionService; + + @Before + public void loadScenariosBootsrap() { + /* + * the required data is loaded in another transaction because if it's + * loaded on the same transaction the added scenario could not be + * retrieved from PredefinedScenario. This happened when executing all + * tests. If you execute this test in isolation this problem doesn't + * happen + */ + transactionService.runOnAnotherTransaction(new IOnTransaction() { + @Override + public Void execute() { + scenariosBootstrap.loadRequiredData(); + return null; + } + }); + } + + @Test + public void testLoadUserByUsername() { + usersBootstrap.loadRequiredData(); + + for (MandatoryUser u : MandatoryUser.values()) { + + UserDetails userDetails = userDetailsService.loadUserByUsername(u + .getLoginName()); + + assertEquals(u.getLoginName(), userDetails.getUsername()); + + assertEquals(u.getInitialRoles(), getUserRoles(userDetails)); + + } + + } + + private Set getUserRoles(UserDetails userDetails) { + + Set userRoles = new HashSet(); + + for (GrantedAuthority a : userDetails.getAuthorities()) { + userRoles.add(UserRole.valueOf(a.getAuthority())); + } + + return userRoles; + + } + +} diff --git a/navalplanner-webapp/src/test/resources/navalplanner-webapp-spring-security-config-test.xml b/navalplanner-webapp/src/test/resources/navalplanner-webapp-spring-security-config-test.xml index 3566ec820..6dc36d664 100644 --- a/navalplanner-webapp/src/test/resources/navalplanner-webapp-spring-security-config-test.xml +++ b/navalplanner-webapp/src/test/resources/navalplanner-webapp-spring-security-config-test.xml @@ -1,23 +1,52 @@ - - + + - + + + - + - + + + - + + - \ No newline at end of file + + + + + + +