13

宝石文件

gem 'pundit', '~> 0.2.1'

应用程序/控制器/application_controller.rb

class ApplicationController < ActionController::Base

  include Pundit
  ...

应用程序/策略/application_policy.rb

class ApplicationPolicy < Struct.new(:user, :record)
  def index?  ; false;                              end
  def show?   ; scope.where(id: record.id).exists?; end
  def create? ; false;                              end
  def new?    ; create?;                            end
  def update? ; false;                              end
  def edit?   ; update?;                            end
  def destroy?; false;                              end
  def scope
    Pundit.policy_scope!(user, record.class)
  end
end

应用/政策/book_policy.rb

class BookPolicy < ApplicationPolicy

  def create?
    record.new_record?
  end

  def new?
    create?      end

  def show?
    record.published? || user == record.user || user.is?(:admin)
  end

end

应用程序/控制器/books_controller.rb

class BooksController < ApplicationController
  before_action :set_book, only: [:show, :edit, :update, :destroy]
  before_action :authenticate_user!, except: [:show]

  after_action :verify_authorized, except: :index
  after_action :verify_policy_scoped, only: :index

  # GET /books/1
  def show
    authorize(@book)
  end

  # GET /books/new
  def new
    @book = Book.new
    authorize(@book)
  end

  # POST /books
  def create
    @book = current_user.books.build(book_params)
    authorize(@book)

    if @book.save
      redirect_to @book, notice: 'Your book was successfully created.'
    else
      render action: 'new'
    end
  end

private
    def set_book
      @book = Book.find(params[:id])
    end

    def book_params
      params.require(:book).permit(:title, :description)
    end
end

测试/工厂/factories.rb

FactoryGirl.define do

  factory :user do
    sequence(:email) { |n| "email#{n}@x.com" }
    password  '12345678'
    password_confirmation  '12345678'
  end

  factory :book do
    title  'xx'
    user
  end

end
4

4 回答 4

18

第一次尝试

根据Minitest我尝试过的文档< Minitest::Test,但得到了gems/minitest-4.7.5/lib/minitest/unit.rb:19:in 'const_missing': uninitialized constant MiniTest::Test (NameError)这导致我发现master 中的文档适用于 Minitest 5。所以我在第 5 版提交之前搜索了 Minitest 文档,发现我们应该是子类化MiniTest::Unit::TestCase

测试/政策/book_policy_test.rb

require 'test_helper'
class BookPolicyTest < Minitest::Test
  ...
end

第二次尝试(正确的超类)

测试/政策/book_policy_test.rb

require 'test_helper'

class BookPolicyTest < Minitest::Unit::TestCase

  def test_new
    user = FactoryGirl.create(:user)
    book_policy = BookPolicy.new(user, Book.new)
    assert book_policy.new?
  end

  def test_create
    book = FactoryGirl.create(:book)
    book_policy = BookPolicy.new(book.user, book)
    assert !book_policy.create?
  end

end

重构一(创建“许可”方法)

测试/政策/book_policy_test.rb

require 'test_helper'

class BookPolicyTest < Minitest::Unit::TestCase

  def test_new
    user = FactoryGirl.create(:user)
    assert permit(user, Book.new, :new)
  end

  def test_create
    book = FactoryGirl.create(:book)
    assert !permit(book.user, book, :create)
  end

private

    def permit(current_user, record, action)
      self.class.to_s.gsub(/Test/, '').constantize.new(current_user, record).public_send("#{action.to_s}?")
    end

end

重构二(创建“PolicyTest”类)

测试/test_helper.rb

class PolicyTest < Minitest::Unit::TestCase

  def permit(current_user, record, action)
    self.class.to_s.gsub(/Test/, '').constantize.new(current_user, record).public_send("#{action.to_s}?")
  end

end

测试/政策/book_policy_test.rb

require 'test_helper'

class BookPolicyTest < PolicyTest

  def test_new
    user = FactoryGirl.create(:user)
    assert permit(user, Book.new, :new)
  end

  def test_create
    book = FactoryGirl.create(:book)
    assert !permit(book.user, book, :create)
  end

end

重构三(创建“禁止”方法)

测试/test_helper.rb

class PolicyTest < Minitest::Unit::TestCase

  def permit(current_user, record, action)
    self.class.to_s.gsub(/Test/, '').constantize.new(current_user, record).public_send("#{action.to_s}?")
  end

  def forbid(current_user, record, action)
    !permit(current_user, record, action)
  end

end

测试/政策/book_policy_test.rb

require 'test_helper'

class BookPolicyTest < PolicyTest

  def test_new
    user = FactoryGirl.create(:user)
    assert permit(user, Book.new, :new)
  end

  def test_create
    book = FactoryGirl.create(:book)
    assert forbid(book.user, book, :create)
  end

end

添加全套策略测试

require 'test_helper'

class BookPolicyTest < PolicyTest

  def test_new
    assert permit(User.new, Book.new, :new)

    book = FactoryGirl.create(:book)
    assert forbid(book.user, book, :new)
  end

  def test_create
    assert permit(User.new, Book.new, :create)

    book = FactoryGirl.create(:book)
    assert forbid(book.user, book, :create)
  end

  def test_show
    # a stranger should be able to see a published book
    stranger = FactoryGirl.build(:user)
    book = FactoryGirl.create(:book, published: true)
    refute_equal stranger, book.user
    assert permit(stranger, book, :show)

    # but not if it's NOT published
    book.published = false
    assert forbid(stranger, book, :show)

    # but the book owner still should
    assert permit(book.user, book, :show)

    # and so should the admin
    admin = FactoryGirl.build(:admin)
    assert permit(admin, book, :show)
  end

end
于 2013-11-28T02:55:11.710 回答
7

我创建了一个 gem 用于使用 minitest 测试权威人士,称为policy-assertions。这是您的测试的样子。

class ArticlePolicyTest < PolicyAssertions::Test
  def test_index_and_show
    assert_permit nil, Article
  end

  def test_new_and_create
    assert_permit users(:staff), Article
  end

  def test_destroy
    refute_permit users(:regular), articles(:instructions)
  end
end
于 2015-06-10T10:57:25.473 回答
3

Pundit 现在提供Pundit#authorize( https://github.com/elabs/pundit/pull/227 )。所以 user664833 的 permit 方法可以更新如下:

def permit(current_context, record, action)
  Pundit.authorize(current_context, record, action)
rescue Pundit::NotAuthorizedError
  false
end
于 2015-07-18T18:14:16.283 回答
2

基于 user664833 的回答,我使用以下内容来测试 Pundit 的 policy_scope:

def permit_index(user, record)
  (record.class.to_s + 'Policy::Scope').constantize.new(user, record.class).resolve.include?(record)
end

例如:

assert permit_index(@book.user, @book)
于 2014-03-20T17:51:08.103 回答