I'm trying to force a user to change his password on the first login (as has been discussed here Grails spring-security - Can I intercept before success action to check required password change?).
However, neither UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY
(which is also deprecated) nor springSecurityService.authentication.name
does provide the entered username. All I get is __grails.anonymous.user__
what can't be used to set the user's new password.
Does anyone of you have an idea how to determine the entered username?
Thank you very much.
I didn't figure out how to pass on the username entered in the login form to the changePassword.gsp
. Nevertheless, I constructed a workaround which is not perfect yet but at least is working.
The authfail
closure in my custom login controller stays as has been presented in Grails spring-security - Can I intercept before success action to check required password change?
However, I deleted the changePassword
closure because I added a textfield in the changePassword.gsp
to enable the user to enter his username. That's also the reason why I think that this solution is not perfect, because normally I would not like to have to re-enter my username as well.
As I'm quite new to grails, some of you may have a hint or two on how to realize this functionality more elegant. I would really appreciate it :)
Following the source extracts for LoginController
and changePassword.gsp
.
LoginController.groovy
:
/**
* Callback after a failed login. Redirects to the auth page with a warning message.
*/
def authfail() {
String msg = ''
def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]
if (exception) {
if (exception instanceof AccountExpiredException) {
msg = g.message(code: "springSecurity.errors.login.expired")
}
else if (exception instanceof CredentialsExpiredException) {
msg = g.message(code: "springSecurity.errors.login.passwordExpired")
if (!springSecurityService.isAjax(request)) {
flash.message = msg
render view: 'changePassword'
return
}
}
// omitting other cases
}
if (springSecurityService.isAjax(request)) {
render([error: msg] as JSON)
}
else {
flash.message = msg
redirect action: 'auth', params: params
}
}
def updatePassword() {
String username = params.username
if (!username) {
flash.message = 'Sorry, an error has occurred'
redirect controller: 'login', action:'auth'
return
}
String password = params.password_old
String newPassword = params.password_new
String newPassword2 = params.password_new_rep
if (!password || !newPassword || !newPassword2 || newPassword != newPassword2) {
flash.message = 'Please enter your current password and a new password'
render view: 'changePassword', model: [username: username]
return
}
User user = userService.getUserByUsername(username)
if (!passwordEncoder.isPasswordValid(user.password, password, null /*salt*/)) {
flash.message = 'Current password is incorrect'
render view: 'changePassword', model: [username: username]
return
}
if (passwordEncoder.isPasswordValid(user.password, newPassword, null /*salt*/)) {
flash.message = 'Please choose a different password from your current one'
render view: 'changePassword', model: [username: username]
return
}
if (user.username.equals(newPassword)) {
flash.message = 'New password must not equal your username'
render view: 'changePassword', model: [username: username]
return
}
// success if we reach here!
user.password = newPassword
user.passwordExpired = false
userService.updateUser(user)
springSecurityService.reauthenticate(username, newPassword)
flash.message = 'Password changed successfully' + (springSecurityService.loggedIn ? '' : ', you can now login')
redirect uri: '/'
}
changePassword.gsp
:
<html>
<head>
<meta name="layout" content="general" />
<title>Change password</title>
</head>
<body>
<content tag="heading">Change password</content>
<content tag="content">
<g:if test="${flash.message }">
${flash.message }
</g:if>
<g:form name="changePassword" autocomplete="off">
<table>
<tr>
<td>Username</td>
<td><g:field type="text" name="username" required="" /></td>
</tr>
<tr>
<td>Old password</td>
<td><g:field type="password" name="password_old" required=""/></td>
</tr>
<tr>
<td>New password</td>
<td><g:field type="password" name="password_new" required=""/></td>
</tr>
<tr>
<td>New password (repeat)</td>
<td><g:field type="password" name="password_new_rep" required=""/></td>
</tr>
<tr>
<td></td>
<td>
<g:actionSubmit name="submit" value="Change password" action="updatePassword"/>
</td>
</tr>
</table>
</g:form>
</content>
<script>
$(document).ready(function() {
$('#username').focus();
});
</script>
</body>
</html>