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

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

Improved the template editor so that template fields can be removed from templates, even if the templates are in use, but only if the template fields are never filled. (see ticket #74)

Also changed the user registration so that the administrator confirmation mails will be sent to the administrators in production environment, but still to gscfproject@… in other environments.

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$
12 * $Author$
13 * $Date$
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_PRODUCTION) ) {
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                        user.password = springSecurityService.encodePassword(command.password, salt)
242                        user.email = command.email
243                        user.save()
244                }
245
246                redirect controller: 'home'
247        }
248
249    private String generatePassword( int length ) {
250        String validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-!@#%^&*()/\\;:"
251        int maxIndex = validChars.length()
252       
253        java.util.Random rnd = new java.util.Random(System.currentTimeMillis()*(new java.util.Random().nextInt()))
254        String resultID = ""
255       
256        for ( i in 0..length ) {
257            int rndPos = Math.abs(rnd.nextInt() % maxIndex);
258            resultID += validChars.charAt(rndPos)
259        }
260
261        return resultID
262    }
263
264        static final passwordValidator = { String password, command ->
265                if( password == "" ) {
266                        return
267                }
268
269                if (command.username && command.username.equals(password)) {
270                        return 'command.password.error.username'
271                }
272
273                if (password && password.length() >= 8 && password.length() <= 64 &&
274                                (!password.matches('^.*\\p{Alpha}.*$') ||
275                                !password.matches('^.*\\p{Digit}.*$') ||
276                                !password.matches('^.*[!@#$%^&].*$'))) {
277                        return 'command.password.error.strength'
278                }
279        }
280
281        static final password2Validator = { value, command ->
282                if (command.password != command.password2) {
283                        return 'command.password2.error.mismatch'
284                }
285        }
286
287        static final usernameValidator = { value, command ->
288                def user = SecUser.findByUsername( command.username );
289                if( user ) {
290            if( user.enabled ) {
291                                return "registerUserCommand.username.unique"
292                        } else if( user.dateCreated.after( new Date() - DAYS_BEFORE_EXPIRY ) ) {
293                                return "registerUserCommand.username.notyetconfirmed"
294                        } else {
295                                RegistrationCode.deleteByUser(user);
296                                user.delete(flush:true);
297                        }
298        }
299        }
300
301}
302
303class ProfileCommand {
304
305        String username
306        String oldPass
307        String email
308        String password
309        String password2
310
311        static constraints = {
312                username blank: false
313                email blank: false, email: true
314                password blank: true, minSize: 8, maxSize: 64, validator: UserRegistrationController.passwordValidator
315                password2 validator: UserRegistrationController.password2Validator
316        }
317}
318
319class RegisterUserCommand {
320
321        String username
322        String email
323
324        static constraints = {
325                email(blank: false, email: true)
326                username(blank: false, validator: UserRegistrationController.usernameValidator)
327        }
328}
Note: See TracBrowser for help on using the repository browser.