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