MTJ Hax

Administrating restful-authentication users with ActiveScaffold

Posted in ruby on rails by mtjhax on January 16, 2010

Needing a quick set of admin pages for a site that uses Restful-authentication, I decided to use one of the many nifty scaffolding tools for Ruby on Rails to generate some customizable views. I settled on ActiveScaffold for reasons I will mention later.

The normal way you use ActiveScaffold is to simply add an ‘active_scaffold’ declaration to your controller that causes views to be dynamically generated. For example:

class UsersController < ApplicationController
  active_scaffold
end

The problem is that this code adds actions such as show, list, and create to your controller that are almost certainly already in use by your user-facing web site. The obvious answer is to create a separate controller and configure ActiveScaffold to  specify which model to use. Easy enough, but there are a couple useful tricks worth considering.

Why ActiveScaffold?

ActiveScaffold caught my eye for a number of reasons including the fact that their main page has links directly to their competition in case “ActiveScaffold isn’t what you’re looking for”. I’m not sure if that’s a friendly open-source attitude or a bit of a boast–maybe a little of both–it’s all good either way. Hobo and Streamlined both looked really cool but were a bigger investment in learning curve than I cared to make that afternoon. Lightweight is always good when patching stuff on to an existing site and refactoring isn’t in the budget.

An interesting feature of ActiveScaffold is that the views are dynamically generated and customized through configuration options, helpers, and CSS (as opposed to code being emitted and then customized directly). There seems to be a minor movement in some Rails circles away from code generators towards configurable, customizable plugins/gems. Another example is authlogic, the hot new flavor for user authentication in Rails. This all seems a little contrary to Rails doctrine (convention over configuration) but the configuration is lightweight and it reduces the amount of cruft I have to add to my code that is tangential to my application’s purpose. For a plugin that makes a ton of sense.

Approaches

One interesting approach to make a user admin interface is to use the map.namespace feature in routes.rb. With this you can create restful routes such as /admin_users, /new_admin_users, /delete_admin_users, etc. Something like this:

map.namespace :admin do |admin|
    admin.resources :users
end

Then you can create new admin controllers and views in app/controllers/admin, app/views/admin/users, etc. This is a great pattern but it’s a little tricky to implement with something like ActiveScaffold that is automatically generating your views.

The simpler approach with ActiveScaffold is to simply create a new controller, say AdminUsersController, and specify the model name in your active_scaffold configuration:

active_scaffold :<your_model_name>

There’s only one hitch. Everyone in Rails these days is pushing the “thin controller, fat model” doctrine. What happens when you need special logic only available to administrators? Obviously you can add to your controller, but you might not be able to access everything in your model to provide this special behavior without opening some potential security holes. The best way for me turned out to be subclassing the User model as well as creating a new controller.

Implementation

In my case, I wanted to automatically activate users that are created by the admin without sending activation emails.

Step 1, subclass your normal Users model in models/user.rb:

# model subclass only used by admin backend
class AdminViewUser < User
  # override method instead of using callback macro to prevent super from being called
  def before_create
    auto_activate
  end
  # (optional) skip post-activation email for users created by admin
  def recently_activated?
    false
  end
protected
  def auto_activate
    @created = false  # (optional) skip sending signup email for users created by admin
    self.activated_at = Time.now.utc
    self.activation_code = nil
  end
end

Step 2, modify models/user_observer.rb to checks for automatic activation before sending out emails (optional):

class UserObserver < ActiveRecord::Observer
  def after_create(user)
    UserMailer.deliver_signup_notification(user) if user.recently_created?
  end
  def after_save(user)
    UserMailer.deliver_activation(user) if user.recently_activated?
  end
end

Step 3, add a new controller that uses ActiveScaffold, restricted to admin users only, in controllers/admin_view_user_controller.rb:

class AdminViewUserController < ApplicationController
  before_filter :admin_required  # see authenticated_system.rb below
  active_scaffold do |config|
    config.label = "My Fancy User Admin Page"
    config.columns = [:login, :email, :password, :password_confirmation, :created_at, :updated_at]
    # exclude some columns from specific views, for more config options see ActiveScaffold web site
    list.columns.exclude :password, :password_confirmation
    show.columns.exclude :password, :password_confirmation
    update.columns.exclude :created_at, :updated_at
    create.columns.exclude :created_at, :updated_at
  end
end

Final step, implement the :admin_required method. I added a column called ‘is_admin’ to my users table and created some new methods in lib/authenticated_system.rb:

module AuthenticatedSystem
  protected
  def is_admin?
    logged_in? && current_user.is_admin?
  end
  def admin_required
    is_admin? || access_denied
  end
end

This is just an example to get you started. There are probably security enhancements that could be made and, obviously, you can administrate models other than just User. In my version I also added a UserGroup model that is also administrated by ActiveScaffold.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: