Composite Handler LDAP-Database. Import of users from LDAP. Support of two types of users (LDAP and Database).

FEA ItEr74S09LdapAuhentication
This commit is contained in:
Cristina Alvarino 2011-05-27 14:27:49 +02:00 committed by Manuel Rego Casasnovas
parent d6ce2dac17
commit 92437394c6
7 changed files with 242 additions and 48 deletions

View file

@ -26,6 +26,7 @@ import org.navalplanner.business.common.BaseEntity;
* This entity will be used to store the LDAP connection properties for
* authentication
*
* @author Ignacio Diaz <ignacio.diaz@comtecsf.es>
* @author Cristina Alvarino <cristina.alvarino@comtecsf.es>
*
*/
@ -48,11 +49,10 @@ public class LDAPConfiguration extends BaseEntity {
private String ldapPassword;
// TODO Almacena si se guardarán los passwords del ldap en la bd
// LDAP passwords will be imported to DB or not
private Boolean ldapSavePasswordsDB;
// TODO Guarda si se va a usar la autenticación con el ldap o no(la de
// navalplan)
// LDAP Authentication will be used or not
private Boolean ldapAuthEnabled;
public String getLdapUserId() {
@ -103,7 +103,7 @@ public class LDAPConfiguration extends BaseEntity {
this.ldapPassword = ldapPassword;
}
public Boolean getLdapSavePasswordsDB() {
public Boolean isLdapSavePasswordsDB() {
return ldapSavePasswordsDB;
}

View file

@ -38,6 +38,7 @@ import org.navalplanner.business.users.daos.IUserDAO;
* @author Fernando Bellas Permuy <fbellas@udc.es>
* @author Jacobo Aragunde Perez <jaragunde@igalia.com>
* @author Cristina Alvarino Perez <cristina.alvarino@comtecsf.es>
* @author Ignacio Diaz Teijido <ignacio.diaz@comtecsf.es>
*
*/
public class User extends BaseEntity {
@ -57,7 +58,7 @@ public class User extends BaseEntity {
private Scenario lastConnectedScenario;
// TODO if a user is a navalplan user or not (ldap)
private Boolean navalplanUser;
private Boolean navalplanUser = true;
/**
* Necessary for Hibernate. Please, do not call it.
@ -91,7 +92,6 @@ public class User extends BaseEntity {
this.loginName = loginName;
}
@NotEmpty(message = "password not specified")
public String getPassword() {
return password;
}

View file

@ -21,7 +21,7 @@
<version name="version" access="property" type="long" />
<property name="loginName" not-null="true" unique="true"
column="login_name" />
<property name="password" not-null="true"/>
<property name="password"/>
<property name="email"/>
<property name="disabled"/>
<property name="navalplanUser" column="navalplan_user"/>

View file

@ -18,42 +18,65 @@
*/
package org.navalplanner.web.users.services;
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.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.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.AuthenticationProvider;
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;
import org.springframework.transaction.annotation.Transactional;
/**
* 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
* <https
* This provider implements the process explained in <https
* ://wiki.navalplan.org/twiki/bin/view/NavalPlan/AnA04S06LdapAuthentication>
*
* At this time it authenticates user against LDAP and then searches it in BD to
* use the BD user in application.
*
* @author Ignacio Diaz <ignacio.diaz@comtecsf.es>
* @author Cristina Alvarino <cristina.alvarino@comtecsf.es>
* @author Ignacio Diaz Teijido <ignacio.diaz@comtecsf.es>
* @author Cristina Alvarino Perez <cristina.alvarino@comtecsf.es>
*
*/
public class LDAPCustomAuthenticationProvider extends
AbstractUserDetailsAuthenticationProvider {
AbstractUserDetailsAuthenticationProvider implements
AuthenticationProvider {
@Autowired
private IAdHocTransactionService transactionService;
@Autowired
private IConfigurationDAO configurationDAO;
@Autowired
private IUserDAO userDAO;
private LDAPConfiguration configuration;
// Template to search in LDAP
private LdapTemplate ldapTemplate;
// Place in LDAP where username is
private String userId;
private UserDetailsService userDetailsService;
private DBPasswordEncoderService passwordEncoderService;
private static final String COLON = ":";
@Override
protected void additionalAuthenticationChecks(UserDetails arg0,
UsernamePasswordAuthenticationToken arg1)
@ -61,25 +84,169 @@ public class LDAPCustomAuthenticationProvider extends
// No needed at this time
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Transactional(readOnly = true)
@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()))) {
final String usernameInserted = username;
User user = null;
throw new BadCredentialsException("User is not in LDAP.");
// Gets user from DB if exists
user = (User) transactionService
.runOnReadOnlyTransaction(new IOnTransaction() {
@Override
public Object execute() {
try {
return userDAO.findByLoginName(usernameInserted);
} catch (InstanceNotFoundException e) {
return null;
}
}
});
// If user != null then exists in NavalPlan
if (null != user && user.isNavalplanUser()) {
// is a NavalPlan user, then we must authenticate against DB
String encodedPassword = passwordEncoderService.encodePassword(
authentication.getCredentials().toString(), username);
if (encodedPassword.equals(user.getPassword())) {
// user credentials are ok
return getUserDetailsService().loadUserByUsername(username);
} else {
throw new BadCredentialsException(
"Credentials are not the same as in database.");
}
} else {
// Gets and returns user from DB once authenticated against LDAP
return getUserDetailsService().loadUserByUsername(username);
// is a LDAP or null user, then we must authenticate against LDAP
// if LDAP is enabled
// Gets the LDAPConfiguration properties
configuration = (LDAPConfiguration) transactionService
.runOnReadOnlyTransaction(new IOnTransaction() {
@Override
public Object execute() {
return configurationDAO.getConfiguration()
.getLdapConfiguration();
}
});
if (configuration.getLdapAuthEnabled()) {
// Establishes the context for LDAP connection.
LDAPCustomContextSource context = (LDAPCustomContextSource) ldapTemplate
.getContextSource();
context.setUrl(configuration.getLdapHost() + COLON
+ configuration.getLdapPort());
context.setBase(configuration.getLdapBase());
context.setUserDn(configuration.getLdapUserDn());
context.setPassword(configuration.getLdapPassword());
try {
context.afterPropertiesSet();
} catch (Exception e) {
// This exception will be never reached if the LDAP
// properties are
// well-formed.
e.printStackTrace();
}
// Sets the new context to ldapTemplate
ldapTemplate.setContextSource(context);
// Test authentication for user against LDAP
if (ldapTemplate.authenticate(DistinguishedName.EMPTY_PATH,
new EqualsFilter(configuration.getLdapUserId(),
username).toString(), authentication
.getCredentials().toString())) {
// Authentication against LDAP was ok
if (null == user) {
// user does not exist in NavalPlan must be imported
final User userNavalplan = User.create();
userNavalplan.setLoginName(username);
// we must check if it is needed to save LDAP passwords
// in
// DB
String encodedPassword = null;
if (configuration.isLdapSavePasswordsDB())
encodedPassword = passwordEncoderService
.encodePassword(authentication
.getCredentials().toString(),
username);
userNavalplan.setPassword(encodedPassword);
userNavalplan.setNavalplanUser(false);
userNavalplan.setDisabled(false);
userNavalplan.addRole(UserRole.ROLE_ADMINISTRATION);
transactionService
.runOnTransaction(new IOnTransaction() {
@Override
public Object execute() {
userDAO.save(userNavalplan);
return true;
}
});
} else {
// user exists in NavalPlan
if (configuration.isLdapSavePasswordsDB()) {
String encodedPassword = passwordEncoderService
.encodePassword(authentication
.getCredentials().toString(),
username);
// We must test if user had password in database,
// because the configuration
// of importing passwords could be changed after 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;
}
});
}
}
}
// Gets and returns user from DB once authenticated against
// LDAP
return getUserDetailsService().loadUserByUsername(username);
} else {
throw new BadCredentialsException("User is not in LDAP.");
}
} else {
// LDAP is not enabled we must check if the LDAP user is in DB
String encodedPassword = passwordEncoderService.encodePassword(
authentication.getCredentials().toString(), username);
if (null != user.getPassword()
&& encodedPassword.equals(user.getPassword())) {
// user credentials are ok
return getUserDetailsService().loadUserByUsername(username);
} else {
throw new BadCredentialsException(
"Authenticating LDAP user against LDAP. Maybe LDAP is out of service. "
+ "Credentials are not the same as in database.");
}
}
}
}
public DBPasswordEncoderService getPasswordEncoderService() {
return passwordEncoderService;
}
public void setPasswordEncoderService(
DBPasswordEncoderService passwordEncoderService) {
this.passwordEncoderService = passwordEncoderService;
}
// Getters and setters
public LdapTemplate getLdapTemplate() {
return ldapTemplate;
@ -89,14 +256,6 @@ public class LDAPCustomAuthenticationProvider extends
this.ldapTemplate = ldapTemplate;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}

