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

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

Added an expiry date to the confirmation email for the user and administrator. See ticket #189

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