0

Authorizing your Rails app with Authority and Rolify

Bài viết này mình xin giới tiệu 1 cách phân quyền mới mạnh mẽ và có thể sử dụng linh hoạt bằng cách sử dụng :

  • Devise: Cung cấp chứng thực(authentication): cho phép người dùng đăng kí và đăng nhập, để bạn biết họ là ai.
  • Rolify: Giúp bạn chỉ định vai trò cho người dùng và kiểm tra vai trò mà họ có.
  • Authority: Điều khiển các role quản lí chúng sao cho phù hợp. Giúp bạn sử dụng các role hoặc bất kỳ logic nào bạn thích) để kiểm soát ai có thể làm những gì.

Installation

Ta thêm 3 gem vào trong gemfile rồi install:

gem 'devise'
gem 'authority'
gem 'rolify'

Bước 1: Cài đặt devise cho user

rails generate devise:install
rails generate devise User

Bước 2: Cài đặt gem Authority

rails generate authority:install

Bước 3: Cài đặt gem Rolify

rails generate rolify:role

Sau đó rake db:migrate ta được 2 model: 1 model chứa các role, và 1 model quản lý role của user

Configuration

Sau khi cài đặt, ta có được file /config/initializers/authority.rb chứa định nghĩa các quyền

Authority.configure do |config|
// Định nghĩa các quyền tương ứng với các action trong controler. Bạn có thể tùy chỉnh theo cách bạn muốn. Mặc định là:
  config.controller_action_map = {
    :index   => 'read',
    :show    => 'read',
    :new     => 'create',
    :create  => 'create',
    :edit    => 'update',
    :update  => 'update',
    :destroy => 'delete'
  }
// Định nghĩa các abiliies
  config.abilities =  {
    :create => 'creatable',
    :read   => 'readable',
    :update => 'updatable',
    :delete => 'deletable'
  }

//user ta dùng để sử dụng để phân quyền. có thể là current_user hoặc current_admin
  config.user_method = :current_user
end

Chỉnh sửa ApplicationAuthorizer được tạo mặc định khi cài gem authority. cập nhập default method để check xem có role admin không?

 def self.default(adjective, user)
    user.has_role? :admin
  end

Điều này cho thấy rằng tất cả các action đều yêu cầu vai trò role admin theo mặc định. Sau đó bạn có thể bắt đầu thêm logic cụ thể hơn.

 # To update a specific resource instance, you must either own it or be an admin
  def updatable_by?(user)
    resource.author == user || user.has_role?(:admin)
  end

Sử dụng phương thức rolify's resourcify trên tất cả model bạn muốn thêm role cho nó. Ví dụ ta thêm role cho model Post

class Post < ActiveRecord::Base
  resourcify
  belongs_to :author, class_name: 'User'
end

Thêm Authority::UserAbilities vào trong model uer(để thêm method như can_update?) và thêm Authority::Abilities vào trong model khác(để thêm method như updatable_by?)

class User < ActiveRecord::Base
  include Authority::UserAbilities
  has_many :posts, foreign_key: :author_id
end

...

class Post < ActiveRecord::Base
  resourcify
  include Authority::Abilities
end

Usage

Ở controler ta tạo 1 authorizer định nghĩa quyền truy cập của user tại model User

class UserAuthorizer < ApplicationAuthorizer

  def self.updatable_by?(user)
    user.has_role?(:admin) || user.has_role?(:editor)
  end

  def self.creatable_by?(user)
    user.has_role?(:admin) || user.has_role?(:editor)
  end

  def self.readable_by?(user)
    user.has_role?(:admin) || user.has_role?(:editor)
  end

  def self.deletable_by?(user)
    user.has_role?(:admin)
  end
end

Ở model /app/models/user.rb

class User < ActiveRecord::Base

  # creatable_by?(user)
  include Authority::Abilities

  # can_create?(resource)
  include Authority::UserAbilities

  # Tương ứng với /app/authorizers/user_authorizer.rb
  self.authorizer_name = 'UserAuthorizer'

  resourcify
  rolify
 
  after_create :assign_default_role

  def assign_default_role
    self.add_role(:editor)
  end

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable#, :confirmable
end

Ở controller: /app/controllers/admins/users_controller.rb

class Admins::UsersController < AdminsController
  alias :user_for_this_request :current_admins_user
  # ngoại trừ action show tất quả đều được phân quyền đã được định nghĩa ở user_authorizer.rb
  authorize_actions_for User, except: :show

  def index
    @users = User.all
  end

  def show

  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      params
        .require(:user)
        .permit(:email, :password, :password_confirmation)
    end

end

Ở application_controller ta render ra trang lỗi và báo với người dùng khi không có quyền truy cập

class ApplicationController < ActionController::Base
  rescue_from Authority::SecurityViolation, with: :authority_forbidden
  def authority_forbidden(error)
    Authority.logger.warn(error.message)
    render file: "#{Rails.root}/public/403.html", status: 403, layout: 'error'
  end
end

Một vài câu lệnh hay dùng: Ta vào rails c . Tạo vài user bằng cách

> alice = User.new
> alice.email = "[email protected]"
> alice.password = "test1234"
> alice.save

> bob = User.new
> bob.email = "[email protected]"
> bob.password = "test1234"
> bob.save

> cathy = User.new
> cathy.email = "[email protected]"
> cathy.password = "test1234"
> cathy.save

Thêm role admin cho user

> alice.add_role "admin"

Tạo Post cho user

> post = bob.posts.new
> post.title = 'Test Post'
> post.name = 'Test Post'
> post.content = 'Nothing to see here'
> post.save

Check xem ai có thể update Post

> bob.can_update?(post) #=> true; he is the author
> alice.can_update?(post) #=> true; she is an admin
> cathy.can_update?(post) #=> false

Kiếm tra xem user có role admin không

user.has_role? :admin #=> true or false

Xóa role admin của user

user.remove_role :admin

Hi vọng bài viết có thể giúp ích cho bạn!

Nguồn tham khảo:

http://qiita.com/[email protected]/items/9c079c275118fa7a0f36 https://github.com/RolifyCommunity/rolify/wiki/Using-rolify-with-Devise-and-Authority


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.