12/08/2018, 11:56

Rails Authorization with Pundit

Xác thực người dùng và kiểm soát quyền của người dùng là phần quan trọng không thể thiếu. Một trong những gem xác thực thường được sử dụng trong RoR là Pundit. Pundit cung cấp một set helper cho phép bạn nâng tầm các class và các đối tượng trong Ruby để xây dựng một hệ thống xác thực đơn giản, hiểu ...

Xác thực người dùng và kiểm soát quyền của người dùng là phần quan trọng không thể thiếu. Một trong những gem xác thực thường được sử dụng trong RoR là Pundit. Pundit cung cấp một set helper cho phép bạn nâng tầm các class và các đối tượng trong Ruby để xây dựng một hệ thống xác thực đơn giản, hiểu qủa và dễ dàng nâng cấp.

I. Cài đặt:

gem "pundit"

Include Pundit trong application_controller.rb

class ApplicationController < ActionController::Base
  include Pundit
  protect_from_forgery
end

Ngoài ra, bạn cũng có thể cài đặt các policy cơ bản bằng cách khởi tạo qua câu lệnh:

rails g pundit:install

II. Policy

Pundit tập trung vào các class policy và đặt trong app/policy. Sau đây là một ví dụ đơn giản, người dùng có thể cập nhập một bài viết (post) nếu có quyền admin hoặc nếu bài viết đó chưa được công khai (unpublished):

class PostPolicy
  attr_reader :user, :post

  def initialize(user, post)
    @user = user
    @post = post
  end

  def update?
    user.admin? or not post.published?
  end
end

Như bạn thấy, đây là một class Ruby. Pundit sẽ đưa ra các gỉa định về class policy như sau:

  • Class policy có cùng tên với class model, kèm theo hậu tố "Policy".
  • argument đầu tiên là user. Trên controller, Pundit sẽ gọi current_user và gửi đến argument này.
  • Argument thứ hai là một dạng object model, là object bạn cần xác thực. Object này không bắt buộc phải là một ActiveRecord hay là một object ActiveModel.
  • Class sẽ implement một query method, ở ví dụ trên là method update?, tên method này tương ứng với một action trên controller.

III. Sử dụng các policy

Trên controller:

def update
  @post = Post.find(params[:id])
  authorize @post
  if @post.update(post_params)
    redirect_to @post
  else
    render :edit
  end
end

Trên view:

<% if policy(@post).update? %>
  <%= link_to "Edit post", edit_post_path(@post) %>
<% end %>

IV. Báo lỗi NotAuthorizedErrors:

Pundit sẽ gọi Pundit::NotAuthorizedError trên ApplicationController. Pundit cho phép tùy biến method user_not_authorized cho tất cả các controller

class ApplicationController < ActionController::Base
  protect_from_forgery
  include Pundit

  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

  private

  def user_not_authorized
    flash[:alert] = "You are not authorized to perform this action."
    redirect_to(request.referrer || root_path)
  end
end

Pundit cũng cho phép tùy biến báo lỗi cho từng method trong Policy. Người dùng có thể dùng các query, record, policy và I18n để tùy biến báo lỗi. Ví dụ như sau:

class ApplicationController < ActionController::Base
 rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

 private

 def user_not_authorized(exception)
   policy_name = exception.policy.class.to_s.underscore

   flash[:error] = t "#{policy_name}.#{exception.query}", scope: "pundit", default: :default
   redirect_to(request.referrer || root_path)
 end
end

Trên file I18n:

en:
 pundit:
   default: 'You cannot perform this action.'
   post_policy:
     update?: 'You cannot edit this post!'
     create?: 'You cannot create posts!'
0