Active Model Serializer 和 Pundit 在 Show CRUD 操作期间删除记录

Active Model Serializer and Pundit deleting records during a Show CRUD action

好吧,这里有东西严重损坏...

我正在为我的 Rails 5 JSONAPI 服务器使用 Active Model Serializer 和 Pundit,为我的前端应用程序使用 Ember。

我有 User 用户模型的模型和 Pundit 策略,可防止非作者查看未发表的故事和章节。

目前,我看到一个奇怪的问题,如下所示:

1. UserA creates StoryA, and two published chapters Chapter1 and Chapter2
2. UserA then creates two unpublished chapters Chapter3 and Chapter4
3. UserA logouts
4. UserB logins
5. UserB views the same story created by UserA
6. Server policy kicks in and scope the results to only published chapters since UserB isn't the author.
7. * An SQL DELETE query is sent to delete the two unpublished stories for some odd reason.

这是一些屏幕截图:

用户 A 创建了 2 个已发布故事和 2 个未发布故事

数据库记录显示 4 个故事属于巨神峰故事

用户 A 注销,用户 B 登录,查看巨神峰故事

(如您所见,UserB 只看到了正确发布的两个章节,但是...)

由于某些奇怪的原因,未发布的章节已从数据库中删除

查看 Rails 控制台,我在 ChaptersController#show CRUD 操作期间看到 DELETE 查询:

Started GET "/stories/16" for 127.0.0.1 at 2017-11-05 17:02:53 +0800
Processing by StoriesController#show as JSON
  Parameters: {"id"=>"16"}
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  Story Load (0.1ms)  SELECT  "stories".* FROM "stories" WHERE "stories"."id" = ? LIMIT ?  [["id", 16], ["LIMIT", 1]]
  Chapter Load (0.3ms)  SELECT "chapters".* FROM "chapters" INNER JOIN "stories" ON "stories"."id" = "chapters"."story_id" INNER JOIN "users" ON "users"."id" = "stories"."user_id" WHERE "chapters"."story_id" = ? AND ((stories.published = 't' AND chapters.published = 't') OR stories.user_id = 2)  [["story_id", 16]]
  Chapter Load (0.1ms)  SELECT "chapters".* FROM "chapters" WHERE "chapters"."story_id" = ?  [["story_id", 16]]
   (0.1ms)  begin transaction
Started GET "/chapters/26" for 127.0.0.1 at 2017-11-05 17:02:53 +0800
  SQL (0.3ms)  DELETE FROM "chapters" WHERE "chapters"."id" = ?  [["id", 32]]
Started GET "/chapters/27" for 127.0.0.1 at 2017-11-05 17:02:53 +0800
Processing by ChaptersController#show as JSON
  SQL (0.1ms)  DELETE FROM "chapters" WHERE "chapters"."id" = ?  [["id", 33]]
Processing by ChaptersController#show as JSON
  Parameters: {"id"=>"26"}
   (2.1ms)  commit transaction
  Parameters: {"id"=>"27"}
  User Load (0.3ms)  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", 1], ["LIMIT", 1]]
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  Chapter Load (0.1ms)  SELECT  "chapters".* FROM "chapters" WHERE "chapters"."id" = ? LIMIT ?  [["id", 26], ["LIMIT", 1]]
[active_model_serializers]   Story Load (0.4ms)  SELECT "stories".* FROM "stories" WHERE "stories"."user_id" = ?  [["user_id", 1]]
  Chapter Load (0.2ms)  SELECT  "chapters".* FROM "chapters" WHERE "chapters"."id" = ? LIMIT ?  [["id", 27], ["LIMIT", 1]]
Started GET "/chapters/32" for 127.0.0.1 at 2017-11-05 17:02:53 +0800
Started GET "/chapters/33" for 127.0.0.1 at 2017-11-05 17:02:53 +0800
  Story Load (0.2ms)  SELECT  "stories".* FROM "stories" WHERE "stories"."id" = ? LIMIT ?  [["id", 16], ["LIMIT", 1]]
[active_model_serializers] Rendered StorySerializer with ActiveModelSerializers::Adapter::JsonApi (22.64ms)
  Story Load (0.1ms)  SELECT  "stories".* FROM "stories" WHERE "stories"."id" = ? LIMIT ?  [["id", 16], ["LIMIT", 1]]
Processing by ChaptersController#show as JSON
Processing by ChaptersController#show as JSON
[active_model_serializers] Rendered ChapterSerializer with ActiveModelSerializers::Adapter::JsonApi (0.82ms)
Completed 200 OK in 43ms (Views: 27.1ms | ActiveRecord: 3.9ms)

我的 ChaptersController show action 甚至没有 "delete" 或 "destroy" 这个词...所以记录是如何被删除的?

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

  if chapter.present?
    authorize chapter
    render json: chapter, status: :ok
  else
    skip_authorization
    render json: { error: "Chapter not found" }, status: :not_found
  end
end

我的章节政策展示方法:

# CHAPTER PUNDIT POLICY
def show?
  (@record.published? && @record.story.published?) || (@record.story.user == @user)
end

我的 StoriesController Show 动作看起来像:

# STORIES CONTROLLER
def show
  story = Story.find_by(id: params[:id])

  if story.present?
    authorize story
    story.chapters = policy_scope(story.chapters)
    render json: story, include: [:user, :chapters], status: :ok
  else
    skip_authorization
    render json: { errors: "Story not found" }, status: :not_found
  end
end

我认为这可能是 Ember 在幕后做了一些有趣的额外查询,但我使用 Postman Mac 应用程序来测试查看故事,果然,未发布的章节没有经过就被删除了Ember 完全没有。由于某些奇怪的原因,它发生在服务器端=/

有什么想法吗?

在我看来,这实际上是在 StoriesController#show 中完成的。您应该再次尝试仅测试此方法(在 RSpec 或其他内容中),并且您还可以单步执行代码以查明删除发生的确切位置。我的猜测是:

story.chapters = policy_scope(story.chapters)

您正在更改此处的故事章节。大概你想做类似

的事情
@displayed_chapters = policy_scope(story.chapters)

没有任何内容可以作为 DELETE 的可能来源。我认为解决方案是一些好的、老式的调试。

  • 正在尝试注释掉操作中的所有行。然后一一取消注释。
  • 查找日志中提到的交易
  • 订阅 sql 查询并在删除查询时引发并查看堆栈跟踪。或者完全覆盖删除。

如果删除是在序列化程序或策略中发生的,那么是用户空间代码中的某些东西使它发生了。两个图书馆都不知道活动记录。