Okay, so I have a User, Book and Chapter entities in my system.
If an author (User entity) publishes a book as well as a chapter, then it's available for the public to see. Let's call author Jim.
That means if another normal user, named Tycus, wants to read Jim's book and book chapters, he should be able to do so.
I am using Pundit gem (https://github.com/elabs/pundit) for permissions.
The problem I am facing is, when Tycus tries to access Jim's book, it appears my Rails is trying to fetch linked relationships (chapter --> book --> author) along with it:
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: "[email protected]", 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'
As a result, Pundit is raising a Pundit::NotAuthorizedError because somehow it thinks I'm trying to access the user's information.
My Emberjs frontend rightfully resonate with this exception raised:
My Chapter_Controller certainly don't explicitly ask for the author's info:
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
I can fix this error by modify my User policy show? method to return true:
def show?
    true
end
My show? method is currently like this:
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? && [email protected]?)
      return true
    elsif (@record.id != @user.id)
      return false
    end
end
But this then exposes my user's information to anyone to see. For example, let's say an author does not want to disclose their real name, only their username (maybe the author isn't too confident his/her book will sell well, so using an alias username to hide their identity).
By specifying true in my User policy show method, any logged in user can make a GET request to: http://localhost:3000/users/{author_id} and see the author's details.
So my question is - is there a way to allow other user to view book and book chapters of an author but at the same time do not allow other users to view the author's personal info?
Update
It appears my active model serializer is the one trying to pull the user record.
I think a similar discussion is happening on the active model serializer github pages: https://github.com/rails-api/active_model_serializers/issues/1552
Update 2 - Chapter Policy Show Method
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
Chapter belongs to a Book and a Book belongs to a User.
How is ChapterPolicy#show defined? Is that method referencing the UserPolicy class? (If so, don't do that!)
Okay...how else can I limit the unpublished chapters to be visible only to the author of the book if I don't check if the currently logged in user is the author?
In case it's any help, my Active Model Serializer for Chapter, Book and User are as follow:
Chapter:
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
Book:
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
User:
class UserSerializer < ActiveModel::Serializer
  attributes :id, :first_name, :last_name, :username, :email, :banned, :photo
  belongs_to :role
  has_many :friends
end
				
                        
Your issue is that due to how your models are related:
Somewhere in your front-end application, you are requesting information about the book's author - it's unclear exactly what information this is, but for example you could perhaps be looking up "other books by this author"?
This is triggering a GET request to
/users/:id, which returns a403error due to your implementation ofUserPolicy#show?.There is no single answer to your problem, as it is somewhat an architectural design issue/decision that you need to make. But for example, possible approach would be to allow users to always be viewable:
...And in your
UserSerializer, conditionally define which attributes get returned, based upon thecurrent_user.