View file

@ -0,0 +1,36 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.navalplanner.web.users.services;
import org.springframework.ldap.core.support.LdapContextSource;
/**
* A Class used to extend LDAPContextSource and set the properties for LDAP
*
* @author Cristina Alvarino Perez <cristina.alvarino@comtecsf.es>
* @author Ignacio Diaz Teijido <ignacio.diaz@comtecsf.es>
*
*/
public class LDAPCustomContextSource extends LdapContextSource {
public LDAPCustomContextSource() {
super();
this.setUrl("ldap://localhost:389");
}
}

View file

@ -43,8 +43,8 @@ import org.springframework.transaction.annotation.Transactional;
* At this time it takes values from authenticated user (LDAP or DB) and gets
* from DB the user properties.
*
* @author Ignacio Diaz <ignacio.diaz@comtecsf.es>
* @author Cristina Alvarino <cristina.alvarino@comtecsf.es>
* @author Ignacio Diaz Teijido <ignacio.diaz@comtecsf.es>
* @author Cristina Alvarino Perez <cristina.alvarino@comtecsf.es>
*
*/
public class LDAPUserDetailsService implements UserDetailsService {
@ -71,7 +71,10 @@ public class LDAPUserDetailsService implements UserDetailsService {
scenario = PredefinedScenarios.MASTER.getScenario();
}
return new CustomUser(user.getLoginName(), user.getPassword(),
String password = user.getPassword();
if (null == password)
password = "foo";
return new CustomUser(user.getLoginName(), password,
!user.isDisabled(), true, // accountNonExpired
true, // credentialsNonExpired
true, // accountNonLocked
@ -89,7 +92,5 @@ public class LDAPUserDetailsService implements UserDetailsService {
}
return grantedAuthorities;
}
}

View file

@ -6,7 +6,7 @@
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">
<!-- NOTE: see http://static.springsource.org/spring-security/site/docs/2.0.x/apidocs/org/springframework/security/vote/AuthenticatedVoter.html
<!-- NOTE: see http://static.springsource.org/spring-security/site/docs/2.0.x/apidocs/org/springframework/security/vote/AuthenticatedVoter.htm
for an explanation of the meaning of IS_AUTHENTICATED_ANONYMOUSLY and IS_AUTHENTICATED_FULLY. -->
<http auto-config="true" realm="NavalPlan Web Application">
@ -64,31 +64,29 @@
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> -->
<custom-authentication-provider/> </beans:bean> -->
<!-- Beans used by the NavalPlan 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" /> -->
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 used by the NavalPlan Web Application when users are registered
<!-- Beans used by the NavalPlan Web Application when users are registerd
in LDAP. At this moment users MUST be also in database with same username.
This will be changed in the near future. the url, base, userDN and password
properties must be set with the proper values -->
<beans:bean id="contextSource"
class="org.springframework.ldap.core.support.LdapContextSource"
p:url="ldap://192.168.65.146:389" p:base="dc=comtecsf,dc=es"
p:userDn="cn=admin,dc=comtecsf,dc=es" p:password="proelldap">
class="org.navalplanner.web.users.services.LDAPCustomContextSource">
</beans:bean>
<beans:bean id="ldapTemplate"
@ -103,7 +101,8 @@
<beans:bean id="authenticationProvider"
class="org.navalplanner.web.users.services.LDAPCustomAuthenticationProvider"
p:userDetailsService-ref="ldapUserDetailsService"
p:ldapTemplate-ref="ldapTemplate" p:userId="uid">
p:ldapTemplate-ref="ldapTemplate"
p:passwordEncoderService-ref="dbPasswordEncoderService">
<custom-authentication-provider />
</beans:bean>
@ -111,5 +110,4 @@
Provider. -->
<beans:bean id="ldapUserDetailsService"
class="org.navalplanner.web.users.services.LDAPUserDetailsService" />
</beans:beans>
</beans:beans>