Software Development |
Ruby on Rails
RESTful_authentication plugin
technoweenie's (aka Rick Olsen) restful_authentication provides a foundation for securely managing user authentication within altered_beast. It provides:
- Login / logout
- Secure password handling
- Account activation by validating email
- Account approval / disabling by admin
- Rudimentary hooks for authorization and access control.
altered_beast user acts_as_state_machine
acts_as_state_machine
restful_authentication plugin uses the acts_as_state_machine plugin to manage the user's states.
The affected Active Record model will act as a finite state machine (FSM).
altered_beast uses it for authentication. The code is in models/user/states.rb. The state values for user are:
- passive (start state)
- pending
- active
- suspended
- deleted
The state machine diagram shows the transitions between the states including Register, Activate, Suspend, Unsuspend and Delete.
Some of these states related to routes in for a user:
/config/routes.rb (snippet)
map.resources :users, :member => { :suspend => :put,
:settings => :get,
:make_admin => :put,
:unsuspend => :put,
:purge => :delete },
AuthenticationSystem
The AuthenticationSystem comes with the restful_authentication plugin. If you have a peek inside application_controller.rb you'll find this line:
app/controllers/application_controller.rb (snippet)
include AuthenticatedSystem
This line, being included in the ApplicationController, ensures authentication applies to the entire application. You'll find the code at lib/authenticated_system.rb.
To use the system you must specify the controllers and actions for which authentication is required. This means setting the before_filters called login_required and admin_required.
app/controllers/application_controller.rb (snippet)
before_filter :login_required, :only => [:new, :edit, :create, :update, :destroy]
app/controllers/forums_controller.rb (snippet)
before_filter :admin_required, :except => [:index, :show]
Is current_user = false a problem?
Wilson (cited in Fernandez, 2008, p. 500-501) is critical of technoweenie's approach to current_user. The objection is that current_user is set to false when no user is logged in which means you must test if logged_in? to stop non-authenticated users from blowing up the system.
Here are the getter and setter methods installed by the restful_authentication plugin:
lib/authenticated_system.rb (snippet)
# Accesses the current user from the session.
# Future calls avoid the database because nil is not equal to false.
def current_user
@current_user ||= (login_from_session || login_from_basic_auth ||
login_from_cookie) unless @current_user == false
end
# Store the given user id in the session.
def current_user=(new_user)
session[:user_id] = new_user ? new_user.id : nil
@current_user = new_user || false
end
Notice that the setter ends with the statement @current_user = new_user || false. This ensures that current_user will be false if nobody is logged in. Because of this you get statements like this throughout the system:
def admin? logged_in? && current_user.admin? end
Wilson suggests overriding current_user in the ApplicationController to give current_user a GuestUser object rather than false. You then have the choice to mimic the interface to a real user or raise a LoginRequiredError to signal to the AuthenticationSystem that a login is required for access.
TODO
- Decide what to do about current_user
References
Fernandez, O. (2008). The Rails Way. NJ: Addison-Wesley.
