ItEr38S17ArquitecturaServidorItEr37S07: General refactoring to Spring Security integration and passwords encoded with SHA-2 (SHA-512).
General refactoring to Spring Security integration and passwords econded with SHA-2 (SHA-512). "naval_user" and "user_roles" tables must be removed after applying this patch.
This commit is contained in:
parent
1345f260fd
commit
03d9834920
11 changed files with 264 additions and 43 deletions
|
|
@ -23,8 +23,8 @@ package org.navalplanner.web.users.bootstrap;
|
|||
import org.navalplanner.business.IDataBootstrap;
|
||||
|
||||
/**
|
||||
* It creates necessary users initially.
|
||||
* It registers mandatory users in the database (if not registered yet).
|
||||
*
|
||||
* @author Fernando Bellas Permuy <fbellas@udc.es>
|
||||
*/
|
||||
public interface IUsersBootstrap extends IDataBootstrap {}
|
||||
public interface IUsersBootstrapInDB extends IDataBootstrap {}
|
||||
|
|
@ -22,20 +22,28 @@ package org.navalplanner.web.users.bootstrap;
|
|||
|
||||
import org.navalplanner.business.users.daos.IUserDAO;
|
||||
import org.navalplanner.business.users.entities.User;
|
||||
import org.navalplanner.web.users.services.IDBPasswordEncoderService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* @author Fernando Bellas Permuy <fbellas@udc.es>
|
||||
*/
|
||||
@Service
|
||||
@Transactional
|
||||
public class UsersBootstrap implements IUsersBootstrap {
|
||||
public class UsersBootstrapInDB implements IUsersBootstrapInDB {
|
||||
|
||||
@Autowired
|
||||
private IUserDAO userDAO;
|
||||
|
||||
private IDBPasswordEncoderService dbPasswordEncoderService;
|
||||
|
||||
public void setDbPasswordEncoderService(
|
||||
IDBPasswordEncoderService dbPasswordEncoderService) {
|
||||
|
||||
this.dbPasswordEncoderService = dbPasswordEncoderService;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadRequiredData() {
|
||||
|
||||
|
|
@ -49,11 +57,18 @@ public class UsersBootstrap implements IUsersBootstrap {
|
|||
|
||||
if (!userDAO.existsByLoginName(u.getLoginName())) {
|
||||
|
||||
userDAO.save(User.create(u.getLoginName(), u.getClearPassword(),
|
||||
userDAO.save(User.create(u.getLoginName(), getEncodedPassword(u),
|
||||
u.getInitialRoles()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String getEncodedPassword(MandatoryUser u) {
|
||||
|
||||
return dbPasswordEncoderService.encodePassword(u.getClearPassword(),
|
||||
u.getLoginName());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* This file is part of ###PROJECT_NAME###
|
||||
*
|
||||
* Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
|
||||
* Desenvolvemento Tecnolóxico de Galicia
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.navalplanner.web.users.services;
|
||||
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.providers.dao.SaltSource;
|
||||
import org.springframework.security.providers.encoding.PasswordEncoder;
|
||||
import org.springframework.security.userdetails.User;
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
|
||||
/**
|
||||
* For maximum flexibility, the implementation uses the password encoder and
|
||||
* the salt source configured in the Spring Security configuration file (in
|
||||
* consequence, it is possible to change the configuration to use any password
|
||||
* encoder and/or salt source without modifying the implementation of this
|
||||
* service). The only restriction the implementation imposes is that when using
|
||||
* a reflection-based salt source, the "username" property must be specified.
|
||||
*
|
||||
* @author Fernando Bellas Permuy <fbellas@udc.es>
|
||||
*/
|
||||
public class DBPasswordEncoderService implements IDBPasswordEncoderService {
|
||||
|
||||
private SaltSource saltSource;
|
||||
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
public void setSaltSource(SaltSource saltSource) {
|
||||
this.saltSource = saltSource;
|
||||
}
|
||||
|
||||
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* The second parameter, <code>loginName</code>, is used as a salt if the
|
||||
* configured salt source is <code>ReflectionSaltSource</code> (which must
|
||||
* be configured to use "username" property as a salt).
|
||||
*/
|
||||
public String encodePassword(String clearPassword, String loginName) {
|
||||
|
||||
/*
|
||||
* The only important parameter in User's constructor is "loginName",
|
||||
* which corresponds to the "username" property if the "saltSource" is
|
||||
* "ReflectionSaltSource". Note that "SystemWideSaltSource" ignores
|
||||
* the "user" passed as a parameter to "saltSource.getSalt".
|
||||
*/
|
||||
UserDetails userDetails = new User(loginName, clearPassword, true,
|
||||
true, true, true, new GrantedAuthority[0]);
|
||||
|
||||
Object salt = null;
|
||||
|
||||
if (saltSource != null) {
|
||||
salt = saltSource.getSalt(userDetails);
|
||||
}
|
||||
|
||||
return passwordEncoder.encodePassword(clearPassword, salt);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -35,7 +35,6 @@ 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.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
|
|
@ -45,8 +44,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
*
|
||||
* @author Fernando Bellas Permuy <fbellas@udc.es>
|
||||
*/
|
||||
@Service("defaultUserDetailsService")
|
||||
public class DefaultUserDetailsService implements UserDetailsService {
|
||||
public class DBUserDetailsService implements UserDetailsService {
|
||||
|
||||
@Autowired
|
||||
private IUserDAO userDAO;
|
||||
|
|
@ -23,14 +23,7 @@ package org.navalplanner.web.users.services;
|
|||
/**
|
||||
* Service for encoding passwords when information about users
|
||||
* is stored in the database. In particular, it must be used to encode a
|
||||
* password when creating a user and to change a user's password. For
|
||||
* maximum flexibility, the implementation of the service uses the password
|
||||
* encoder and the salt source configured in the Spring Security configuration
|
||||
* file (in consequence, it is possible to change the configuration to use
|
||||
* any password encoder and/or salt source without modifying the
|
||||
* implementation of this service). The only restriction the implementation
|
||||
* imposes is that when using a reflection-based salt source, the "username"
|
||||
* property must be specified.
|
||||
* password when creating a user and to change a user's password.
|
||||
* <b/>
|
||||
* When information about users is maintained externally (e.g. in a LDAP
|
||||
* server), this service is not used, since the Web application is not
|
||||
|
|
@ -38,6 +31,12 @@ package org.navalplanner.web.users.services;
|
|||
*
|
||||
* @author Fernando Bellas Permuy <fbellas@udc.es>
|
||||
*/
|
||||
public interface IPasswordEncoderService {
|
||||
public interface IDBPasswordEncoderService {
|
||||
|
||||
/**
|
||||
* Encodes a clear password. The second parameter (which must be the
|
||||
* login name) may be used as a salt.
|
||||
*/
|
||||
public String encodePassword(String clearPassword, String loginName);
|
||||
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
<beans:beans xmlns="http://www.springframework.org/schema/security"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
|
||||
|
||||
|
|
@ -28,16 +29,33 @@
|
|||
|
||||
</http>
|
||||
|
||||
<authentication-provider user-service-ref="defaultUserDetailsService">
|
||||
<!--
|
||||
<password-encoder hash="md5">
|
||||
-->
|
||||
<!-- NOTE: see IPasswordEncoderService's JavaDoc for restrictions
|
||||
on "user-property". -->
|
||||
<!--
|
||||
<salt-source user-property="username"/>
|
||||
</password-encoder>
|
||||
-->
|
||||
</authentication-provider>
|
||||
<!--
|
||||
Beans used by Spring Security (current configuration assumes users
|
||||
are registered in the database).
|
||||
-->
|
||||
<beans:bean id="passwordEncoder" class="org.springframework.security.providers.encoding.ShaPasswordEncoder">
|
||||
<beans:constructor-arg value="512"/>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="saltSource" class="org.springframework.security.providers.dao.salt.ReflectionSaltSource"
|
||||
p:userPropertyToUse="username"/>
|
||||
|
||||
<beans:bean id="authenticationProvider" class="org.springframework.security.providers.dao.DaoAuthenticationProvider"
|
||||
p:passwordEncoder-ref="passwordEncoder" p:saltSource-ref="saltSource" p:userDetailsService-ref="dbUserDetailsService">
|
||||
<custom-authentication-provider/>
|
||||
</beans:bean>
|
||||
|
||||
<!--
|
||||
Beans used by the Naval Planner Web application when users are
|
||||
registered in the database. When users are registered externally
|
||||
(e.g. in a LDAP server), these lines may be commented.
|
||||
-->
|
||||
<beans:bean id="dbUserDetailsService" class="org.navalplanner.web.users.services.DBUserDetailsService"/>
|
||||
|
||||
<beans:bean id="dbPasswordEncoderService" class="org.navalplanner.web.users.services.DBPasswordEncoderService"
|
||||
p:passwordEncoder-ref="passwordEncoder" p:saltSource-ref="saltSource"/>
|
||||
|
||||
<beans:bean id="usersBootstrapInDB" class="org.navalplanner.web.users.bootstrap.UsersBootstrapInDB"
|
||||
p:dbPasswordEncoderService-ref="dbPasswordEncoderService"/>
|
||||
|
||||
</beans:beans>
|
||||
|
|
|
|||
|
|
@ -24,9 +24,14 @@ package org.navalplanner.web.test;
|
|||
* A class containing constants for global names.
|
||||
*
|
||||
* @author Manuel Rego Casasnovas <mrego@igalia.com>
|
||||
* @author Fernando Bellas Permuy <fbellas@udc.es>
|
||||
*/
|
||||
public class WebappGlobalNames {
|
||||
|
||||
public final static String WEBAPP_SPRING_CONFIG_TEST_FILE = "classpath:/navalplanner-webapp-spring-config-test.xml";
|
||||
public final static String WEBAPP_SPRING_CONFIG_TEST_FILE =
|
||||
"classpath:/navalplanner-webapp-spring-config-test.xml";
|
||||
|
||||
public final static String WEBAPP_SPRING_SECURITY_CONFIG_TEST_FILE =
|
||||
"classpath:/navalplanner-webapp-spring-security-config-test.xml";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,13 +24,14 @@ 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.test.WebappGlobalNames.WEBAPP_SPRING_CONFIG_TEST_FILE;
|
||||
import static org.navalplanner.web.test.WebappGlobalNames.WEBAPP_SPRING_SECURITY_CONFIG_TEST_FILE;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
|
||||
import org.navalplanner.business.users.daos.IUserDAO;
|
||||
import org.navalplanner.business.users.entities.User;
|
||||
import org.navalplanner.web.users.bootstrap.IUsersBootstrap;
|
||||
import org.navalplanner.web.users.bootstrap.IUsersBootstrapInDB;
|
||||
import org.navalplanner.web.users.bootstrap.MandatoryUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
|
@ -38,18 +39,19 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Tests for <code>IUsersBootstrap</code>.
|
||||
* Tests for <code>IUsersBootstrapInDB</code>.
|
||||
*
|
||||
* @author Fernando Bellas Permuy <fbellas@udc.es>
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = { BUSINESS_SPRING_CONFIG_FILE,
|
||||
WEBAPP_SPRING_CONFIG_FILE, WEBAPP_SPRING_CONFIG_TEST_FILE })
|
||||
@ContextConfiguration(locations = {BUSINESS_SPRING_CONFIG_FILE,
|
||||
WEBAPP_SPRING_CONFIG_FILE, WEBAPP_SPRING_CONFIG_TEST_FILE,
|
||||
WEBAPP_SPRING_SECURITY_CONFIG_TEST_FILE})
|
||||
@Transactional
|
||||
public class UsersBootstrapTest {
|
||||
public class UsersBootstrapInDBTest {
|
||||
|
||||
@Autowired
|
||||
private IUsersBootstrap usersBootstrap;
|
||||
private IUsersBootstrapInDB usersBootstrap;
|
||||
|
||||
@Autowired
|
||||
private IUserDAO userDAO;
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* This file is part of ###PROJECT_NAME###
|
||||
*
|
||||
* Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
|
||||
* Desenvolvemento Tecnolóxico de Galicia
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.test.WebappGlobalNames.WEBAPP_SPRING_CONFIG_TEST_FILE;
|
||||
import static org.navalplanner.web.test.WebappGlobalNames.WEBAPP_SPRING_SECURITY_CONFIG_TEST_FILE;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
|
||||
import org.navalplanner.business.users.daos.IUserDAO;
|
||||
import org.navalplanner.business.users.entities.User;
|
||||
import org.navalplanner.web.users.bootstrap.IUsersBootstrapInDB;
|
||||
import org.navalplanner.web.users.bootstrap.MandatoryUser;
|
||||
import org.navalplanner.web.users.services.IDBPasswordEncoderService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Tests for <code>DBPasswordEncoderService</code>.
|
||||
*
|
||||
* @author Fernando Bellas Permuy <fbellas@udc.es>
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {BUSINESS_SPRING_CONFIG_FILE,
|
||||
WEBAPP_SPRING_CONFIG_FILE, WEBAPP_SPRING_CONFIG_TEST_FILE,
|
||||
WEBAPP_SPRING_SECURITY_CONFIG_TEST_FILE})
|
||||
@Transactional
|
||||
public class DBPasswordEncoderServiceTest {
|
||||
|
||||
@Autowired
|
||||
private IDBPasswordEncoderService dbPasswordEncoderService;
|
||||
|
||||
@Autowired
|
||||
private IUsersBootstrapInDB usersBootstrap;
|
||||
|
||||
@Autowired
|
||||
private IUserDAO userDAO;
|
||||
|
||||
@Test
|
||||
public void testEncodePassword() throws InstanceNotFoundException {
|
||||
|
||||
usersBootstrap.loadRequiredData();
|
||||
|
||||
for (MandatoryUser u : MandatoryUser.values()) {
|
||||
|
||||
String encodedPassword = dbPasswordEncoderService.encodePassword(
|
||||
u.getClearPassword(), u.getLoginName());
|
||||
User user = userDAO.findByLoginName(u.getLoginName());
|
||||
|
||||
assertEquals(user.getPassword(), encodedPassword);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ 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.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;
|
||||
|
|
@ -31,7 +32,7 @@ import java.util.Set;
|
|||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.navalplanner.business.users.entities.UserRole;
|
||||
import org.navalplanner.web.users.bootstrap.IUsersBootstrap;
|
||||
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;
|
||||
|
|
@ -42,22 +43,22 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Tests for implementations of Spring Security's
|
||||
* <code>UserDetailsService</code>.
|
||||
* Tests for <code>DBUserDetailsService</code>.
|
||||
*
|
||||
* @author Fernando Bellas Permuy <fbellas@udc.es>
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = { BUSINESS_SPRING_CONFIG_FILE,
|
||||
WEBAPP_SPRING_CONFIG_FILE, WEBAPP_SPRING_CONFIG_TEST_FILE })
|
||||
@ContextConfiguration(locations = {BUSINESS_SPRING_CONFIG_FILE,
|
||||
WEBAPP_SPRING_CONFIG_FILE, WEBAPP_SPRING_CONFIG_TEST_FILE,
|
||||
WEBAPP_SPRING_SECURITY_CONFIG_TEST_FILE})
|
||||
@Transactional
|
||||
public class UserDetailsServiceTest {
|
||||
public class DBUserDetailsServiceTest {
|
||||
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Autowired
|
||||
private IUsersBootstrap usersBootstrap;
|
||||
private IUsersBootstrapInDB usersBootstrap;
|
||||
|
||||
@Test
|
||||
public void testLoadUserByUsername() {
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
||||
|
||||
<bean id="passwordEncoder" class="org.springframework.security.providers.encoding.ShaPasswordEncoder">
|
||||
<constructor-arg value="512"/>
|
||||
</bean>
|
||||
|
||||
<bean id="saltSource" class="org.springframework.security.providers.dao.salt.ReflectionSaltSource"
|
||||
p:userPropertyToUse="username"/>
|
||||
|
||||
<bean id="dbUserDetailsService" class="org.navalplanner.web.users.services.DBUserDetailsService"/>
|
||||
|
||||
<bean id="dbPasswordEncoderService" class="org.navalplanner.web.users.services.DBPasswordEncoderService"
|
||||
p:passwordEncoder-ref="passwordEncoder" p:saltSource-ref="saltSource"/>
|
||||
|
||||
<bean id="usersBootstrapInDB" class="org.navalplanner.web.users.bootstrap.UsersBootstrapInDB"
|
||||
p:dbPasswordEncoderService-ref="dbPasswordEncoderService"/>
|
||||
|
||||
</beans>
|
||||
Loading…
Add table
Reference in a new issue