Rails - 政策中有 Pundit Scopes
Rails - with Pundit Scopes in policy
我正在尝试弄清楚如何在我的文章政策中使用专家政策范围。
我写了一篇文章政策,它嵌套了一个范围,然后在其中有一个解析方法。 resolve 方法有基于 current_user
是谁的替代方法。
我的文章政策有:
class ArticlePolicy < ApplicationPolicy
class Scope < Scope
attr_reader :user, :scope
# I now think I don't need these actions because I have changed the action in the articles controller to look for policy scope.
# def index?
# article.state_machine.in_state?(:publish)
# end
def show?
article.state_machine.in_state?(:publish) ||
user == article.user ||
article.state_machine.in_state?(:review) && user.org_approver ||
false
end
end
def create?
article.user.has_role?(:author)
end
def update?
# user && user.article.exists?(article.id) #&& user.article.created_at < 15.minutes.ago
user.present? && user == article.user
# add current state is not published or approved
end
def destroy?
user.present? && user == article.user
# user.admin?
# user.present?
# user && user.article.exists?(article.id)
end
end
private
def article
record
end
def resolve
if user == article.user
scope.where(user_id: user_id)
elsif approval_required?
scope.where(article.state_machine.in_state?(:review)).(user.has_role?(:org_approver))
else
article.state_machine.in_state?(:publish)
end
end
def approval_required?
true if article.user.has_role?(:author)
# elsif article.user.profile.organisation.onboarding.article_approval == true
# if onboarding (currently in another branch) requires org approval
end
def org_approver
if article.user.has_role? :author
user.has_role? :editor
# if onboarding (currently in another branch) requires org approval, then the approval manager for that org
elsif article.user.has_role? :blogger
user.has_role? :editor if user.profile.organisation.id == article.user.profile.organisation.id
end
end
end
pundit 文档中的示例显示了如何将其用于索引,但我如何将 resolve 方法用于显示操作?我可以为各种其他控制器操作编写多个解析方法吗?
我对 pundit
没有太多经验,但是通过查看文档和您的代码,我可以看到两件事。
1 - 你不应该在你的范围 class.
中使用像 show?
这样的方法
在您的范围 class 内,您应该只使用 returns 范围内的方法。 returns boolean 的方法应该在 Policy 级别。但是在您的代码中,我可以在 class 范围内使用布尔方法。
Instances of this class respond to the method resolve, which should return some kind of result which can be iterated over. For ActiveRecord classes, this would usually be an ActiveRecord::Relation.
2 - 鉴于 Scope 是 PORO(普通旧 Ruby 对象),您可以有多个解析方法(当然名称不同 :)),因为解析只是一个方法名称。
也许你可以做类似的事情
#policy
class ArticlePolicy < ApplicationPolicy
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
class Scope < Scope
def resolve
# some scope
end
def resolve_show
#scope for show action
# E.g scope.all
end
end
def show?
article.state_machine.in_state?(:publish) ||
user == article.user ||
article.state_machine.in_state?(:review) && user.org_approver || false
end
end
在你的控制器中
#Articles controller
class ArticlesController < ApplicationController
...
def show
authorize Article
ArticlePolicy::Scope.new(current_user, Article).resolve_show
end
...
end
这应该首先使用 ArticlePolicy#show?
和 ArticlePolicy::Scope#resolve_show
的范围授权您的显示方法
免责声明:未经测试的代码,使用风险自负;)
我正在尝试弄清楚如何在我的文章政策中使用专家政策范围。
我写了一篇文章政策,它嵌套了一个范围,然后在其中有一个解析方法。 resolve 方法有基于 current_user
是谁的替代方法。
我的文章政策有:
class ArticlePolicy < ApplicationPolicy
class Scope < Scope
attr_reader :user, :scope
# I now think I don't need these actions because I have changed the action in the articles controller to look for policy scope.
# def index?
# article.state_machine.in_state?(:publish)
# end
def show?
article.state_machine.in_state?(:publish) ||
user == article.user ||
article.state_machine.in_state?(:review) && user.org_approver ||
false
end
end
def create?
article.user.has_role?(:author)
end
def update?
# user && user.article.exists?(article.id) #&& user.article.created_at < 15.minutes.ago
user.present? && user == article.user
# add current state is not published or approved
end
def destroy?
user.present? && user == article.user
# user.admin?
# user.present?
# user && user.article.exists?(article.id)
end
end
private
def article
record
end
def resolve
if user == article.user
scope.where(user_id: user_id)
elsif approval_required?
scope.where(article.state_machine.in_state?(:review)).(user.has_role?(:org_approver))
else
article.state_machine.in_state?(:publish)
end
end
def approval_required?
true if article.user.has_role?(:author)
# elsif article.user.profile.organisation.onboarding.article_approval == true
# if onboarding (currently in another branch) requires org approval
end
def org_approver
if article.user.has_role? :author
user.has_role? :editor
# if onboarding (currently in another branch) requires org approval, then the approval manager for that org
elsif article.user.has_role? :blogger
user.has_role? :editor if user.profile.organisation.id == article.user.profile.organisation.id
end
end
end
pundit 文档中的示例显示了如何将其用于索引,但我如何将 resolve 方法用于显示操作?我可以为各种其他控制器操作编写多个解析方法吗?
我对 pundit
没有太多经验,但是通过查看文档和您的代码,我可以看到两件事。
1 - 你不应该在你的范围 class.
中使用像show?
这样的方法
在您的范围 class 内,您应该只使用 returns 范围内的方法。 returns boolean 的方法应该在 Policy 级别。但是在您的代码中,我可以在 class 范围内使用布尔方法。
Instances of this class respond to the method resolve, which should return some kind of result which can be iterated over. For ActiveRecord classes, this would usually be an ActiveRecord::Relation.
2 - 鉴于 Scope 是 PORO(普通旧 Ruby 对象),您可以有多个解析方法(当然名称不同 :)),因为解析只是一个方法名称。
也许你可以做类似的事情
#policy
class ArticlePolicy < ApplicationPolicy
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
class Scope < Scope
def resolve
# some scope
end
def resolve_show
#scope for show action
# E.g scope.all
end
end
def show?
article.state_machine.in_state?(:publish) ||
user == article.user ||
article.state_machine.in_state?(:review) && user.org_approver || false
end
end
在你的控制器中
#Articles controller
class ArticlesController < ApplicationController
...
def show
authorize Article
ArticlePolicy::Scope.new(current_user, Article).resolve_show
end
...
end
这应该首先使用 ArticlePolicy#show?
和 ArticlePolicy::Scope#resolve_show
免责声明:未经测试的代码,使用风险自负;)