-
Notifications
You must be signed in to change notification settings - Fork 15
Tutorial
This tutorial is an introduction to creating authorization with Banken.
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
def index
@posts = Post.all
end
def show
end
def new
@post = Post.new
end
def edit
end
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post, notice: 'Post was successfully created.'
else
render :new
end
end
def update
if @post.update(post_params)
redirect_to @post, notice: 'Post was successfully updated.'
else
render :edit
end
end
def destroy
@post.destroy
redirect_to posts_url, notice: 'Post was successfully destroyed.'
end
private
def set_post
@post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :body, :published_at)
end
end
We try to implement the process of updating the articles that meet the following requirements for the above posts_controller.rb
.
- Can update anyone article in the case of unpublished it.
- Can't update article in the case of published it.
Banken does not have constraints and conventions for the authority control.
You can freely design the logic and role for authorization.
This time, it add a admin
column to the users table.
true
means Administrator and 'false' means general user.
> User.find(1).admin?
true
> User.find(2).admin?
false
If you want to have three or more roles, You can use the Enum of Rails 4.1, If you want to have a plurality of types of roles to one user, You can create the roles table. Banken can faithfully control authority even if such as requirements.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include Banken
protect_from_forgery
end
> rails g banken:install
create app/loyalties/application_loyalty.rb
Preparation is very simple, two-step.
-
Banken
moduleinclude
toApplicationController
-
rails g banken:install
generateapp/loyalties/application_loyalty.rb
#app/loyalties/application_loyalty.rb
class ApplicationLoyalty
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def index?
false
end
def show?
false
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
end
Now you keep Banken in your app. Preparation is now ready.
It create a loyalty class with the same name as the Controller.
So, You need to create PostsLoyalty
class with the same name as PostsController
class
> rails g banken:loyalty posts
create app/loyalties/posts_loyalty.rb
#app/loyalties/posts_loyalty.rb
class PostsLoyalty < ApplicationLoyalty
end
You can create PostsLoyalty
that inherits from ApplicationLoyalty
in app/loyalties/
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def update
authorize! @post
if @post.update(post_params)
redirect_to @post, notice: 'Post was successfully updated.'
else
render :edit
end
end
end
It is possible to determine whether processing after by performing authorize!
.
authorize!
perform the following processing.
- It create a instance of
PostsLoyalty
class with the same name asPostsController
. - It execute
update?
method in a instance ofPostsLoyalty
class.
In this case, you can imagine that authorize would have done something like this
def update
raise "not authorized" unless PostsLoyalty.new(current_user, @post).update?
if @post.update(post_params)
redirect_to @post, notice: 'Post was successfully updated.'
else
render :edit
end
end
As the first argument current_user
is executed when you create an instance of PostsLoyalty
class.
So, You must define the current_user
in ApplicationController
.
@post
of the second argument is an object that you passed as first argument to authorize!
.
First argument of authorize!
do not need to pass if it is not necessary.
# app/loyalties/posts_loyalty.rb
class PostsLoyalty < ApplicationLoyalty
def update?
user.admin? || record.unpublished?
end
end
user
iscurrent_user
andrecord
is@post
of first argument of authorize!
.
Banken raise an exception(Banken::NotAuthorizedError
) in case of returning false by update?
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
rescue_from Banken::NotAuthorizedError, with: :user_not_authorized
private
def user_not_authorized(exception)
loyalty_name = exception.loyalty.class.to_s.underscore
flash[:error] = t "#{loyalty_name}.#{exception.query}", scope: "banken", default: :default
redirect_to(request.referrer || root_path)
end
end
ja:
banken:
default: 'You cannot perform this action.'
posts_loyalty:
update?: 'You cannot edit this post!'
create?: 'You cannot create posts!'
<% if loyalty(@post, :posts).update? %>
<%= link_to "Edit post", edit_post_path(@post) %>
<% end %>
You can easily get a hold of an instance of the loyalty through the loyalty
method in both the view. This is especially useful for conditionally showing links or buttons in the view.
In this case, you can imagine that authorize would have done something like this
<% if PostsLoyalty.new(current_user, @post).update? %>
<%= link_to "Edit post", edit_post_path(@post) %>
<% end %>
It is now complete.
As you can see, Banken is a small library. But it can separate the logic of Controller and View to Loyalty layer. And it is very useful as Fat Controller measures. Remember that all of the loyalty class are just plain Ruby classes, which means you can use the same mechanisms you always use to DRY things up. Encapsulate a set of permissions into a module and include them in multiple policies. Banken is very simple internal implementation and is not an extension of Rails. If it is Rails version up. it will not be a problem.