Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 481
- Log:
Add support for return-to URL by explicit session key and
make sure that white space in e-mail addresses is stripped.
- Author:
- rool
- Date:
- Sun May 08 22:51:59 +0100 2022
- Size:
- 20199 Bytes
1 | ####################################################################### |
2 | # File: account_controller.rb # |
3 | # (C) Hipposoft 2006-2011 # |
4 | # # |
5 | # Purpose: Hub account management. # |
6 | # # |
7 | # Author: A.D.Hodgkinson # |
8 | # # |
9 | # History: 31-Jan-2011 (ADH): Comment header added; prior history # |
10 | # not recorded. # |
11 | ####################################################################### |
12 | |
13 | class AccountController < ApplicationController |
14 | |
15 | layout 'default.html.erb' |
16 | |
17 | # Cache the logged in and out PNG images in RAM; they're only small. |
18 | |
19 | @@logged_in_image = File.read("#{RAILS_ROOT}/public/images/icons/logged_in.png") |
20 | @@logged_out_image = File.read("#{RAILS_ROOT}/public/images/icons/logged_out.png") |
21 | |
22 | # Action permissions for this class as a class variable, exposed |
23 | # to the public through a class method. |
24 | |
25 | @@hubssolib_permissions = HubSsoLib::Permissions.new({ |
26 | :change_password => [ :admin, :webmaster, :privileged, :normal ], |
27 | :change_details => [ :admin, :webmaster, :privileged, :normal ], |
28 | :delete => [ :admin, :webmaster, :privileged, :normal ], |
29 | :delete_confirm => [ :admin, :webmaster, :privileged, :normal ], |
30 | :list => [ :admin, :webmaster, :privileged ], |
31 | :enumerate => [ :admin, :webmaster ], |
32 | :show => [ :admin, :webmaster ], |
33 | :edit_roles => [ :admin ], |
34 | :destroy => [ :admin ] |
35 | }) |
36 | |
37 | def AccountController.hubssolib_permissions |
38 | @@hubssolib_permissions |
39 | end |
40 | |
41 | # HTTPS enforcement for all methods, except the login indicator; if someone |
42 | # is on an HTTP page, the login indicator needs to be fetched by HTTP too so |
43 | # it can show "logged out" as the secure-only cookies won't get sent. It is |
44 | # very confusing to be on an HTTP page, apparently fetching the indicator by |
45 | # HTTP, only to have the image fetch quietly redirect behind the scenes, go |
46 | # to HTTPS, and say you're logged in - when everyone else thinks you're not. |
47 | |
48 | before_filter :hubssolib_ensure_https, :except => :login_indication |
49 | |
50 | # The "proper" login method |
51 | # |
52 | def login |
53 | @title = 'Log in' |
54 | return_to_url = hubssolib_get_return_to() || session[:return_to_url] |
55 | |
56 | session.delete(:return_to_url) |
57 | |
58 | # GET methods just show the login screen. We dump all known application |
59 | # cookies at this point, since they can be stale and logins might not be |
60 | # recognised within those applications otherwise. |
61 | # |
62 | unless request.post? |
63 | cookies.delete( '_canvass_session' ) |
64 | cookies.delete( '_radiantapp_session_id' ) |
65 | cookies.delete( '_instikiapp_session_id' ) |
66 | cookies.delete( 'beastapp_session_id' ) |
67 | cookies.delete( 'typoapp_session_id' ) |
68 | cookies.delete( 'gulleryapp_session_id' ) |
69 | cookies.delete( 'collaboaapp_session_id' ) |
70 | cookies.delete( 'rcvswebapp_session_id' ) |
71 | cookies.delete( 'hubapp_shared_id' ) |
72 | cookies.delete( '_hub_session' ) |
73 | |
74 | session[:return_to_url] = return_to_url |
75 | return |
76 | end |
77 | |
78 | @email = params[:email] |
79 | @email = @email.strip() if @email.present? |
80 | |
81 | self.hubssolib_current_user = from_real_user(User.authenticate(@email, params[:password])) |
82 | |
83 | if (self.hubssolib_current_user and self.hubssolib_current_user != :false) |
84 | hubssolib_set_last_used(Time.now.utc) |
85 | |
86 | privileges = hubssolib_get_user_roles.to_human_s.downcase |
87 | hubssolib_set_flash( |
88 | :notice, |
89 | "Logged in successfully. Welcome, #{hubssolib_get_user_name}. " << |
90 | "You have #{privileges} privileges." |
91 | ) |
92 | |
93 | if return_to_url.present? |
94 | redirect_to(return_to_url) |
95 | else |
96 | redirect_to(:controller => 'tasks', :action => nil) |
97 | end |
98 | |
99 | else |
100 | hubssolib_set_flash(:alert, 'Incorrect e-mail address or password.') |
101 | |
102 | end |
103 | end |
104 | |
105 | # Log out the user and redirect to the Tasks controller. |
106 | # |
107 | def logout |
108 | @title = 'Log out' |
109 | hubssolib_log_out() |
110 | hubssolib_set_flash(:attention, 'You are now logged out.') |
111 | redirect_to :controller => 'tasks', :action => nil |
112 | end |
113 | |
114 | def signup |
115 | @title = 'Sign up' |
116 | return unless request.post? |
117 | |
118 | # Bulk assignment from the params hash is safe because the User object |
119 | # contains nothing that won't be overwritten anyway or isn't already |
120 | # protected by attr_accessible in the User model. |
121 | |
122 | @user = User.new(params[:user]) |
123 | @user.email = @user.email.strip() if @user.email.present? |
124 | |
125 | if ( @user.email.downcase.ends_with?( '.kr' ) || @user.email.downcase.ends_with?( '.cn' ) ) |
126 | hubssolib_set_flash(:attention, 'Due to overwhelming spam volumes from some locations, self-signups for those locations are blocked. Please contact ROOL for assistence.') |
127 | redirect_to :controller => 'tasks', :action => nil |
128 | return |
129 | end |
130 | |
131 | # Are there any users yet? If not, grant this user admin permissions. |
132 | # Administrators are for just this application; whether or not admin |
133 | # privileges affect other applications depends on the level of external |
134 | # SSO integration. |
135 | |
136 | if (User.count.zero?) |
137 | |
138 | @user.roles = HubSsoLib::Roles.new(true).to_s |
139 | @user.save! |
140 | @user.activate |
141 | self.hubssolib_current_user = from_real_user(@user) |
142 | |
143 | hubssolib_set_flash( |
144 | :notice, |
145 | 'Thanks for signing up. You are now the system administrator ' << |
146 | 'and your account has been automatically activated.' |
147 | ) |
148 | |
149 | else |
150 | |
151 | # Have to do the captcha verification explicitly before trying to save |
152 | # the model, as verification is not done as part of validation and we |
153 | # don't want to successfully save a model only to find that the captcha |
154 | # text is incorrect. |
155 | |
156 | begin |
157 | success = verify_recaptcha( |
158 | :model => @user, |
159 | :message => "The reCaptcha challenge wasn't happy with the response. Please try again or contact RISC OS Open for assistance." |
160 | ) |
161 | rescue => e |
162 | Rails.logger.error(e.inspect) |
163 | hubssolib_set_flash(:attention, 'The reCaptcha system is currently not available and cannot verify signups. Please try again later.') |
164 | redirect_to :controller => 'tasks', :action => nil |
165 | return |
166 | end |
167 | |
168 | raise ActiveRecord::RecordInvalid.new(@user) if not success |
169 | |
170 | @user.roles = HubSsoLib::Roles.new(false).to_s |
171 | @user.save! |
172 | |
173 | hubssolib_set_flash( |
174 | :notice, |
175 | 'Thanks for signing up. Your site account must be activated ' << |
176 | 'before you can use it - please check your e-mail account ' << |
177 | 'for a message which tells you what you should do next.' |
178 | ) |
179 | |
180 | end |
181 | |
182 | redirect_to :controller => 'tasks', :action => nil |
183 | |
184 | rescue ActiveRecord::RecordInvalid |
185 | render :action => 'signup' |
186 | end |
187 | |
188 | def activate |
189 | activation_code = params[:activation_code] || params[:id] |
190 | |
191 | unless activation_code.nil? |
192 | @user = User.find_by_activation_code(activation_code) |
193 | |
194 | if @user and @user.activate |
195 | self.hubssolib_current_user = from_real_user(@user) |
196 | |
197 | hubssolib_set_flash( |
198 | :notice, |
199 | "Your #{INSTITUTION_NAME_LONG} web site account is now active." |
200 | ) |
201 | |
202 | hubssolib_redirect_back_or_default(:controller => 'tasks', :action => nil) |
203 | else |
204 | hubssolib_set_flash( |
205 | :alert, |
206 | "Unable to activate your #{INSTITUTION_NAME_LONG} web site account. " << |
207 | 'Is the activation code correct, or has it already been used? ' << |
208 | "If in doubt please try to sign up again. Contact #{INSTITUTION_NAME_SHORT} if you " << |
209 | 'keep having trouble.' |
210 | ) |
211 | |
212 | redirect_to :controller => 'account', :action => 'signup' |
213 | end |
214 | else |
215 | redirect_to :controller => 'account', :action => 'signup' |
216 | end |
217 | end |
218 | |
219 | def change_password |
220 | @title = 'Change password' |
221 | return unless request.post? |
222 | |
223 | user = to_real_user(self.hubssolib_current_user) |
224 | |
225 | if User.authenticate(user.email, params[:old_password]) |
226 | if (params[:password] == params[:password_confirmation]) |
227 | user.password_confirmation = params[:password_confirmation] |
228 | user.password = params[:password] |
229 | save_password_and_set_flash(user) |
230 | self.hubssolib_current_user = from_real_user(user) |
231 | |
232 | redirect_to :controller => 'tasks', :action => nil |
233 | else |
234 | set_password_mismatch_flash |
235 | @old_password = params[:old_password] |
236 | end |
237 | else |
238 | hubssolib_set_flash(:alert, 'Incorrect current password.') |
239 | end |
240 | end |
241 | |
242 | def change_details |
243 | @title = 'Update account details' |
244 | @user = to_real_user(self.hubssolib_current_user) |
245 | @real_name = @user ? @user.real_name || '' : '' |
246 | |
247 | return unless request.post? |
248 | |
249 | if (params[:real_name]) |
250 | @user.real_name = @real_name = params[:real_name] |
251 | @user.save! |
252 | self.hubssolib_current_user = from_real_user(@user) |
253 | |
254 | hubssolib_set_flash(:notice, 'Account details updated successfully.') |
255 | redirect_to :controller => 'tasks', :action => nil |
256 | end |
257 | end |
258 | |
259 | def forgot_password |
260 | @title = 'Forgotten password' |
261 | return unless request.post? |
262 | |
263 | @user = User.find(:first, :conditions => ["LOWER(email) = ?", params[:email].downcase]) |
264 | |
265 | unless @user.nil? |
266 | @user.forgot_password |
267 | @user.save! |
268 | |
269 | hubssolib_set_flash( |
270 | :notice, |
271 | 'An e-mail message which tells you how to reset your ' << |
272 | 'account password has been set to your e-mail address.' |
273 | ) |
274 | |
275 | redirect_to :controller => 'tasks', :action => nil |
276 | else |
277 | hubssolib_set_flash( |
278 | :alert, |
279 | 'No account was found for the given e-mail address.' |
280 | ) |
281 | end |
282 | end |
283 | |
284 | def reset_password |
285 | @title = 'Reset password' |
286 | |
287 | if params[:id].nil? |
288 | hubssolib_redirect_back_or_default(:controller => 'tasks', :action => nil) |
289 | return |
290 | end |
291 | |
292 | @user = User.find_by_password_reset_code(params[:id]) |
293 | |
294 | if (@user.nil?) |
295 | hubssolib_set_flash( |
296 | :alert, |
297 | 'Invalid reset code. Did your e-mail client break up the reset ' << |
298 | 'link so it spanned more than one line? If so, please try again, ' << |
299 | 'copying all of the link in the message however many lines it spans.' |
300 | ) |
301 | |
302 | hubssolib_redirect_back_or_default(:controller => 'tasks', :action => nil) |
303 | return |
304 | end |
305 | |
306 | t = Time.now.utc |
307 | if (t >= (@user.password_reset_code_expires_at || t)) # Allows for 'nil' in expiry field |
308 | hubssolib_set_flash( |
309 | :alert, |
310 | 'The reset code has expired. Please try your reset request again.' |
311 | ) |
312 | redirect_to :controller => 'account', :action => 'forgot_password' |
313 | return |
314 | end |
315 | |
316 | unless params[:password] |
317 | hubssolib_set_flash(:alert, 'Reset your password using the form below.') |
318 | return |
319 | end |
320 | |
321 | if (params[:password] == params[:password_confirmation]) |
322 | @user.password_confirmation = params[:password_confirmation] |
323 | @user.password = params[:password] |
324 | @user.reset_password |
325 | save_password_and_set_flash(@user) |
326 | self.hubssolib_current_user = from_real_user(@user) |
327 | redirect_to :controller => 'tasks', :action => nil |
328 | return |
329 | else |
330 | set_password_mismatch_flash |
331 | return |
332 | end |
333 | end |
334 | |
335 | def delete |
336 | hubssolib_set_flash(:alert, 'Are you sure?') |
337 | title = 'Delete account: Are you sure?' |
338 | end |
339 | |
340 | def delete_confirm |
341 | me = to_real_user(self.hubssolib_current_user) |
342 | hubssolib_log_out() |
343 | me.destroy |
344 | |
345 | hubssolib_clear_flash() |
346 | hubssolib_set_flash(:attention, 'Your account has been deleted.') |
347 | redirect_to :controller => 'tasks', :action => nil |
348 | end |
349 | |
350 | def list |
351 | @title = 'List of user accounts' |
352 | |
353 | # Page zero means 'all'. |
354 | |
355 | if (params.has_key?(:page) && params[:page] == '0' ) |
356 | page = 1 |
357 | per_page = User.count |
358 | else |
359 | page = params[:page] |
360 | per_page = 20 |
361 | end |
362 | |
363 | @users = User.paginate :page => page, |
364 | :per_page => per_page, |
365 | :order => 'created_at DESC' |
366 | end |
367 | |
368 | # Enumerate active users (those users known to the DRb server). |
369 | # |
370 | def enumerate |
371 | @title = 'Active users' |
372 | @users = hubssolib_enumerate_users |
373 | @users = [] if @users.nil? |
374 | |
375 | # Map the user objects returned from the HubSsoLib Gem to |
376 | # internal users. |
377 | |
378 | @users.map! { |user| to_real_user(user) } |
379 | |
380 | # Page number zero is magic; it indicates "all items". |
381 | |
382 | if (params.has_key?(:page) && params[:page] == '0' ) |
383 | page = 1 |
384 | per_page = @users.count |
385 | else |
386 | page = params[:page] |
387 | per_page = 20 |
388 | end |
389 | |
390 | @users = @users.paginate :page => page, |
391 | :per_page => per_page, |
392 | :order => 'created_at DESC' |
393 | end |
394 | |
395 | # Show details of a specific user account. |
396 | # |
397 | def show |
398 | @title = 'User account details' |
399 | @user = User.find(params[:id]) |
400 | @referrer = request.env["HTTP_REFERER"] |
401 | @referrer = nil unless (@referrer && !@referrer.empty?) |
402 | end |
403 | |
404 | def edit_roles |
405 | @title = 'Edit account roles' |
406 | |
407 | # We must have a valid ID |
408 | |
409 | unless (request.post?) and (params[:id]) and (@user = User.find(params[:id])) |
410 | redirect_to :controller => 'tasks', :action => nil |
411 | return |
412 | end |
413 | |
414 | # If 'commit' is present, the form was submitted with details rather than |
415 | # visited from a list or account details view. |
416 | |
417 | return unless (params[:commit]) |
418 | |
419 | # Validate the result |
420 | |
421 | roles = (params[:user] ? params[:user][:roles] : '').to_authenticated_roles |
422 | |
423 | unless (roles.validate) |
424 | hubssolib_set_flash( |
425 | :alert, |
426 | 'Invalid roles chosen. ' << |
427 | 'At least one item in the list must be selected.' |
428 | ) |
429 | else |
430 | @user.roles = roles.to_s |
431 | @user.save! |
432 | |
433 | # Did I update my own roles? |
434 | |
435 | if (hubssolib_get_user_id == @user.id) |
436 | self.hubssolib_current_user = from_real_user(@user) |
437 | end |
438 | |
439 | hubssolib_set_flash(:notice, 'Account roles updated successfully.') |
440 | redirect_to :action => 'show', :id => @user.id |
441 | end |
442 | end |
443 | |
444 | def destroy |
445 | user = User.find(params[:id]) |
446 | |
447 | if (hubssolib_get_user_id == user.id) |
448 | hubssolib_set_flash( |
449 | :alert, |
450 | 'Please use the normal control panel below to delete your own account.' |
451 | ) |
452 | redirect_to root_path |
453 | return |
454 | elsif (user.roles.to_authenticated_roles.include?(:admin)) |
455 | hubssolib_set_flash( |
456 | :alert, |
457 | 'You cannot destroy an administrator account from here! ' << |
458 | 'You can only do that at the control panel when ' << |
459 | 'logged into the account, or at the database level.' |
460 | ) |
461 | else |
462 | user.destroy |
463 | hubssolib_set_flash(:alert, 'The account has been deleted.') |
464 | end |
465 | |
466 | redirect_to :action => 'list' |
467 | end |
468 | |
469 | # The login_indication method is unusual; it returns data for an image, |
470 | # with no-cache parameters set, to indicate whether or not the user is |
471 | # logged in. It does not render a view. |
472 | # |
473 | # The idea is that a caller which caches HTML can include an image tag |
474 | # that points its source data to this method; the image will be updated |
475 | # even if the HTML stays cached. |
476 | # |
477 | def login_indication |
478 | headers['Pragma'] = 'no-cache' |
479 | headers['Cache-Control'] = 'no-cache, must-revalidate' |
480 | |
481 | send_data hubssolib_logged_in? ? @@logged_in_image : @@logged_out_image, |
482 | :type => 'image/png', |
483 | :disposition => 'inline' |
484 | end |
485 | |
486 | # A supporting unusual method is login_conditional, which redirects to |
487 | # the login page if the user is logged out or the tasks page if the user |
488 | # is logged in. It explicitly clears a return-to link, if there is one, |
489 | # so that the user doesn't drop out of Hub. This is useful if the page |
490 | # from which the user came cannot support (for example) the Flash display |
491 | # because of, say, caching. |
492 | # |
493 | def login_conditional |
494 | if (hubssolib_ensure_https) # Redirect back to here using HTTPS, if not already |
495 | hubssolib_store_location(nil) |
496 | |
497 | if (hubssolib_logged_in?) |
498 | redirect_to :controller => 'tasks', :action => nil |
499 | else |
500 | redirect_to :action => 'login' |
501 | end |
502 | end |
503 | end |
504 | |
505 | protected |
506 | |
507 | # Pass a HubSsoLib::User object. Returns an equivalent User Model object. |
508 | # |
509 | def to_real_user(user) |
510 | return nil if user.nil? |
511 | raise 'Incorrect argument class' unless (user.class == HubSsoLib::User or user.class == DRbObject) |
512 | |
513 | # Unpleasant "user_" prefix in HubSsoLib::User field names is to avoid |
514 | # collisions (e.g. of "id") with DRbObject. |
515 | |
516 | real_user = User.find(user.user_id) |
517 | raise 'No equivalent real user' unless real_user |
518 | |
519 | real_user.activated_at = Time.zone.parse(user.user_activated_at) |
520 | real_user.activation_code = user.user_activation_code |
521 | real_user.created_at = Time.zone.parse(user.user_created_at) |
522 | real_user.crypted_password = user.user_crypted_password |
523 | real_user.email = user.user_email |
524 | real_user.member_id = user.user_member_id |
525 | real_user.password_reset_code = user.user_password_reset_code |
526 | real_user.password_reset_code_expires_at = Time.zone.parse(user.user_password_reset_code_expires_at) |
527 | real_user.real_name = user.user_real_name |
528 | real_user.remember_token = user.user_remember_token |
529 | real_user.remember_token_expires_at = Time.zone.parse(user.user_remember_token_expires_at) |
530 | real_user.roles = user.user_roles |
531 | real_user.salt = user.user_salt |
532 | real_user.updated_at = Time.zone.parse(user.user_updated_at) |
533 | |
534 | return real_user |
535 | end |
536 | |
537 | # Pass a User Model object. Returns an equivalent HubSsoLib::User object. |
538 | # |
539 | def from_real_user(real_user) |
540 | return nil if real_user.nil? |
541 | raise 'Incorrect argument class' unless real_user.class == User |
542 | |
543 | user = HubSsoLib::User.new |
544 | |
545 | user.user_activated_at = real_user.activated_at.to_s |
546 | user.user_activation_code = real_user.activation_code |
547 | user.user_created_at = real_user.created_at.to_s |
548 | user.user_crypted_password = real_user.crypted_password |
549 | user.user_email = real_user.email |
550 | user.user_id = real_user.id |
551 | user.user_member_id = real_user.member_id |
552 | user.user_password_reset_code = real_user.password_reset_code |
553 | user.user_password_reset_code_expires_at = real_user.password_reset_code_expires_at.to_s |
554 | user.user_real_name = real_user.real_name |
555 | user.user_remember_token = real_user.remember_token |
556 | user.user_remember_token_expires_at = real_user.remember_token_expires_at.to_s |
557 | user.user_roles = real_user.roles |
558 | user.user_salt = real_user.salt |
559 | user.user_updated_at = real_user.updated_at.to_s |
560 | |
561 | return user |
562 | end |
563 | |
564 | def save_password_and_set_flash(user) |
565 | if ( user.save ) |
566 | hubssolib_set_flash(:notice, 'Your password has been changed.') |
567 | else |
568 | hubssolib_set_flash(:alert, 'Sorry, your password could not be changed.') |
569 | end |
570 | end |
571 | |
572 | def set_password_mismatch_flash |
573 | hubssolib_set_flash( |
574 | :alert, |
575 | 'The new password differed from the password confirmation you entered.' |
576 | ) |
577 | end |
578 | end |