Skip to content
This repository has been archived by the owner on Jan 30, 2020. It is now read-only.

Refactoring #26

Merged
merged 3 commits into from
Oct 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions lib/graphql-pundit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@

require 'graphql'

# Define `authorize` and `authorize!` helpers
# Defines authorization related helpers
module GraphQL
# rubocop:disable Metrics/MethodLength
def self.assign_authorize(raise_unauthorized)
# rubocop:enable Metrics/MethodLength
lambda do |defn, query = nil, policy: nil, record: nil|
# Defines `authorize` and `authorize!` helpers
class AuthorizationHelper
attr_reader :raise_unauthorized

def initialize(raise_unauthorized)
@raise_unauthorized = raise_unauthorized
end

def call(defn, query = nil, policy: nil, record: nil)
opts = {record: record,
query: query || defn.name,
policy: policy,
Expand All @@ -23,14 +28,15 @@ def self.assign_authorize(raise_unauthorized)
end
end

def self.assign_scope
lambda do |defn, proc = :infer_scope|
# Defines `scope` helper
class ScopeHelper
def call(defn, proc = :infer_scope)
Define::InstanceDefinable::AssignMetadataKey.new(:scope).
call(defn, proc)
end
end

Field.accepts_definitions(authorize: assign_authorize(false),
authorize!: assign_authorize(true),
scope: assign_scope)
Field.accepts_definitions(authorize: AuthorizationHelper.new(false),
authorize!: AuthorizationHelper.new(true),
scope: ScopeHelper.new)
end
95 changes: 54 additions & 41 deletions lib/graphql-pundit/instrumenters/authorization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,55 @@ module Pundit
module Instrumenters
# Instrumenter that supplies `authorize`
class Authorization
# This does the actual Pundit authorization
class AuthorizationResolver
attr_reader :current_user, :old_resolver, :options
def initialize(current_user, old_resolver, options)
@current_user = current_user
@old_resolver = old_resolver
@options = options
end

def call(root, arguments, context)
unless authorize(root, arguments, context)
raise ::Pundit::NotAuthorizedError
end
old_resolver.call(root, arguments, context)
rescue ::Pundit::NotAuthorizedError
if options[:raise]
raise GraphQL::ExecutionError, "You're not authorized to do this"
end
end

private

def authorize(root, arguments, context)
if options[:proc]
options[:proc].call(root, arguments, context)
else
record = record(root, arguments, context)
::Pundit::PolicyFinder.new(policy(record)).policy!.
new(context[current_user], record).public_send(query)
end
end

def query
@query ||= options[:query].to_s + '?'
end

def policy(record)
options[:policy] || record
end

def record(root, arguments, context)
if options[:record].respond_to?(:call)
options[:record].call(root, arguments, context)
else
options[:record] || root
end
end
end

attr_reader :current_user

def initialize(current_user = :current_user)
Expand All @@ -15,48 +64,12 @@ def initialize(current_user = :current_user)

def instrument(_type, field)
return field unless field.metadata[:authorize]
old_resolve = field.resolve_proc
resolve_proc = resolve_proc(current_user,
old_resolve,
field.metadata[:authorize])
old_resolver = field.resolve_proc
resolver = AuthorizationResolver.new(current_user,
old_resolver,
field.metadata[:authorize])
field.redefine do
resolve resolve_proc
end
end

# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/BlockLength
def resolve_proc(current_user, old_resolve, options)
lambda do |obj, args, ctx|
begin
result = if options[:proc]
options[:proc].call(obj, args, ctx)
else
query = options[:query].to_s + '?'
record = if options[:record].respond_to?(:call)
options[:record].call(obj, args, ctx)
else
options[:record] || obj
end
policy = options[:policy] || record
policy = ::Pundit::PolicyFinder.new(policy).policy!
policy = policy.new(ctx[current_user], record)
policy.public_send(query)
end
unless result
raise ::Pundit::NotAuthorizedError, query: query,
record: record,
policy: policy
end
old_resolve.call(obj, args, ctx)
rescue ::Pundit::NotAuthorizedError
if options[:raise]
raise GraphQL::ExecutionError,
"You're not authorized to do this"
end
end
resolve resolver
end
end
end
Expand Down
77 changes: 47 additions & 30 deletions lib/graphql-pundit/instrumenters/scope.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,69 @@ module Pundit
module Instrumenters
# Instrumenter that supplies `scope`
class Scope
attr_reader :current_user
# Applies the scoping to the passed object
class ScopeResolver
attr_reader :current_user, :scope, :old_resolver

def initialize(current_user = :current_user)
@current_user = current_user
end
def initialize(current_user, scope, old_resolver)
@current_user = current_user
@old_resolver = old_resolver

# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
def instrument(_type, field)
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
scope = field.metadata[:scope]
return field unless scope
unless valid_value?(scope)
raise ArgumentError, 'Invalid value passed to `scope`'
unless valid_value?(scope)
raise ArgumentError, 'Invalid value passed to `scope`'
end

@scope = new_scope(scope)
end

def call(root, arguments, context)
new_scope = scope.call(root, arguments, context)
old_resolver.call(new_scope, arguments, context)
end

old_resolve = field.resolve_proc
private

scope_proc = lambda do |obj, _args, ctx|
unless inferred?(scope)
obj.define_singleton_method(:policy_class) { scope }
def new_scope(scope)
return scope if proc?(scope)

lambda do |root, _arguments, context|
unless inferred?(scope)
root.define_singleton_method(:policy_class) { scope }
end

::Pundit.policy_scope!(context[current_user], root)
end
end

::Pundit.policy_scope!(ctx[current_user], obj)
def valid_value?(value)
value.is_a?(Class) || inferred?(value) || proc?(value)
end
scope_proc = scope if proc?(scope)

field.redefine do
resolve(lambda do |obj, args, ctx|
new_scope = scope_proc.call(obj, args, ctx)
old_resolve.call(new_scope, args, ctx)
end)
def proc?(value)
value.respond_to?(:call)
end

def inferred?(value)
value == :infer_scope
end
end

private
attr_reader :current_user

def valid_value?(value)
value.is_a?(Class) || inferred?(value) || proc?(value)
def initialize(current_user = :current_user)
@current_user = current_user
end

def proc?(value)
value.respond_to?(:call)
end
def instrument(_type, field)
scope = field.metadata[:scope]
return field unless scope

def inferred?(value)
value == :infer_scope
old_resolver = field.resolve_proc
resolver = ScopeResolver.new(current_user, scope, old_resolver)

field.redefine do
resolve resolver
end
end
end
end
Expand Down