Leon's Blogging

Coding blogging for hackers.

用 Pundit 來做權限管理

| Comments

可以搭配 enum 簡單快速地做出權限管理。

指令

產生資料夾 app/policies/

1
rails g pundit:install

設定

接著就可以根據你的每個 controller action 去個別設定是否有這權限
通常會搭配 deviseenum 加上欄位 role 去做調整。

1
2
3
4
5
6
7
8
9
10
11
12
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

Controller

在 controller 就可以藉由 authorize 來設定要授權的是什麼

1
2
3
4
5
6
7
8
9
def update
  @post = Post.find(params[:id])
  authorize @post
  if @post.update(post_params)
    redirect_to @post
  else
    render :edit
  end
end

記得要 include Pundit
也可以加上 verify_authorized method 來確保每個 action 都有做好設定

1
2
3
4
class ApplicationController < ActionController::Base
  include Pundit
  after_action :verify_authorized
end

錯誤處理 rescue

可以在 ApplicationController 上面直接做處理,相當方便

1
2
3
4
5
6
7
8
9
10
11
12
13
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

RSPEC

基本上 github 上面就非常清楚了

1
2
#rails_helper.rb
require "pundit/rspec"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#spec/policies

describe PostPolicy do
  subject { described_class }

  permissions :update?, :edit? do
    it "denies access if post is published" do
      expect(subject).not_to permit(User.new(admin: false), Post.new(published: true))
    end

    it "grants access if post is published and user is an admin" do
      expect(subject).to permit(User.new(admin: true), Post.new(published: true))
    end

    it "grants access if post is unpublished" do
      expect(subject).to permit(User.new(admin: false), Post.new(published: false))
    end
  end
end

gem: pundit

參考資料:
Rails Notes: Authorization Using Pundit
Rail Authorization with pundit and devise

Comments