and can easily grow into a complex mess of tangled if statements and what not.
.
The requirement stated that the three roles had to be implemented, Admin, Privileged User and Regular User, and a user can
only be assigned one role.
Looking at the simplest thing that could possibly work, I started of by adding a field to the users table containing an integer
value:
> ./script/generate migration add_role_type_to_users role_type:integer> rake db:migrate
However, throwing integers around in the code everywhere isn't much fun and this is where a AppConfig file comes in handy
(see my previous post for more information):
development: admin: 1 privileged_user: 2 regular_user: 3 ...
Now instead of asking whether a user has a
role_type
of 3
, we can ask it if it has a role_type
of AppConfig.reqular_user
. But that's still not quite good enough though, since we still have to ask wether
current_user.role_type ==
AppConfig.regular_user
, a nicer api would be to ask the current user object directly, as incurrent_user.regular_user?
Well, that's easy enough, we just have to add the following methods to
app/models/user.rb
:
def admin? role_type == AppConfig.adminend
def privileged_user? role_type == AppConfig.privileged_userend
def regular_user? role_type = AppConfig.regular_userend
Now since we don't want a bunch of if statements in our views, it's a good idea to add the following methods to our
app/helpers/application_helper.rb
def admin_area(&block) if current_user if current_user.admin? concat content_tag(:span, capture(&block), :class => "admin_area") end endend
def privileged_user_area(&block) if current_user if current_user.admin? || current_user.privileged_user? concat content_tag(:span, capture(&block), :class => "privileged_user_area") end endend
def regular_user_area(&block) if current_user concat content_tag(:span, capture(&block), :class => "regular_user_area") endend
Now you can restrict your views like so:
<html> <body> <% admin_area do %> <p>Content which only an admin can see</p> <% end %>
<% privileged_user_area do %> <p>Content which only an admin and a privileged user can see</p> <% end %>
<% regular_user_area do %> <p>Content which all logged in users can see</p> <% end %> </body></html>
But only restricting access to certain parts of your views is hardly enough, more likely you want to restrict access to certain
controller actions.
So lets start by creating the file
config/initializers/authorizer.rb
and paste the following snippet in there:
module Authorizer def self.included(controller) controller.extend ClassMethods end
def authorized?(*roles) if current_user unless valid_user?(*roles) session[:user_id] = nil login_required end else login_required end end
def valid_user?(*roles) statement = returning [] do |s| roles.each { |role| s << current_user.send("#{role}?") } end.join(" || ")
return eval(statement) end
module ClassMethods def ensure_role(*args) actions, roles = args.extract_options!, args before_filter(actions) { |c| c.authorized? *roles } end endend
ActionController::Base.send(:include, Authorizer)
This creates a before filter available to all controllers:
class FirstController < ApplicationController
ensure_role :admin, :privileged_user, :regular_user, :except => [ :index, :show ] ...end
class SecondController < ApplicationController ensure_role :admin, :privileged_user, :regular_user, :only => [ :create, :update ] ...end
That's it for now, I might revisit it later and clean it up a bit and maybe turn it into a plugin.