dimanche 5 novembre 2017

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

Okay, something is seriously broken here...

I am using Active Model Serializer and Pundit for my Rails 5 JSONAPI server and Ember for my frontend application.

I have User model and Pundit policy for User model which prevent non-authors from viewing unpublished stories and chapters.

At the moment, I am seeing a weird problem which goes like this:

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.

Here's some screenshot:

UserA creates 2 published stories and 2 unpublished stories

screenshot 1

Database record shows 4 stories belong to Mount Targon story

screenshot 2

UserA logs out and UserB logs in, views Mount Targon story

screenshot 3

(As you can see, UserB only sees the two published chapters which is correct but...)

The unpublished chapters are deleted from the database for some odd reason

screenshot 4

Looking at the Rails console, I see the DELETE query during a ChaptersController#show CRUD action:

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)

My ChaptersController show action does not even have the word "delete" or "destroy" in it...so how does the record get deleted?

# 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

My Chapter Policy show method:

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

My StoriesController Show action looks like:

# 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

I thought it might be Ember doing some funny extra query behind the scenes but I use Postman Mac application to test viewing the story and sure enough, the unpublished chapters are deleted without going through Ember at all. It's happening server side for some odd reason =/

Any ideas?




Aucun commentaire:

Enregistrer un commentaire