The project is in a healthy, maintained state
Allows the developer to define policies for controllers.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

~> 7
 Project Readme

Controller Policies

Installation

Add this line to your application's Gemfile:

gem 'controller_policies', '~> 1.0'

Usage

Generating Definitions

This gem takes advantage of placing all your policy definitions for controllers in one folder (app/policies). To generate a policy, run:

rails g policy_definition my/namespace

This will generate a file: app/policies/my/namespace/definitions.rb

The developer should edit this file and add the policies for the app. It is important to note that the location of the definitions file should reflect the namespace of the associated controllers.

actions key

The actions key is an array of strings that contain a list of supported controller actions for automation of policy checking.

For example, you have this definition:

module Policies
  module Base
    DEFINITIONS = {
      code: 'policy_code',
      name: 'Policy Name',
      description: 'I am a policy.',
      actions: ['feature_app/users', 'data_app/products#index', 'subscriptions']
    }
  end
end

The above definition will have enforced policies on Base::FeatureApp::UsersController (all actions), Base::DataApp::ProductsController (index action) and Base::SubscriptionController.

Do note that the actions array implement Regular Expression Matching. That means that if you have multiple controllers with the same name on their parent namespace, the parent will be matched first. To avoid this problem, simply add the namespace to match the intended child instead.

module Base
  DEFINITIONS = {
    code: 'policy_code',
    name: 'Policy Name',
    description: 'I am a policy.',
    actions: ['feature_app/users', 'another_base/data_app/products#index', 'base/subscriptions']
  }
end

Adding Policy Enforcement in Controllers

Simply add the line has_enforced_policies, and pass a block with one argument (ability_code), or override the ability?(ability_code) method.

class MyController < ApplicationController
  has_enforced_policies do |ability_code|
    render 'unauthorized' unless current_user.abilities.include? ability_code
  end

  # ...
end
class MyController < ApplicationController
  has_enforced_policies

  def ability?(ability_code)
    render 'unauthorized' unless current_user.abilities.include? ability_code
  end
  # ...
end

It is recommended to use render or redirect_to within this block to prevent the controllers from executing the action when the ability did not exist in the data. The ability checking is done in a before_action callback, hence using render or redirect_to will stop further controller actions. This is a Rails behavior.

Since storing abilities are very flexible and there are truly infinite ways of doing it, this gem did not support that feature. Instead, the developer must define their own ability checking.

Skipping Policy Enforcement in Certain Actions

There might be an event where there is a need to skip automatic policy enforcements in certain actions. As explained above, the policy enforcement is done in a before_action callback. To skip a policy enforcement, simply use the skip_before_action :check_abilities_by_definition method from Rails. The :only and :except options are also available to filter actions.

class MyOtherController < MyController
  skip_before_action :check_abilities_by_definition, only: [:new, :edit]

  # ...
end

Ability

The Ability class is a model for abilities that come from the definition files.

Class Methods

#all

Get all abilities from all definitions.

Ability.all

#all_codes

Get all ability codes from all definitions.

Ability.all_codes

#where(*queries)

Filter abilities based on namespace. queries can be an array of Strings, Modules or Classes.

Ability.where(Policies::FeatureOne, Policies::FeatureTwo, Policies::FeatureOne::SubFeatureA)
Ability.where('/feature_one', '/feature_two', '/feature_one/sub_feature_a')

#match(expression)

Match abilities based on a matching string or regex. The matcher is based on the namespace. expression can be a Regexp or String.

Ability.match(/Policies::FeatureOne(::)?(.)*/)

Instance Methods

#code

The code of the ability.

ability.code

#name

The name of the ability.

ability.name

#description

The description of the ability.

ability.description

#actions

Controller actions that the ability can check against.

ability.actions

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/tieeeeen1994/controller_policies. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the ControllerPolicies project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.