12/08/2018, 15:34

Phân quyền người dùng với gem Administrate và Rolify

Administrate là một thư viện Rails tự động tạo ra trang quản lý admin. Trang quản lý này sẽ tạo ra giao diện mặc định cho phép người dùng có thể thêm, sửa hoặc xóa các bạn ghi của tất cả các model trong ứng dụng. Hệ thống nào cũng cần phải có chức năng phân quyền, ở đây tôi giới thiệu một thư ...

Administrate là một thư viện Rails tự động tạo ra trang quản lý admin. Trang quản lý này sẽ tạo ra giao diện mặc định cho phép người dùng có thể thêm, sửa hoặc xóa các bạn ghi của tất cả các model trong ứng dụng.

Hệ thống nào cũng cần phải có chức năng phân quyền, ở đây tôi giới thiệu một thư viện hỗ trợ phân quyền khá hay đó là gem Rolify. Và chúng ta sẽ có một giao diện quản lý phân quyền cho tất cả người dùng một cách rất nhanh và linh hoạt.

Thêm vào trong Gemfile

gem 'rolify' 

Sau đó bundle install

chạy lệnh sau để kích hoạt:

$ rails g rolify Role User

và migrate

$ rake db:migrate

Một model role sẽ được tạo ra hỗ trợ admin phân quyền Chúng ta tạo những quyền truy cập khác nhau để lưu vào database

Role.create name: :normal
Role.create name: :staff
Role.create name: :admin

Thêm vào trong Gemfile

gem 'administrate' 

tiếp theo

$ bundle install

chạy lệnh sau để cài đặt vào hệ thống

$ rails generate administrate:install

Cài đặt giao diện quản lý phân quyền:

$ rails generate administrate:dashboard Role

Bạn nên cài đặt routes cho Users và Roles ví dụ như sau:

# config/routes.rb
namespace :admin do
  resources :users
  resources :roles, only: [:index, :show]
  root to: "users#index"
end

Chỉ cho phép người dùng có quyền admin mới được thực hiện:

# app/controllers/admin/application_controller.rb
module Admin
  class ApplicationController < Administrate::ApplicationController
    before_action :authenticate_user!
    before_action :authenticate_admin

    def authenticate_admin
      redirect_to root_path, alert: 'Not authorized.' unless current_user.has_role?(:admin)
    end
  end
end

Bây giờ chúng ta sẽ cấu hình giao diện phân quyền để chỉ nhìn thấy tên quyền:

# app/dashboards/role_dashboard.rb
class RoleDashboard < Administrate::BaseDashboard
  ATTRIBUTE_TYPES = {
    name: Field::String,
  }.freeze

  COLLECTION_ATTRIBUTES = [
    :name,
  ].freeze

  SHOW_PAGE_ATTRIBUTES = [
    :name,
  ].freeze

  FORM_ATTRIBUTES = [
    :name,
  ].freeze

end

Để chỉnh sửa giao diện quản lý phân quyền, chúng ta cần tạo một CustomField kế thừa HasManyField

$ rails generate administrate:field HasManyRoles

Những file bên dưới phải được sửa như sau:

# fields/has_many_roles_field.rb 
# https://github.com/thoughtbot/administrate/issues/192
require "administrate/field/base"

class HasManyRolesField < Administrate::Field::HasMany
end


# app/views/fields/has_many_roles_field/_form.html.erb
<div class="field-unit__label">
  <%= f.label field.attribute_key, field.attribute %>
</div>
<div class="field-unit__field">
  <%= f.select(field.attribute_key, nil, {}, multiple: true) do %>
  <%= options_for_select(field.associated_resource_options, field.selected_options) %>
  <% end %>
</div>

# app/views/fields/has_many_roles_field/_index.html.erb
<%= field.data.pluck(:name).join(' ') %>

# app/views/fields/has_many_roles_field/_show.html.erb
<%= field.data.pluck(:name).join(' ') %>

Bây giờ chúng ta tiến hành sửa để hiển thị quyền truy cập

# app/dashboards/user_dashboard.rb
class UserDashboard < Administrate::BaseDashboard
  ATTRIBUTE_TYPES = {
    id: Field::Number,
    email: Field::String,
    roles: HasManyRolesField,
  }.freeze

  COLLECTION_ATTRIBUTES = [
    :roles,
    :id,
    :email,
  ].freeze

  SHOW_PAGE_ATTRIBUTES = [
    :roles,
    :id,
    :email,
  ].freeze

  FORM_ATTRIBUTES = [
    :roles,
    :email,
  ].freeze
end

Restart server và vào đường dẫn http://localhost:3000/admin/users để xem giao diện quản lý mới

Bonus: Phân quyền mặc định cho người dùng mới

# app/models/user.rb
class User < ApplicationRecord
  ...
  after_initialize :set_default_role, if: :new_record?

  validates :roles, presence: true

  def set_default_role
    self.add_role(:normal)
  end
end

Extra bonus: validate quyền truy cập:

# app/models/role.rb
class Role < ApplicationRecord
...
validates :name,
        inclusion: { in: ["admin", "normal", "staff"] },
        uniqueness: true
end

Sử dụng Administrate, chúng ta có thể dễ dàng thực hiện được phân quyền một cách linh hoạt chỉ bởi một số dòng code đơn giản.

0