Rails Pundit允许其他人查看已发布的内容,同时阻止他人查看用户的信息

时间:2016-12-16 15:41:07

标签: ruby-on-rails active-model-serializers pundit

好的,我的系统中有UserBookChapter个实体。

如果作者(User实体)发布了一本书和一章,那么它可供公众查看。让我们致电作者吉姆。

这意味着如果另一位名叫Tycus的普通用户想要阅读Jim的书籍和书籍章节,他应该能够这样做。

我正在使用Pundit gem(https://github.com/elabs/pundit)获取权限。

我面临的问题是,当Tycus试图访问Jim的书时,看来我的Rails正试图获取链接关系(chapter --> book --> author)及其:

Started GET "//books/16" for ::1 at 2016-12-16 23:29:38 +0800
Processing by BooksController#show as JSON
  Parameters: {"id"=>"16"}
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 21], ["LIMIT", 1]]
  Book Load (0.1ms)  SELECT  "books".* FROM "books" WHERE "books"."id" = ? LIMIT ?  [["id", 16], ["LIMIT", 1]]
  Role Load (0.1ms)  SELECT  "roles".* FROM "roles" WHERE "roles"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
[active_model_serializers]   User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
[active_model_serializers]   Chapter Load (0.1ms)  SELECT "chapters".* FROM "chapters" WHERE "chapters"."book_id" = ?  [["book_id", 16]]
[active_model_serializers]   Genre Load (0.1ms)  SELECT "genres".* FROM "genres" INNER JOIN "books_genres" ON "genres"."id" = "books_genres"."genre_id" WHERE "books_genres"."book_id" = ?  [["book_id", 16]]
[active_model_serializers]   Love Load (0.1ms)  SELECT "loves".* FROM "loves" WHERE "loves"."book_id" = ?  [["book_id", 16]]
[active_model_serializers] Rendered BookSerializer with ActiveModelSerializers::Adapter::JsonApi (30.04ms)
Completed 200 OK in 48ms (Views: 29.6ms | ActiveRecord: 2.0ms)


Started GET "//chapters/5" for ::1 at 2016-12-16 23:29:38 +0800
Processing by ChaptersController#show as JSON
  Parameters: {"id"=>"5"}
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 21], ["LIMIT", 1]]
  Chapter Load (0.1ms)  SELECT  "chapters".* FROM "chapters" WHERE "chapters"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
  Book Load (0.1ms)  SELECT  "books".* FROM "books" WHERE "books"."id" = ? LIMIT ?  [["id", 16], ["LIMIT", 1]]
Started GET "//chapters/7" for ::1 at 2016-12-16 23:29:38 +0800
Started GET "//chapters/6" for ::1 at 2016-12-16 23:29:38 +0800
[active_model_serializers]   User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
Processing by ChaptersController#show as JSON
Processing by ChaptersController#show as JSON
Started GET "//users/2" for ::1 at 2016-12-16 23:29:38 +0800
[active_model_serializers] Rendered ChapterSerializer with ActiveModelSerializers::Adapter::JsonApi (10.73ms)
  Parameters: {"id"=>"7"}
  Parameters: {"id"=>"6"}
Processing by UsersController#show as JSON
Completed 200 OK in 24ms (Views: 14.2ms | ActiveRecord: 0.9ms)


  User Load (0.8ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 21], ["LIMIT", 1]]
  User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 21], ["LIMIT", 1]]
  Parameters: {"id"=>"2"}
  Chapter Load (0.2ms)  SELECT  "chapters".* FROM "chapters" WHERE "chapters"."id" = ? LIMIT ?  [["id", 7], ["LIMIT", 1]]
  Chapter Load (0.2ms)  SELECT  "chapters".* FROM "chapters" WHERE "chapters"."id" = ? LIMIT ?  [["id", 6], ["LIMIT", 1]]
  User Load (0.7ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 21], ["LIMIT", 1]]
  Book Load (0.1ms)  SELECT  "books".* FROM "books" WHERE "books"."id" = ? LIMIT ?  [["id", 16], ["LIMIT", 1]]
  Book Load (0.1ms)  SELECT  "books".* FROM "books" WHERE "books"."id" = ? LIMIT ?  [["id", 16], ["LIMIT", 1]]
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
[active_model_serializers]   User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
[active_model_serializers]   User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  Role Load (0.2ms)  SELECT  "roles".* FROM "roles" WHERE "roles"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
[active_model_serializers] Rendered ChapterSerializer with ActiveModelSerializers::Adapter::JsonApi (6.75ms)
[active_model_serializers] Rendered ChapterSerializer with ActiveModelSerializers::Adapter::JsonApi (5.92ms)
Completed 403 Forbidden in 16ms (ActiveRecord: 1.0ms)


