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

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

Implemented ability to changed password and email address when logged in (ticket 165).

File size: 7.5 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    def springSecurityService
20        def authenticationService
21   
22    /**
23     * Shows a simple registration form
24     */
25    def index = {
26    }
27
28    /**
29     * Registers a new user. Also sends an e-mail to the user and to the administrators
30     * for confirmation
31     */
32    def add = {
33        if( !params.username || !params.email ) {
34            flash.message = "You must enter a username and provide an email address"
35            render(view: "index", model: [username: params.username, email: params.email])
36            return
37        }
38
39        // Check whether this username already exists
40        if( SecUser.findByUsername( params.username ) ) {
41            flash.message = "Username already exists"
42            render(view: "index", model: [username: params.username, email: params.email])
43            return
44        }
45
46        // Generate a random password
47        def password = this.generatePassword(8)
48
49        def user = new SecUser(
50           username: params.username,
51           email: params.email,
52           password: springSecurityService.encodePassword(password, params.username),
53           userConfirmed: false, adminConfirmed: false)
54       
55        // Redirect user if save fails
56        if( !user.save(failOnError: true) ) {
57            render(view: "index", model: [username: params.username, email: params.email])
58            return
59        }
60
61        // Clear the flash message so the user does not see old messages
62        flash.message = ""
63
64        // Create links for the user and administrator to click on. These codes are built from
65        // the username and encrypted password. They do not provide 100% security, since the codes
66        // could be broken, but it is enough for the confirmation step
67        def userCode = ( user.username + user.password + 'user' ).encodeAsMD5();
68        def adminCode = ( user.username + user.password + 'admin' ).encodeAsMD5();
69        def userLink = createLink( controller: 'userRegistration', action: 'confirmUser', params: [id: user.id, code: userCode], absolute: true )
70        def adminLink = createLink( controller: 'userRegistration', action: 'confirmAdmin', params: [id: user.id, code: adminCode], absolute: true )
71
72        // Send an email to the user
73        try {
74            sendMail {
75                to      params.email
76                subject "Registration at GSCF"
77                html    g.render(template:'/email/registrationConfirmationUser', model:[username: user.username, password: password, link: userLink])
78            }
79        } catch(Exception e) {
80            log.error "Problem sending email $e.message", e
81            flash.message = 'Email could not be sent'
82        }
83
84        // Send an email to the administrators
85        try {
86            sendMail {
87                to      "gscfproject@gmail.com"
88                subject "New user (" + user.username + ") at GSCF"
89                html    g.render(template:"/email/registrationConfirmationAdmin", model:[username: user.username, email: user.email, link: adminLink])
90            }
91        } catch(Exception e) {
92            log.error "Problem sending email $e.message", e
93            flash.message = "Email could not be sent to administrators"
94        }
95
96        // Give the user a nice welcome page
97        [username: user.username, password: password]
98    }
99
100    def confirmUser = {
101        def code = params.code
102        def id = params.id
103
104        def user = SecUser.findById(id)
105
106        def generatedCode = ( user.username + user.password + "user" ).encodeAsMD5()
107        if( !user || code != generatedCode ) {
108            flash.message = "No user found with given parameters. Please make sure you have copied the URL correctly."
109            return
110        }
111
112        if( user.userConfirmed ) {
113            flash.message = "This registration has already been confirmed."
114            return
115        }
116
117        user.userConfirmed = true
118        user.save(flush: true)
119
120        if( user.adminConfirmed ) {
121            flash.message = "Your registration has been confirmed. You can now login."
122        } else {
123            flash.message = "Your registration has been confirmed. An administrator has to approve your registration before you can use it."
124        }
125    }
126
127    @Secured(['ROLE_ADMIN', 'IS_AUTHENTICATED_FULLY'])
128    def confirmAdmin = {
129        def code = params.code
130        def id = params.id
131
132        def user = SecUser.findById(id)
133
134        def generatedCode = ( user.username + user.password + "admin" ).encodeAsMD5();
135        if( !user || code != generatedCode ) {
136            flash.message = "No user found with specified code. Please make sure you have copied the URL correctly."
137            return;
138        }
139
140        if( user.adminConfirmed ) {
141            flash.message = "This user has already been approved. This might be done by another administrator"
142            return
143        }
144
145        user.adminConfirmed = true
146        user.save(flush: true)
147
148        flash.message = "The registration of " + user.username + " is approved."
149    }
150
151    @Secured(['IS_AUTHENTICATED_REMEMBERED'])
152        def profile = {
153                [ user: authenticationService.getLoggedInUser() ]
154        }
155
156    @Secured(['IS_AUTHENTICATED_REMEMBERED'])
157        def updateProfile = { ProfileCommand command ->
158                def user = authenticationService.getLoggedInUser();
159                command.username = user.username
160                command.oldPass = user.password
161                command.validate()
162
163                if (command.hasErrors()) {
164                        render( view: 'profile', model: [user: user, command: command]);
165                        return
166                }
167
168                String salt = user.username
169                RegistrationCode.withTransaction { status ->
170                        user.password = springSecurityService.encodePassword(command.password, salt)
171                        user.email = command.email
172                        user.save()
173                }
174
175                redirect controller: 'home'
176        }
177
178    private String generatePassword( int length ) {
179        String validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-!@#%^&*()/\\;:"
180        int maxIndex = validChars.length()
181       
182        java.util.Random rnd = new java.util.Random(System.currentTimeMillis()*(new java.util.Random().nextInt()))
183        String resultID = ""
184       
185        for ( i in 0..length ) {
186            int rndPos = Math.abs(rnd.nextInt() % maxIndex);
187            resultID += validChars.charAt(rndPos)
188        }
189
190        return resultID
191    }
192
193        static final passwordValidator = { String password, command ->
194                if( password == "" ) {
195                        return
196                }
197
198                if (command.username && command.username.equals(password)) {
199                        return 'command.password.error.username'
200                }
201
202                if (password && password.length() >= 8 && password.length() <= 64 &&
203                                (!password.matches('^.*\\p{Alpha}.*$') ||
204                                !password.matches('^.*\\p{Digit}.*$') ||
205                                !password.matches('^.*[!@#$%^&].*$'))) {
206                        return 'command.password.error.strength'
207                }
208        }
209
210        static final password2Validator = { value, command ->
211                if (command.password != command.password2) {
212                        return 'command.password2.error.mismatch'
213                }
214        }
215}
216
217class ProfileCommand {
218
219        String username
220        String oldPass
221        String email
222        String password
223        String password2
224
225        static constraints = {
226                username blank: false
227                email blank: false, email: true
228                password blank: true, minSize: 8, maxSize: 64, validator: UserRegistrationController.passwordValidator
229                password2 validator: UserRegistrationController.password2Validator
230        }
231}
232
Note: See TracBrowser for help on using the repository browser.