2010-04-01 6 views
1

J'utilise le plugin Acegi Security pour Grails, et l'authentification via LDAP. Les journaux d'application montrent que lors de la connexion, nous pouvons authentifier l'utilisateur et obtenir ses rôles via LDAP, mais la connexion échoue car les détails utilisateur ne peuvent pas être trouvés dans la base de données de l'application.Auto-création des détails de l'utilisateur avec Grails et LDAP

Existe-t-il un moyen de créer et d'enregistrer automatiquement un objet de domaine de base Informations utilisateur de base s'il n'en existe pas déjà?

- Mise à jour
Voici les entrées de débogage pertinentes que je vois actuellement

DEBUG populator.DefaultLdapAuthoritiesPopulator - Roles from search: [Role1, Role2, etc] 
ERROR springsecurity.GrailsDaoImpl - User not found: MyUserName 
DEBUG rememberme.TokenBasedRememberMeServices - Interactive login attempt was unsuccessful. 

Répondre

2

Bien sûr. Vous devez mettre en œuvre sur mesure AuthProvider

SecurityConfig.groovy:

security { 
    providerNames = ['ldapAuthProvider'] 
} 

Ldap Auth Fournisseur:

import domain.user.AppUser 
import org.apache.commons.codec.digest.DigestUtils 
import org.apache.log4j.Logger 
import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUserImpl 
import org.springframework.security.BadCredentialsException 
import org.springframework.security.GrantedAuthority 
import org.springframework.security.GrantedAuthorityImpl 
import org.springframework.security.providers.UsernamePasswordAuthenticationToken 
import org.springframework.security.providers.dao.AbstractUserDetailsAuthenticationProvider 
import org.springframework.security.userdetails.UserDetails 

/** 
* Authentication provider that checks user credentials against LDAP 
*/ 
class LdapAuthProvider extends AbstractUserDetailsAuthenticationProvider { 
    private static final Logger log = Logger.getLogger(LdapAuthProvider.class) 

    def appUserService 

    /** 
    * Checks password hash stored in the session with password in authentication token. 
    */ 
    protected void additionalAuthenticationChecks(UserDetails details, 
     UsernamePasswordAuthenticationToken authentication) { 

     if (details.password != DigestUtils.md5Hex(authentication.credentials)) { 
      throw new BadCredentialsException(details.username) 
     } 
    } 

    /** 
    * Retrieves user from LDAP, 
    * checks credentials, 
    * updates local copy of user data, 
    * returns user details. 
    */ 
    protected UserDetails retrieveUser(String login, UsernamePasswordAuthenticationToken authentication) { 
     AppUser.withTransaction { 
      log.debug("Trying to retrieve user \"$login\"...") 
      def password = authentication.credentials?.toString() 

      def ldapUser = appUserService.findLdapUser(login) 
      if (!(password && ldapUser?.authenticate(password))) { 
       log.debug("Can't authenticate \"$login\"") 
       throw new BadCredentialsException(login) 
      } 

      AppUser localUser = AppUser.findByLogin(login, [cache: true]) 

      if (!localUser) { 
       log.debug("Can't authenticate \"$login\"") 
       localUser = appUserService.updateLocalUser(ldapUser) 
      } 

      log.debug("User \"$login\" is authenticated.") 
      def authorities = localUser.collectAuthorities().collect {String authority -> 
       log.debug("\thas right \"$authority\"") 
       new GrantedAuthorityImpl(authority) 
      } 

      def userDetails = new AppUser(); 
      userDetails.setAssignedTemplate(localUser.assignedTemplate) 
      userDetails.setFullName(localUser.getFullName()) 
      userDetails.setLogin(localUser.getLogin()) 
      userDetails.setEmail(localUser.getEmail()) 
      userDetails.setDisabled(localUser.getDisabled()) 
      userDetails.setManager(localUser.getManager()) 
      userDetails.setRoles(new HashSet(localUser.getRoles())) 


      log.debug("Retrieving user \"$login\" is completed.") 
      return new GrailsUserImpl(userDetails.login, DigestUtils.md5Hex(password), true, true, true, true, 
       authorities.toArray(new GrantedAuthority[authorities.size()]), userDetails) 
     } 
    } 
} 

Et appUserService.updateLocalUser(ldapUser) vous devez créer/modifier votre objet de domaine et persistent dans la base de données.

AppUser updateLocalUser(LdapUser ldapUser) { 
    def login = ldapUser.login 
    log.debug("Start updating local user ${login}...") 
    def localUser = AppUser.findByLogin(login, [cache: true]) ?: new AppUser() 
    if (localUser.id) { 
     log.debug("user $login was found in local DB") 
     if (localUser.disabled^ldapUser.isDisabled()) { 
      log.debug("...user ${login} has been ${localUser.disabled ? 'activated' : 'disabled'}...") 
     } 
    } else { 
     log.debug("user $login is new") 
    } 
    localUser.login = login 
    localUser.email = ldapUser.email 
    localUser.fullName = ldapUser.fullName ?: login 
    localUser.disabled = ldapUser.isDisabled(); 
    localUser.roles?.clear() 
    ldapUser.memberOf.collect { Role.findByLdapName(it, [cache: true]) }.each {role -> 
     if (role) { 
      localUser.addToRoles(role) 
     } 
    }; 
    localUser.save(flush: true) 
    log.debug("Update local user $login is complete.") 
} 

MISE À JOUR # 1

Vous pouvez mettre en œuvre UserDetailsService personnalisée:

package com.foo.bar; 

import org.springframework.security.userdetails.UserDetails; import org.springframework.security.userdetails.UserDetailsService; 

public class MyUserDetailsService implements UserDetailsService { 

public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException, DataAccessException { 

// lookup user and data 

return new MyUserDetails(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, id, fullName); } } 
+0

Ai-je vraiment besoin de mettre en œuvre le LdapProvider personnalisé? En utilisant le standard dans Spring-Security, il semble que je peux obtenir les données d'utilisateur/rôle pertinentes à partir du magasin LDAP. J'ai juste besoin de coller dans un UserDetailsService personnalisé ou quelque chose pour passer la partie où il se trouve dans la base de données pour l'objet utilisateur. J'ai inclus certaines des instructions de débogage dans la question –

+0

Le code ci-dessus est du projet réel avec quelques cleaups. J'ai réfléchi à votre commentaire et je pense que vous pouvez utiliser UserDetailsService personnalisé. J'ai mis à jour la réponse. – uthark

+0

Oui, je l'ai trié avec un UserDetailsService personnalisé. La seule chose est qu'il existe une deuxième méthode loadUserByUsername, qui prend le nom d'utilisateur et un booléen qui représente si rechercher des rôles à partir de la base de données. Dans mon cas, la deuxième méthode était appelée, et je devais la remplacer. –