RailsAuthorize
Simple and flexible authorization Rails system inspired by Pundit.
Installation
Add this line to your application's Gemfile:
gem 'rails_authorize'
And then execute:
$ bundle
Or install it yourself as:
$ gem install rails_authorize
Example
# app/models/post.rb
class Post
def published?
return published == true
end
end
# app/policies/application_policy.rb
class ApplicationPolicy
attr_reader :user, :target, :context
def initialize(user, target, context)
@user = user
@target = target
@context = context
end
end
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def index?
true
end
def show?
user.is_admin? and target.published?
end
def scope
target.where(published: true)
end
def permitted_attributes
if user.admin?
%i[status body title]
else
%i[body title]
end
end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include RailsAuthorize
end
# app/controllers/posts_controller.rb
class PostController
def index
@posts = authorized_scope(Post)
...
end
def update
@post = Post.find(params[:id])
@post.update(permitted_attributes(@post))
...
end
def show
@post = Post.find(params[:id])
authorize @post
...
end
end
Customize user
Rails Authorize will call the current_user
method to retrieve the user for authorization. If you need to customize it you can pass user
as option to method authorize
:
# app/controllers/posts_controller.rb
class PostController
def show
@post = Post.find(params[:id])
@user = User.find(params[:user_id])
authorize @post, user: @user
...
end
end
Customize action
Rails Authorize will use the controller action name as identifier of policy method to use for authorization. If you need to customize it you can pass action
as option to method authorize
:
# app/controllers/posts_controller.rb
class PostController
def show
@post = Post.find(params[:id])
authorize @post, action: :custom_action?
...
end
end
Define context
Rails Authorize allow you to define the context objects that you need to authorize an action:
# app/controllers/posts_controller.rb
class PostController
def show
@post = Post.find(params[:id])
authorize @post, context: {template: params[:template]}
...
end
end
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def show?
if context[:template] == 'complete' ?
user.is_admin?
else
true
end
end
end
Strong parameters
Rails uses strong_parameters to handle mass-assignment protection in the controller. With this gem you can control which attributes a user has access via your policies.
You can set up a permitted_attributes
method in your policy like this:
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def permitted_attributes
if user.admin?
%i[status body title]
else
%i[body title]
end
end
end
You can now retrieve these attributes from the policy:
policy(@post).permitted_attributes
policy(Post).permitted_attributes
Rails Authorize provides permitted_attributes
helper method to use it in your controllers:
# app/controllers/posts_controller.rb
class PostController
def create
Post.create(permitted_attributes(Post))
end
def update
@post.update(permitted_attributes(@post))
end
end
By default permitted_attributes
makes params.require(:post)
if you want to personalize what attribute is required in params, your policy must define a param_key
:
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def param_key
'custom_key'
end
end
Also, you can pass custom key as option using param_key
for specific situations:
# app/controllers/posts_controller.rb
class PostController
def update
@post.update(permitted_attributes(@post, param_key: 'custom_key'))
end
end
If you want to permit different attributes based on the current action, you can define a permitted_attributes_for_#{action_name}
method on your policy:
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def permitted_attributes_for_create
[:title, :body]
end
def permitted_attributes_for_update
[:body]
end
end
Use without target
Sometimes you need to authorize a controller action that it doesn't use a model to authorize.
For this situations you can omit target
and pass only options with policy
to authorize
or permitted_attributes
:
# app/controllers/custom_controller.rb
class CustomController
def show
authorize policy: CustomPolicy
...
end
def create
resource = Resource.new(permitted_attributes(policy: CustomPolicy))
...
end
end
# app/policies/custom_policy.rb
class CustomPolicy < ApplicationPolicy
def show?
# target is nil
...
end
def permitted_attributes
[:title, :body]
end
end
Ensuring authorization and scoping are performed
In certain kind of applications where almost all or even the whole application is private, in each of the actions you have to make sure that authorization is performed. To make sure that developers perform authorization, RailsAuthorize provides two methods. verify_authorized
makes sure that authorization is performed, and likewise verify_policy_scoped
checks that scoping is performed
Both methods are mainly aimed to be called on after_action
.
class ApplicationController < ActionController::Base
include RailsAuthorize
after_action :verify_authorized, except: :index
after_action :verify_policy_scoped, only: :index
end
Skipping verification
If you're using verify_authorized
in your controllers but need to conditionally bypass verification, you can use skip_authorization
. For bypassing verify_policy_scoped
, use skip_policy_scope
.
def create
record = Record.new(attributes)
if record.valid?
authorize record
else
skip_authorization
end
end
Rspec
For writing expressive tests for your policies in RSpec you can use this gem: rails_authorize_matchers
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/rjurado01/rails_authorize.
License
The gem is available as open source under the terms of the MIT License.