Rails Action Authorization
Rails Action Authorization is a rails plugin that gives developers a lightweight authorization framework.
While there are lots of rails plugins designed to do authorization, Rails Action Authorization strives to be the most intuitive while simultaneously allowing developers to write the smallest possible amount of code to have a functioning authorization system.
Usage
There are two parts to using rails-action-authorization. The first part is defining rules.
Rules are defined on models, and they specify how actions determine how to authorize users (or other models).
The second parrt of using rails-action-authorization is on the controller side. In any action in which authorization is required, run the check_authorization method.
Step One, defining rules
Suppose we are writing the proverbial blogging application, and we need some way to determine whether a user is permitted to edit a post.
The first step is to define a rule for the action that will need authorization. In our case, we'll need authorization for the edit and update actions. To define a rule, we'll have to edit our Post model.
# models/Post.rb
class Post < ApplicationRecord
...
endRules are defined using the define_rule method. define_rule takes at least one argument that is a string or a symbol that represents the action that needs authorization in the format controller#action. In our case, we need to authorize the edit and update actions like so:
# models/Post.rb
class Post < ApplicationRecord
...
define_rule 'posts#edit', 'posts#update' do |post, user|
# Authorization code here
end
enddefine_rule can take as many arguments as desired, enabling developers to define authorization rules for multiple actions simultaneously.
The block must return true or false, true if the user is permitted to edit the post
and false if the user is not permitted. The block itself will always yield the resource
that is being authorized as the first argument and the actor requesting authorization as the second argument. The first argument should always be the type as the class in which define_rule is called (if you ever encounter a case where it is not, then please report it as a bug). The second argument could technically be any model, but will probably most often be a User instance.
Since we only want a post's author to be able to edit a post, we'll define our rule as follows:
# models/Post.rb
class Post < ApplicationRecord
...
define_rule 'posts#edit', 'posts#update' do |post, user|
post.author == user
end
endNext, we have to check the authorization of the user in each controller. The only thing required
to do this is to invoke check_authorization somewhere in each action that requries authorization.
# controllers/posts_controller.rb
class PostsController < ApplicationController
before_action :authorize_user, only: [:edit, :update]
...
def edit
...
#You will be able to reference @post in your views as usuals.
end
def update
...
end
private
def authorize_user
@post = check_authorization(Post.find_by(id: params[:id]), current_user) # Here, use your own method for getting the current user.
end
endNotice that we are able to call check_authorization in a before_action. This is because check_authorization
knows how to identify the action it is called in without requiring developers to specify it themselves.
This will work great when the owner tries to edit his own posts, but if another user attempts to edit
a post, the server will raise an error. rails-action-authorization raises a ForbiddenError if a
user fails to be authorized in order to eliminate potential ambiguity of returning nil or some other
value.
In order to handle authorization failures, we'll have to adapt our controller slightly:
# controllers/posts_controller.rb
class PostsController < ApplicationController
before_action :authorize_user, only: [:edit, :update]
rescue_from ActionAuthorization::ForbiddenError, with:
:handle_forbidden
...
def edit
...
#You will be able to reference @post in your views as usuals.
end
def update
...
end
private
def authorize_user
@post = check_authorization(Post.find_by(id: params[:id]), current_user) # Here, use your own method for getting the current user.
end
def handle_forbidden
render '403', status: 403
end
endThis will cause the execution of any action to cease immediately when a user fails to authorize, and
instead run the code in handle_forbidden.
Of course, the above example assumes that you have a template for a 403 error, but you can run
whatever code is necessary in your case.
Installation
Add this line to your application's Gemfile:
gem 'rails-action-authorization'And then execute:
$ bundleOr install it yourself as:
$ gem install rails-action-authorizationLicense
The gem is available as open source under the terms of the MIT License.