Rails Pundit policy_scope 渲染 json 包含字段

Rails Pundit policy_scope on render json include fields

继续我之前的问题:

我遇到这样一种情况,User 应该无法查看另一个用户的 未发布的 属于 Story 作者创建的章节。

例如如果 UserA 创建了一个名为 Targon 的故事并提供了 2 个已发布的章节和 2 个未发布的章节,那么 UserB 应该只能看到 Targon 个故事的已发布章节。

通常使用 Pundit 策略范围界定,它会界定 index CRUD 操作的范围。

然而,我需要确定范围的是 Chapters 在呈现 json 行期间属于 Story

render json: story, include: [:user, :chapters], status: :ok

我试过:

# ---------------------------------------------------------------------------
# ActiveRecord auto-save will kick in and delete all unpublished chapters
# ---------------------------------------------------------------------------
story.chapters = policy_scope(story.chapters)

render json: story, include: [:user, :chapters], status: :ok

根据https://gist.github.com/demisx/9896113(has_many部分)当我重新分配story.chapters时,上面的代码将删除属于Targon的所有未发布的章节:

story.chapters = policy_scope(story.chapters) # BAD

我希望有一些方法可以做这样的事情:

render json: story, include: [:user, policy_scope(:chapters)], status: :ok

目前,如果不限定 story.chapters 范围,任何使用 ID 16 (Targon) 获取 Story 的用户都将返回 JSONAPI:

{
    "data": {
        "id": "16",
        "type": "stories",
        "attributes": {
            "title": "Mount Targon",
            "summary": "Mount Targon is the mightiest peak in Runeterra, a towering peak of sun-baked rock amid a range of summits unmatched in scale anywhere else in the world. Located far from civilization, Mount Targon is utterly remote and all but impossible to reach save by the most determined seeker. Many legends cling to Mount Targon, and, like any place of myth, it is a beacon to dreamers, madmen and questors of adventure. Some of these brave souls attempt to scale the impossible mountain, perhaps seeking wisdom or enlightenment, perhaps chasing glory or some soul-deep yearning to witness its summit. The ascent is all but impossible, and those hardy few who somehow survive to reach the top almost never speak of what they have seen. Some return with a haunted, empty look in their eyes, others changed beyond all recognition, imbued by an Aspect of unearthly, inhuman power with a destiny few mortals can comprehend.",
            "published": true,
            "published-date": "2017-11-02T10:35:33.184Z",
            "created-at": "2017-11-02T10:35:33.184Z",
            "updated-at": "2017-11-04T07:35:04.083Z",
            "cover": {
                "url": "http://res.cloudinary.com/chewedon/image/upload/v1509780931/c8ubn3tfivxziyxwynsa.png",
                "standard": {
                    "url": "http://res.cloudinary.com/chewedon/image/upload/c_fill,g_north,h_300,w_200/c8ubn3tfivxziyxwynsa.png"
                }
            }
        },
        "relationships": {
            "user": {
                "data": {
                    "id": "1",
                    "type": "users"
                }
            },
            "chapters": {
                "data": [{
                    "id": "26",
                    "type": "chapters"
                }, {
                    "id": "27",
                    "type": "chapters"
                }, {
                    "id": "37",
                    "type": "chapters"
                }, {
                    "id": "38",
                    "type": "chapters"
                }]
            }
        }
    },
    "included": [{
        "id": "1",
        "type": "users",
        "attributes": {
            "username": "Chewedon",
            "photo": {
                "url": "http://res.cloudinary.com/chewedon/image/upload/v1509857442/nx1tqlcdxrhz6r3kjx87.jpg",
                "standard": {
                    "url": "http://res.cloudinary.com/chewedon/image/upload/c_fill,g_north,h_150,w_150/nx1tqlcdxrhz6r3kjx87.jpg"
                }
            }
        },
        "relationships": {
            "stories": {
                "data": [{
                    "id": "1",
                    "type": "stories"
                }, {
                    "id": "2",
                    "type": "stories"
                }, {
                    "id": "3",
                    "type": "stories"
                }, {
                    "id": "4",
                    "type": "stories"
                }, {
                    "id": "5",
                    "type": "stories"
                }, {
                    "id": "6",
                    "type": "stories"
                }, {
                    "id": "8",
                    "type": "stories"
                }, {
                    "id": "9",
                    "type": "stories"
                }, {
                    "id": "10",
                    "type": "stories"
                }, {
                    "id": "11",
                    "type": "stories"
                }, {
                    "id": "12",
                    "type": "stories"
                }, {
                    "id": "13",
                    "type": "stories"
                }, {
                    "id": "14",
                    "type": "stories"
                }, {
                    "id": "15",
                    "type": "stories"
                }, {
                    "id": "16",
                    "type": "stories"
                }]
            }
        }
    }]
}

在关系部分,章节 3738 未发布,导致我的 Ember 前端出现 403 Forbidden。

理想情况下,服务器应该在返回记录之前确定这些字段的范围,但由于我在上面和我之前的 Whosebug 问题中描述的错误,我一直在研究如何使用 Pundit 确定包含字段的范围。

有什么想法吗?

感谢来自上一个链接问题的用户 oowowaee,他建议覆盖 Story 序列化程序的 chapters 字段(我不知道你可以这样做),代码现在可以工作并且记录不会从数据库中删除。

class StorySerializer < ActiveModel::Serializer
  include Pundit

  attributes :id, :title, :summary, :published, :published_date, :created_at, :updated_at, :cover

  belongs_to :user
  has_many :chapters

  # ------------------------------------------------------------------------
  # Note: need to use 'object.chapters' not 'self.chapters` below.
  # ------------------------------------------------------------------------
  def chapters
    policy_scope(object.chapters)
  end
end