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

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.