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