Gem CanCanCan
Bài đăng này đã không được cập nhật trong 4 năm
1. Giới thiệu gem CanCanCan:
CanCanCan
là một authorization library giúp ta quy định được user nào có quyền truy cập được resource nào và có thể action gì với resource đó.- Tất cả logic về phần quyền có thể được định nghĩa trong một hay nhiều file mà không bị lặp lại giữa nhiều controller, view khác nhau.
- Điều đó giúp cho logic phân quyền được tập trung tại một chỗ dễ dàng cho maintain và test hơn
CanCanCan
gồm 2 phần chính
- Authorization library cho phép định nghĩa các rule khác nhau để quy định logic phần quyền cho các object khác nhau, cung cấp thêm các helper để check phân quyền.
- Rails helper giúp đơn giản hóa code trong Rails Controller bằng loading và checking phân quyền của từng model một cách tự động và hạn chế được lặp code.
2. Installation:
- Thêm gem
cancancan
vàoGemfile
# Gemfile gem "cancancan"
- Chạy lệnh bundle install để install gem
cancancan
vào project Rails.
3. Define Abilities:
- Chạy lệnh để tạo Ability class quy định logic phân quyền cho user
rails g cancan:ability
- File
app/models/ability.rb
được tạo raclass Ability include CanCan::Ability def initialize(user) end end
- Method initialize của class Ability được dùng để định nghĩa logic phân quyền của user được truyền vào.
- Ví dụ:
can :read, :all
- Với ví dụ trên user không cần login cũng có permission
read
tất cả (:all
) model. - Hoặc
can :read, :all if user.present?
- Với ví dụ trên user đã login mới có permission
read
tất cả (:all
) model. - Theo mặc định thì current_user được truyền vào trong method initialize của class Ability.
- Ta có các ví dụ khác như sau
# user không cần logic cũng có permission read với các post có public là true can :read, Post, public: true # user đã login ó permission read với các post có user_id là id của user đã login can :read, Post, user_id: user.id if user.present? # user admin đã login có permission read với các post (bất kể public và user_id của post đó) can :read, Post if user.present? && user.admin?
4. The can
method:
- Method
can
được sử dụng để quy định logic phân quyền. - Method
can
nhận hai tham số. - Tham số thứ nhất là action mà bạn đang quy đinh logic phân quyền.
- action nhận 4 giá trị tương đương với
CRUD
lần lượt là :create, :read, :update, :destroy - Tham số thứ hai là class của object mà bạn đang quy định logic phân quyền.
- Ví dụ:
can :update, Article
- Ngoài ra khi sử dụng
Action Aliases
vàCustom Actions
, action có thể nhận thêm các giá trị khác. - action và class cũng có thể nhận giá trị là array.
- Ví dụ:
can [:update, :destroy], [Article, Comment]
- action có thể nhận giá trị là :manage tương đương với tất cả
action
. - class có thể nhận giá trị là :all tương đương với tất cả
class
. - Trong trường hơp có các action ngoài các action CRUD, khi action nhân giá trị
:manage
thì user cũng có permission để thực hiện các action này. - Ví dụ:
can :manage, User can :invite, User
- Khi đó user có thể thực hiện những action CRUD và action
invite
với User. - Để user chỉ thực hiện những action CRUD thôi, ta có thể sử dụng AliasAction như sau.
alias_action :create, :read, :update, :destroy, to: :crud can :crud, User
- Bản thân các action
:create
,:read
,:update
cũng là cácalias action
mặc định như sau.alias_action :new, to: :create alias_action :index, :show, to: :read alias_action :edit, to: :update
5. Hash of condition:
- Hash condition có thể được thêm vào method
can
để quy định logic phân quyền được apply cho record nào. - Ví dụ:
can :read, Project, active: true, user_id: user.id can :read, Project, priority: 1..3
- Trong trường hợp logic phân quyền dựa trên association, bạn có thể sử dụng nested hash như sau
can :read, Post, category: {visible: true} can :read, Project, group: {id: user.group_ids}
- Hash condition cũng có thể sử dụng scope như sau
can :read, Photo.unowned do |photo| photo.groups.empty? end
6. Combining Abilities:
- Chúng ta có thể định nghĩa nhiều logic phân quyền cho chung một resource.
- Ví dụ
can :read, Project, released: true can :read, Project, preview: true
- Với ví dụ trên thì user có thể thực hiện action
:read
với Project có released là true hoặc có preview là true. - Bên cạnh
can
ta còn có cannot để quy định user không thể thực hiện action nào đó - Ví dụ
can :manage, Project cannot :destroy, Project
- Với ví dụ trên thì user có thể thực hiện tất cả action trừ action
:destroy
.
7. Check Abilities:
- Bên cạnh method
can
vàcannot
để quy định logic phân quyền của user với từng model,cancancan
cung cấp thêm method can? và cannot? để kiểm tra user có thể thực hiện action nào đó hay không. - Trong view và controller ta có thể thực hiện kiểm tra phân quyền như sau
can? :create, Project
- Nếu method
can
nhận hash condition, methodcan?
được gọi với class sẽ bỏ qua hash condition và luôn luôn trả vềtrue
- Ví dụ
can :read, Project, priority: 3 can? :read, Project # true can? :read, project # true nếu project.priority == 3 can? :read, project # false nếu project.priority != 3
- Nguyên nhân là với record
project
thì có thể thực hiên kiểm traproject.priority
có bằng 3 hay không - Nhưng với class
Project
thì không thể thự hiện kiểm tra như vậy được. - Nguyên nhân
cancancan
trả về true với case này là để sử dụng cho action index - Tuy nhiên đối với action index, thay vì sử dụng scope
all
ta nên sử dụng Fetching Records với scope accessible_by như sau - Ví dụ:
Project.accessible_by(curent_action, :index)
- Đối với rails console ta có thể thực hiện kiểm rea phần quyền như sau
user = User.first curent_ability = Abilirt.new user curent_ability.can? :read, Project
8. Controller helpers:
CanCanCan
cần methodcurent_user
tồn tại trong controller.- Do đó cần setup với gem
Devise
trước khi thực hiện phân quyền với gemCanCanCan
.
1. Authorizations:
- Method authorize! được gọi trong controller sẽ raise exception nếu user không được phân quyền để thực hiện action đang được authorize.
- Ví dụ:
def update authorize! :update, @post end
CanCanCan
sẽ raise CanCan::AccessDenied exception, chúng ra cần thực hiện rescue_from trong controller để tránh bị dừng chương trình hoặc hiển thị trang lỗi với userclass ApplicationController < ActionController::Base rescue_from CanCan::AccessDenied, with: :cancan_access_denied private def cancan_access_denied flash[:danger] = "You are not authorized to access this page." redirect_to root_url end end
2. Loaders:
CanCanCan
cung cấp method load_and_authorize_resource để tự động load resource và thực hiện kiểm tra quyền của user trước mỗi action.- Method này là kết hợp của hai methof load_resource và authorize_resource
- Method load_resource sẽ tự động thực hiện load resource trước mỗi action
- Ví dụ
class PostsController < ApplicationController load_resource def index # @posts được load bởi load_resource # @posts = Post.accessible_by(current_ability) end def show # @post được load bởi load_resource # @post = Post.find id: params[:id] end # action edit, update, destroy tương tự action show end
- Method
load_resource
sẽ raise ActiveRecord::RecordNotFound exception với các action:show
,:edit
,:update
,:destroy
khi không tìm thấy resource. - Do đó ta cần thực hiện rescue_from ActiveRecord::RecordNotFound tương tự
rescue_from CanCan::AccessDenied
class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound, with: :active_record_record_not_found private def active_record_record_not_found flash[:danger] = "Couldn't find resource." redirect_to root_url end end
- Method authorize_resource sẽ tự động thực hiện kiểm tra quyền của user với resource trước mỗi action.
class PostsController < ApplicationController load_resource authorize_resource def index # authorize! :read, @posts end def show # authorize! :read, @post end end
CanCanCan
cung cấp các method skip_load_and_authorize_resource, skip_load_resource, skip_authorize_resource để skip các method load_and_authorize_resource, load_resource, authorize_resource- Ví dụ:
class PostsController < ApplicationController load_and_authorize_resource skil_load_and_authorize_resource only: :index def index # load_and_authorize_resource không được gọi end def show # load_and_authorize_resource được gọi end end
9. Source code demo:
All rights reserved