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: work@osx.eu $ |
---|
13 | * $Date: 2011-04-06 09:36:21 +0000 (wo, 06 apr 2011) $ |
---|
14 | */ |
---|
15 | package dbnp.authentication |
---|
16 | import grails.plugins.springsecurity.Secured |
---|
17 | import org.codehaus.groovy.grails.commons.GrailsApplication |
---|
18 | import grails.util.GrailsUtil |
---|
19 | |
---|
20 | class 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 | |
---|
304 | class 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 | |
---|
320 | class 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 | } |
---|