Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@zmajstor
Last active August 29, 2015 14:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zmajstor/c501a27e63685901e0a3 to your computer and use it in GitHub Desktop.
Save zmajstor/c501a27e63685901e0a3 to your computer and use it in GitHub Desktop.
simple role-based authorization by current controller#action

##Problems with role-based authorizations

The CanCan way

e.g. gem 'access-granted' https://github.com/chaps-io/access-granted

role :admin, { is_admin: true } do
  can :destroy, Post
end

role :member do
  can :read, Post
  can :create, Post
end

####Authorizing controller actions

class PostsController
  def show
    @post = Post.find(params[:id])
    authorize! :read, @post
  end

  def create
    authorize! :create, Post
    # (...)
  end
end

we have to define authorize! for each controller action; in case of many controllers, it could be overwhelming, e.g. 42 controllers -> 312 methods to define authorize!


alternative approach needed!

like this:

  1. group controllers in directories (folders), like: admin/, super_admin/ ...
  2. define authorization rules based on User role methods: e.g. super_admin? admin? auditor? tech? (role methods returns boolean value)
  3. check for user authorization in controllers or in the ApplicationController
class AccessValidor
def initialize(params:, user:)
@params = params
@user_rules = user.assigned_rules_by_roles
end
# authorized_rules is array of authorization rules (selected by the controller and action)
def authorized_rules
@authorized_rules ||= Array.new.tap do |result|
# check for authorization rule if controller is in the controllers folder, e.g. "admin/"
result += rules_with_current_action(user_rules_by_controller_group)
# and those authorized by controller
result += rules_with_current_action(user_rules_by_controller)
end
end
private
def user_rules_by_controller_group
return [] if controller_group.blank?
@user_rules.select{ |a| (a[:controller_group] == :any) || (a[:controller_group] == controller_group) }
end
def user_rules_by_controller
return [] if controller.blank?
@user_rules.select{ |a| a[:controller] == controller }
end
def controller_group
controller[/\A\w*\//] # this is top folder name, e.g. "admin/", (nil otherwise)
end
def controller
@params["controller"] # e.g. "admin/devices", or "home", etc.
end
def action
@params["action"] # e.g. "index", "show", "custom_action", etc.
end
def rules_with_current_action(rules)
rules.select{ |a| (a[:actions] == :any) || a[:actions].include?(action) }
end
end
class ApplicationController < ActionController::Base
def authorize_user!
render_unauthenticated unless current_user
render_unauthorized unless authorized_for_action?
end
def authorized_for_action?
@access_validor ||= AccessValidor.new(params: params, user: current_user)
@access_validor.authorized_rules.any?
end
end
class User
def assigned_rules_by_roles
@assigned_rules_by_roles ||= AccessPolicy.new(self).assigned_rules_by_roles
end
AccessPolicy = Struct.new(:user) do
def assigned_rules_by_roles # set of authorization rules (assigned by user roles)
Set.new.tap do |set|
set << { controller: "sessions", actions: :any }
set << { controller: "enroll", actions: :any }
set << { controller: "home", actions: :any }
if @user.super_admin?
set << { controller_group: :any, actions: :any }
end
if @user.admin?
set << { controller_group: "admin/", actions: :any}
end
if @user.auditor?
set << { controller_group: "admin/", actions: %w(index show) }
end
if @user.tech?
set << { controller_group: "admin/", actions: %w(index show) }
set << { controller: "admin/settings", actions: %w(edit update) }
set << { controller: "admin/devices", actions: %w(push edit_note update_note) }
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment