tags:

views:

937

answers:

3

I'm looking for an implementation of a password retrieval procedure using the Acegi plugin for Grails...Google is failing me...

+3  A: 

IMHO this is currently not part of the Acegi plugin. I've added a forgotPassword action to the LoginController:

    def forgotPassword = {
    if (params.username) {
        User user = User.findByUsername(params.username)
        if (user) {
            def password = randomService.generateRandomString(8)
            user.passwd = authenticateService.encodePassword(password)
            if (!user.save(flush:true)) {
                user.errors.each {
                    log.error "err $it"
                }
                flash.message = message(code: "LoginController.msg.forgot.error")
            } else {
                sendMail {
                    to user.username
                    subject message(code:"LoginController.mail.forgot.subject" )
                    body(view:"forgotPasswordEmail", model: [person:user, password:password])
                }
                flash.message = message(code:"LoginController.msg.forgot", args:[user.username] )
            }
        } else {
            flash.message = message(code:"LoginController.msg.forgot.unknown", args:[params.username])
        }
    }
}

The code above uses the Grails mail plugin.

Stefan
This is a good starting implementation. One potential problem with it is that it automatically resets the password, even if someone else requested it, so there's the potential for a malicious user to continually hit "reset password" and change the user's password without them actually wanting it changed (though they'd still get the e-mail).We did something like this, but instead had a table that held one time use tokens with a short time to live that are e-mailed to reset a password. If the token isn't used, the password is unchanged. Only one token in the table per user maximum.
Ted Naleid
Totally agreed. Another option would be a kind of security question that man websites e.g. "What's your mother's maiden name?". Only if the question is answered correctly, password will be reset. Maybe it would made sense, to factor out those account handling functions "recover password", "recover login name", "delete account" in a seperate plugin that depends on the acegi plugin.
Stefan
+3  A: 

Google is failing you because there isn't one. It's really not possible to reverse the hashed password (without brute force cracking and rainbow tables), and if it were, that'd mean that your system was not secure.

The common pattern is to e-mail the user that forgot their password with a one time use token that they can then use to reset the password to whatever they want to. This isn't built into the framework, but it's not too hard to do manually (I'd suggest using the grails mail plugin).

Ted Naleid
+1  A: 

The Acegi plugin doesn't support this out of the box, but if you add the email-confirmation plugin, it's pretty easy to roll your own.

Here are the steps:

Create a password reset form that asks the user to enter their email address and new password.

The controller action that handles the password reset form should validate the data and use the email-confirmation plugin to send an email to the user with a link for them to click on to confirm their change of password. You can do this by calling the following method on the EmailConfirmationService service added by the plugin.

def sendConfirmation(String emailAddress, String theSubject, Map model = null, 
String userToken = null)

where:

emailAddress = address of user changing password
theSubject = subject of e-mail sent
model = any data passed to GSP that creates e-mail body
userToken = hashed user's password

when the user clicks on the link in the e-mail (refer to the plugin docs for info about how to customise this e-mail), the onConfirmation closure of the service will be invoked.

This closure should be assigned in Bootstrap.groovy like this:

def emailConfirmationService

def init = { servletContext -> 

  emailConfirmationService.onConfirmation = { email, hashedPassword ->

    User user = User.findByEmail(email)
    user.passwd = hashedPassword
    if (!user.save()) {
        // Handle this error, somehow....
    }

    // Then return a map which will redirect the user to the login screen  (for example)
    [controller:'userProfile', action:'login'] 
  } 
}

Notice that the user's email and hashed password are passed into this closure, enabling you to reset and save the user's password.

Don