module JSONAPI
module Authorization
# An authorizer is a class responsible for linking JSONAPI operations to
# your choice of authorization mechanism.
#
# This class uses Pundit for authorization. It does not yet support all
# the available operations — you can use your own authorizer class instead
# if you have different needs. See the README.md for configuration
# information.
#
# Fetching records is the concern of +PunditScopedResource+ which in turn
# affects which records end up being passed here.
class DefaultPunditAuthorizer
attr_reader :user
# Creates a new DefaultPunditAuthorizer instance
#
# ==== Parameters
#
# * +context+ - The context passed down from the controller layer
def initialize(context)
@user = context[:user]
end
# GET /resources
#
# ==== Parameters
#
# * +source_class+ - The source class (e.g. +Article+ for +ArticleResource+)
def find(source_class)
::Pundit.authorize(user, source_class, 'index?')
end
# GET /resources/:id
#
# ==== Parameters
#
# * +source_record+ - The record to show
def show(source_record)
::Pundit.authorize(user, source_record, 'show?')
end
# GET /resources/:id/relationships/other-resources
# GET /resources/:id/relationships/another-resource
#
# A query for a +has_one+ or a +has_many+ association
#
# ==== Parameters
#
# * +source_record+ - The record whose relationship is queried
# * +related_record+ - The associated +has_one+ record to show or +nil+
# if the associated record was not found. For a +has_many+ association,
# this will always be +nil+
def show_relationship(source_record, related_record)
::Pundit.authorize(user, source_record, 'show?')
::Pundit.authorize(user, related_record, 'show?') unless related_record.nil?
end
# GET /resources/:id/another-resource
#
# A query for a record through a +has_one+ association
#
# ==== Parameters
#
# * +source_record+ - The record whose relationship is queried
# * +related_record+ - The associated record to show or +nil+ if the
# associated record was not found
def show_related_resource(source_record, related_record)
::Pundit.authorize(user, source_record, 'show?')
::Pundit.authorize(user, related_record, 'show?') unless related_record.nil?
end
# GET /resources/:id/other-resources
#
# A query for records through a +has_many+ association
#
# ==== Parameters
#
# * +source_record+ - The record whose relationship is queried
def show_related_resources(source_record)
::Pundit.authorize(user, source_record, 'show?')
end
# PATCH /resources/:id
#
# ==== Parameters
#
# * +source_record+ - The record to be modified
# * +new_related_records+ - An array of records to be associated to the
# +source_record+. This will contain the records specified in the
# "relationships" key in the request
#--
# TODO: Should probably take old records as well
def replace_fields(source_record, new_related_records)
::Pundit.authorize(user, source_record, 'update?')
new_related_records.each do |record|
::Pundit.authorize(user, record, 'update?')
end
end
# POST /resources
#
# ==== Parameters
#
# * +source_class+ - The class of the record to be created
# * +related_records+ - An array of records to be associated to the new
# record. This will contain the records specified in the
# "relationships" key in the request
def create_resource(source_class, related_records)
::Pundit.authorize(user, source_class, 'create?')
related_records.each do |record|
::Pundit.authorize(user, record, 'update?')
end
end
# DELETE /resources/:id
#
# ==== Parameters
#
# * +source_record+ - The record to be removed
def remove_resource(source_record)
::Pundit.authorize(user, source_record, 'destroy?')
end
# PATCH /resources/:id/relationships/another-resource
#
# A replace request for a +has_one+ association
#
# ==== Parameters
#
# * +source_record+ - The record whose relationship is modified
# * +old_related_record+ - The current associated record
# * +new_related_record+ - The new record replacing the +old_record+
# association, or +nil+ if the association is to be cleared
def replace_to_one_relationship(source_record, old_related_record, new_related_record)
raise NotImplementedError
end
# POST /resources/:id/relationships/other-resources
#
# A request for adding to a +has_many+ association
#
# ==== Parameters
#
# * +source_record+ - The record whose relationship is modified
# * +new_related_records+ - The new records to be added to the association
def create_to_many_relationship(source_record, new_related_records)
raise NotImplementedError
end
# PATCH /resources/:id/relationships/other-resources
#
# A replace request for a +has_many+ association
#
# ==== Parameters
#
# * +source_record+ - The record whose relationship is modified
# * +new_related_records+ - The new records replacing the entire +has_many+
# association
#--
# TODO: Should probably take old records as well
def replace_to_many_relationship(source_record, new_related_records)
raise NotImplementedError
end
# DELETE /resources/:id/relationships/other-resources
#
# A request to deassociate elements of a +has_many+ association
#
# NOTE: this is called once per related record, not all at once
#
# ==== Parameters
#
# * +source_record+ - The record whose relationship is modified
# * +related_record+ - The record which will be deassociatied from +source_record+
def remove_to_many_relationship(source_record, related_record)
raise NotImplementedError
end
# DELETE /resources/:id/relationships/another-resource
#
# A request to deassociate a +has_one+ association
#
# ==== Parameters
#
# * +source_record+ - The record whose relationship is modified
# * +related_record+ - The record which will be deassociatied from +source_record+
def remove_to_one_relationship(source_record, related_record)
raise NotImplementedError
end
end
end
end