Completed 200 OK in 22ms (Views: 9.9ms | ActiveRecord: 1.6ms)


Completed 200 OK in 20ms (Views: 7.9ms | ActiveRecord: 1.0ms)



Pundit::NotAuthorizedError (not allowed to show? this #<User id: 2, first_name: "James", last_name: "Raynor", username: "Jimmy", email: "chewedon+jim@gmail.com", password_digest: "$2a$10$9xCQKiku7YD.xjzbj34/P.4JUHCOf4lKXbVeqKy2PNb...", banned: false, role_id: 3, created_at: "2016-12-01 13:56:30", updated_at: "2016-12-11 08:31:42", photo: "jim_raynor.jpg", email_confirmed: true, confirm_token: nil, password_reset_token: nil>):

app/controllers/users_controller.rb:33:in `show'

结果,Pundit正在筹集Pundit::NotAuthorizedError,因为它认为我试图访问用户的信息。

我的Emberjs前端合理地引起了这个例外:

Ember error

我的Chapter_Controller肯定没有明确要求提供作者的信息:

def show
  chapter = Chapter.find_by(id: params[:id])

  if chapter.present?
    authorize chapter
    render json: chapter
  else
    skip_authorization
    render status: :not_found
  end
end

我可以通过修改User政策show?方法来修复此错误,以便返回true

def show?
    true
end

我的show?方法目前是这样的:

def show?
    # Allowing admins to view other admins (but do not allow update or deleting other admins)
    if @user.superuser? || @record.id == @user.id || (@user.admin? && !@record.superuser?)
      return true
    elsif (@record.id != @user.id)
      return false
    end
end

但是这会将我的用户信息暴露给任何人看。例如,让我们说作者不想透露他们的真实姓名,只是他们的用户名(也许作者对他/她的书卖得不太自信,所以使用别名{{1}隐藏自己的身份。)

通过在我的用户策略show方法中指定username,任何已登录的用户都可以向true发出GET请求,并查看作者的详细信息。

所以我的问题是 - 有没有办法允许其他用户查看作者的书籍和书籍章节,但同时又不允许其他用户查看作者的个人信息?

更新

看来我的活动模型序列化程序是试图提取用户记录的那个。

我认为在活动模型序列化程序github页面上发生了类似的讨论:https://github.com/rails-api/active_model_serializers/issues/1552

更新2 - 章节政策显示方法

http://localhost:3000/users/{author_id}

def show? # superuser and admins should respect author's privacy # and not be able to view author's unpublished works owner? || @record.published end def owner? @record.book.author_id == @user.id end 属于ChapterBook属于Book

  

ChapterPolicy#show是如何定义的?该方法是否引用了   UserPolicy类? (如果是这样,请不要这样做!)

好的......如果我不检查当前登录的用户是否是作者,我还能如何限制未发布的章节仅对本书的作者可见?

如果有任何帮助,我的章节,书籍和用户的Active Model Serializer如下:

章:

User

图书:

class ChapterSerializer < ActiveModel::Serializer
  attributes :id, :title, :order, :content, :published, :picture
  attribute :content, if: :content_author?

  belongs_to :book

  def content_author?
    # ---------------------------------------------------------
    # Only author of the content can view their unpublished
    # chapter content. Other users including superuser, admin
    # and other normal users should not be able to view
    # author's unpublished chapter content, even during
    # admin/superuser updating author's chapter operation.
    #
    # We want to respect author's privacy and entitlement
    # to publishing their story whenever they feel it's ready.
    # ---------------------------------------------------------
    if current_user != object.book.author && !object.published
      return false
    else
      return true
    end
  end
end

用户:

class BookSerializer < ActiveModel::Serializer
  attributes :id, :title, :blurb, :adult_content, :published, :cover

  belongs_to :author, class_name: "User"
  has_many :chapters
  has_many :genres
  has_many :loves
end

1 个答案:

答案 0 :(得分:0)

您的问题是由于模型的相关性:

class ChapterSerializer < ActiveModel::Serializer
  belongs_to :book
end

class BookSerializer < ActiveModel::Serializer
  belongs_to :author, class_name: "User"
end

在您的前端应用程序的某个地方,您要求提供有关该书作者的信息 - 它不清楚这是什么信息,但是例如您可能正在查找&#34;其他本作者的书籍&#34;?

这会触发对/users/:id的GET请求,由于您403的实施,该请求会返回UserPolicy#show?

您的问题没有单一的答案,因为它有点是您需要做的架构设计问题/决定。但是,例如,可能的方法是允许用户始终可见:

class UserPolicy
  def show?
    true
  end
end

...在您的UserSerializer中,根据current_user有条件地定义返回哪些属性。