source: trunk/grails-app/controllers/dbnp/authentication/UserRegistrationController.groovy @ 1712

Last change on this file since 1712 was 1712, checked in by robert@…, 10 years ago

Solved user registration bugs: changing password didn't work and lost password form resulted in an error

  • Property svn:keywords set to Rev Author Date
File size: 10.2 KB
Line 
1/**
2 * RegisterController Controler
3 *
4 * This controller handles user subscription
5 *
6 * @author      robert@isdat.nl (Robert Horlings)
7 * @since       20101016
8 * @package     dbnp.authentication
9 *
10 * Revision information:
11 * $Rev: 1712 $
12 * $Author: robert@isdat.nl $
13 * $Date: 2011-04-06 09:36:21 +0000 (wo, 06 apr 2011) $
14 */
15package dbnp.authentication
16import grails.plugins.springsecurity.Secured
17import org.codehaus.groovy.grails.commons.GrailsApplication
18import grails.util.GrailsUtil
19
20class UserRegistrationController {
21        static int DAYS_BEFORE_EXPIRY = 3;
22        static int DAYS_BEFORE_EXPIRY_ADMIN = 365;
23
24    def springSecurityService
25        def authenticationService
26   
27    /**
28     * Shows a simple registration form
29     */
30    def index = {
31    }
32
33    /**
34     * Registers a new user. Also sends an e-mail to the user and to the administrators
35     * for confirmation
36     */
37    def add = { RegisterUserCommand command ->
38               
39                command.validate()
40
41                if (command.hasErrors()) {
42                        def addSendUserLink = false;
43
44                        // Check the errors and append a link if needed
45                        command.errors.allErrors.each {
46                                if( it.code == "registerUserCommand.username.notyetconfirmed" ) {
47                                        addSendUserLink = true;
48                                }
49                        }
50
51                        flash.message = "";
52            render(view: "index", model: [username: params.username, email: params.email, command: command, addSendUserLink: addSendUserLink])
53                        return
54                }
55
56        // Generate a random password
57        def password = this.generatePassword(8)
58
59        def user = new SecUser(
60           username     : params.username,
61           email: params.email,
62           password: springSecurityService.encodePassword(password, params.username),
63           userConfirmed: false, adminConfirmed: false)
64       
65        // Redirect user if save fails
66        if( !user.save(failOnError: true) ) {
67            render(view: "index", model: [username: params.username, email: params.email])
68            return
69        }
70
71        // Clear the flash message so the user does not see old messages
72        flash.message = ""
73
74                sendUserConfirmationMail( user, password );
75                sendAdminConfirmationMail( user );
76
77        // Give the user a nice welcome page
78    }
79
80        def sendUserConfirmation = {
81                def user = SecUser.findByUsername( params.username );
82                if( !user ) {
83                        flash.message = "No user with this username is found in the database.";
84            render(view: "index", model: [username: params.username])
85                }
86                if( user.userConfirmed ) {
87                        flash.message = "This user has already been confirmed";
88            render(view: "index", model: [username: params.username])
89                }
90
91                // Remove old registration codes
92                RegistrationCode.deleteByUser(user);
93
94                // Create a new password
95        def password = this.generatePassword(8)
96                user.password = springSecurityService.encodePassword(password, user.username)
97
98                // Send a message
99                sendUserConfirmationMail(user, password);
100
101                flash.message = "A new confirmation email has been sent to your registered email address";
102                render(view: "index", model: [username: params.username])
103        }
104
105        private sendUserConfirmationMail( SecUser user, String password ) {
106                def userCode = new RegistrationCode(userId: user.id, expiryDate: new Date() + UserRegistrationController.DAYS_BEFORE_EXPIRY).save( flush: true );
107        def userLink = createLink( controller: 'userRegistration', action: 'confirmUser', params: [code: userCode.token], absolute: true )
108
109        // Send an email to the user
110        try {
111            sendMail {
112                to      user.email
113                subject "Registration at GSCF"
114                html    g.render(template:'/email/registrationConfirmationUser', model:[username: user.username, password: password, expiryDate: userCode.expiryDate, link: userLink])
115            }
116        } catch(Exception e) {
117            log.error "Problem sending email $e.message", e
118            flash.message = 'Email could not be sent'
119                        return false
120        }
121
122                return true
123        }
124
125        private sendAdminConfirmationMail( SecUser user ) {
126                def adminCode = new RegistrationCode(userId: user.id, expiryDate: new Date() + UserRegistrationController.DAYS_BEFORE_EXPIRY_ADMIN).save(flush: true)
127        def adminLink = createLink( controller: 'userRegistration', action: 'confirmAdmin', params: [code: adminCode.token], absolute: true )
128
129                // If we are in production, send the mails to all administrators
130                // Otherwise, send it to a default (spam) mail address
131                def adminMail = "gscfproject@gmail.com";
132                if ( !GrailsUtil.getEnvironment().equals(GrailsApplication.ENV_DEVELOPMENT) ) {
133                        def administrators = SecRole.findUsers( 'ROLE_ADMIN' );
134                        if( administrators.size() > 0 ) {
135                                adminMail = administrators.email.toArray();
136                        }
137                }
138
139        // Send an email to the administrators
140        try {
141                        // Determine administrator email addresses
142            sendMail {
143                to      adminMail
144                subject "New user (" + user.username + ") at GSCF"
145                html    g.render(template:"/email/registrationConfirmationAdmin", model:[username: user.username, email: user.email, link: adminLink])
146            }
147        } catch(Exception e) {
148            log.error "Problem sending email $e.message", e
149            flash.message = "Email could not be sent to administrators"
150                        return false
151        }
152                return true
153        }
154
155
156    def confirmUser = {
157        def token = params.code
158
159                def registrationCode = token ? RegistrationCode.findByToken(token) : null
160                if (!registrationCode) {
161            flash.message = "No user found with given parameters. Please make sure you have copied the URL correctly."
162            return
163                }
164
165                if (registrationCode.expiryDate.before(new Date())) {
166            flash.message = "Your registration should have been confirmed within " + UserRegistrationController.DAYS_BEFORE_EXPIRY + " days. This confirmation link has expired. Please register again."
167            return
168                }
169
170                def user = SecUser.findById(registrationCode.userId)
171
172        if( user.userConfirmed ) {
173            flash.message = "This registration has already been confirmed."
174            return
175        }
176
177        user.userConfirmed = true
178        user.save(flush: true)
179
180                // Remove the registrationCode
181                registrationCode.delete();
182
183        if( user.adminConfirmed ) {
184            flash.message = "Your registration has been confirmed. You can now login."
185        } else {
186            flash.message = "Your registration has been confirmed. An administrator has to approve your registration before you can use it."
187        }
188    }
189
190    @Secured(['ROLE_ADMIN', 'IS_AUTHENTICATED_FULLY'])
191    def confirmAdmin = {
192
193        def token = params.code
194
195                def registrationCode = token ? RegistrationCode.findByToken(token) : null
196                if (!registrationCode) {
197            flash.message = "No user found with specified code. Please make sure you have copied the URL correctly."
198            return
199                }
200
201                if (registrationCode.expiryDate.before(new Date())) {
202            flash.message = "You should have approved this registration within " + UserRegistrationController.DAYS_BEFORE_EXPIRY_ADMIN + " days. This confirmation link has expired."
203            return
204                }
205
206                def user = SecUser.findById(registrationCode.userId)
207
208        if( user.adminConfirmed ) {
209            flash.message = "This user has already been approved. This might be done by another administrator"
210            return
211        }
212
213        user.adminConfirmed = true
214        user.save(flush: true)
215
216                // Remove the registrationCode
217                registrationCode.delete();
218
219        flash.message = "The registration of " + user.username + " is approved."
220    }
221
222    @Secured(['IS_AUTHENTICATED_REMEMBERED'])
223        def profile = {
224                [ user: authenticationService.getLoggedInUser() ]
225        }
226
227    @Secured(['IS_AUTHENTICATED_REMEMBERED'])
228        def updateProfile = { ProfileCommand command ->
229                def user = authenticationService.getLoggedInUser();
230                command.username = user.username
231                command.oldPass = user.password
232                command.validate()
233
234                if (command.hasErrors()) {
235                        render( view: 'profile', model: [user: user, command: command]);
236                        return
237                }
238
239                String salt = user.username
240                RegistrationCode.withTransaction { status ->
241                        if( command.password != "" ) 
242                                user.password = springSecurityService.encodePassword(command.password, salt)
243                        user.email = command.email
244                        user.save()
245                }
246
247                redirect controller: 'home'
248        }
249
250    private String generatePassword( int length ) {
251        String validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-!@#%^&*()/\\;:"
252        int maxIndex = validChars.length()
253       
254        java.util.Random rnd = new java.util.Random(System.currentTimeMillis()*(new java.util.Random().nextInt()))
255        String resultID = ""
256       
257        for ( i in 0..length ) {
258            int rndPos = Math.abs(rnd.nextInt() % maxIndex);
259            resultID += validChars.charAt(rndPos)
260        }
261
262        return resultID
263    }
264
265        static final passwordValidator = { String password, command ->
266                if( password == "" ) {
267                        return
268                }
269
270                if (command.username && command.username.equals(password)) {
271                        return 'command.password.error.username'
272                }
273
274                if (password && password.length() >= 8 && password.length() <= 64 &&
275                                (!password.matches('^.*\\p{Alpha}.*$') ||
276                                !password.matches('^.*\\p{Digit}.*$') ||
277                                !password.matches('^.*[!@#$%+^&].*$'))) {
278                        return 'command.password.error.strength'
279                }
280        }
281
282        static final password2Validator = { value, command ->
283                if (command.password != command.password2) {
284                        return 'command.password2.error.mismatch'
285                }
286        }
287
288        static final usernameValidator = { value, command ->
289                def user = SecUser.findByUsername( command.username );
290                if( user ) {
291            if( user.enabled ) {
292                                return "registerUserCommand.username.unique"
293                        } else if( user.dateCreated.after( new Date() - DAYS_BEFORE_EXPIRY ) ) {
294                                return "registerUserCommand.username.notyetconfirmed"
295                        } else {
296                                RegistrationCode.deleteByUser(user);
297                                user.delete(flush:true);
298                        }
299        }
300        }
301
302}
303
304class ProfileCommand {
305
306        String username
307        String oldPass
308        String email
309        String password
310        String password2
311
312        static constraints = {
313                username blank: false
314                email blank: false, email: true
315                password blank: true, minSize: 8, maxSize: 64, validator: UserRegistrationController.passwordValidator
316                password2 validator: UserRegistrationController.password2Validator
317        }
318}
319
320class RegisterUserCommand {
321
322        String username
323        String email
324
325        static constraints = {
326                email(blank: false, email: true)
327                username(blank: false, validator: UserRegistrationController.usernameValidator)
328        }
329}
Note: See TracBrowser for help on using the repository browser.