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:
parent
d6ce2dac17
commit
92437394c6
7 changed files with 242 additions and 48 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"/>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
Loading…
Add table
Reference in a new issue