Skip to content

Assign multiple roles to any User or other ActiveRecord object. Select only the appropriate objects based on intelligent, chainable ActiveRecord::Relation finder methods.

License

Notifications You must be signed in to change notification settings

OliverHKU/effective_roles

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Effective Roles

Assign multiple roles to any User or other ActiveRecord object. Select only the appropriate objects based on intelligent, chainable ActiveRecord::Relation finder methods.

Implements multi-role authorization based on an integer roles_mask field

Includes a mixin for adding authentication for any model.

SQL Finders for returning an ActiveRecord::Relation with all permitted records.

Handy formtastic and simple_form helpers for assigning roles.

Rails 3.2.x and Rails 4

Getting Started

Add to Gemfile:

gem 'effective_roles'

Run the bundle command to install it:

bundle install

Install the configuration file:

rails generate effective_roles:install

The generator will install an initializer which describes all configuration options.

Usage

Add the mixin to an existing model:

class Post
  acts_as_role_restricted
end

Then create a migration to add the :roles_mask column to the model.

rails generate migration add_roles_to_post roles_mask:integer

which will create a migration something like

class AddRolesToPost < ActiveRecord::Migration
  def change
    add_column :posts, :roles_mask, :integer
  end
end

Strong Parameters

Make your controller aware of the acts_as_role_restricted passed parameters:

def permitted_params
  params.require(:base_object).permit(:roles => [])
end

Usage

Defining Roles

All roles are defined in the config/effective_roles.rb initializer. The roles are defined once and may be applied to any acts_as_role_restricted model in the application.

Model

Assign roles:

post.roles = [:admin, :superamdin]
post.save

See if an object has been assigned a specific role:

post.is_role_restricted?
=> true

post.is?(:admin)
=> true

post.is_any?(:editor, :superadmin)
=> true

post.roles
=> [:admin, :superadmin]

Compare against another acts_as_role_restricted object:

post = Post.new()
post.roles = [:admin]

user = User.new()
user.roles = []

post.roles_permit?(user)
=> false  # Post requires the :admin role, but User has no admin role
post.roles = [:superadmin]
user.roles = [:admin]

post.roles_permit?(user)
=> false  # User does not have the superadmin role
post.roles = [:admin]
user.roles = [:superadmin, :admin]

post.roles_permit?(user)
=> true  # User has required :admin role

Finder Methods

Find all objects that have been assigned a specific role (or roles). Will not return posts that have no assigned roles (roles_mask = 0 or NULL)

Post.with_role(:admin, :superadmin)   # Can pass as an array if you want
Post.with_role(current_user.roles)

Find all objects that are appropriate for a specific role. Will return posts that have no assigned roles

Post.for_role(:admin)
Post.for_role(current_user.roles)

These are both ActiveRecord::Relations, so you can chain them with other methods like normal.

Assignable Roles

Specifies which roles can be assigned to a resource by a specific user.

See the initializers/effective_roles.rb for more information.

  config.assignable_roles = {
    :superadmin => [:superadmin, :admin, :member], # Superadmins may assign any resource any role
    :admin => [:admin, :member],                   # Admins may only assign the :admin or :member role
    :member => []                                  # Members may not assign any roles
  }

When used in a Form Helper (see below), only the appropriate roles will be displayed.

However, this restriction is not enforced on the controller level, so someone could inspect & re-write the form parameters and still assign a role that they are not allowed to.

To prevent this, add something like the following code to your controller:

before_filter :only => [:create, :update] do
  if params[:user] && params[:user][:roles]
    params[:user][:roles] = params[:user][:roles] & EffectiveRoles.assignable_roles_for(current_user, User.new()).map(&:to_s)
  end
end

Form Helper

If you pass current_user (or any acts_as_role_restricted object) into these helpers, only the assignable_roles will be displayed.

Formtastic

semantic_form_for @user do |f|
  = effective_roles_fields(f)

or

semantic_form_for @user do |f|
  = effective_roles_fields(f, current_user)

simple_form

simple_form_for @user do |f|
  = f.input :roles, :collection => EffectiveRoles.roles_collection(f.object), :as => :check_boxes

or

simple_form_for @user do |f|
  = f.input :roles, :collection => EffectiveRoles.roles_collection(f.object, current_user), :as => :check_boxes

Summary table

Use the effective_roles_summary_table view helper to output a table of the actual permission levels for each role and ActiveRecord object combination.

You can customize the helper function with the following keys: roles, only, except, plus and additionally

effective_roles_summary_table(roles: [:admin, :superadmin], only: [Post, Event])
effective_roles_summary_table(except: [Post, User])
effective_roles_summary_table(plus: [Reports::PostReport]) # Add a non ActiveRecord object to the output, sorted with the other model names
effective_roles_summary_table(additionally: [Reports::PostReport]) # Add a non ActiveRecord object to the output, after the other models
effective_roles_summary_table(plus: {post_report: :export}) # A custom permission based on a symbol

You can override the effective_roles_authorization_label(klass) method for better control of the label display.

Bitmask Implementation

The underlying role information for any acts_as_role_restricted ActiveRecord object is stored in that object's roles_mask field.

roles_mask is an integer, in which each power of 2 represents the presence or absense of a role.

If we have the following roles defined:

EffectiveRoles.setup do |config|
  config.roles = [:superadmin, :admin, :betauser, :member]
end

Then the following will hold true:

user = User.new()

user.roles
=> []

user.roles_mask
=> 0

user.roles = [:superadmin]
user.roles_mask
=> 1

user.roles = [:admin]
user.roles_mask
=> 2

user.roles = [:betauser]
user.roles_mask
=> 4

user.roles = [:member]
user.roles_mask
=> 8

As well:

user.roles = [:superadmin, :admin]
user.roles_mask
=> 3

user.roles = [:superadmin, :betauser]
user.roles_mask
=> 5

user.roles = [:admin, :member]
user.roles_mask
=> 10

user.roles = [:superadmin, :admin, :betauser, :member]
user.roles_mask
=> 15

Keep in mind, when using this gem you should never be working directly with the roles_mask field.

All roles are get/set through the roles and roles= methods.

License

MIT License. Copyright Code and Effect Inc.

Code and Effect is the product arm of AgileStyle, an Edmonton-based shop that specializes in building custom web applications with Ruby on Rails.

Credits

This model implements the https://github.com/ryanb/cancan/wiki/Role-Based-Authorization multi role based authorization based on the roles_mask field

Testing

The test suite for this gem is unfortunately not yet complete.

Run tests by:

rake spec

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Bonus points for test coverage
  6. Create new Pull Request

About

Assign multiple roles to any User or other ActiveRecord object. Select only the appropriate objects based on intelligent, chainable ActiveRecord::Relation finder methods.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Ruby 95.3%
  • HTML 2.5%
  • JavaScript 1.2%
  • CSS 1.0%