-
Notifications
You must be signed in to change notification settings - Fork 36
/
guard.rb
100 lines (79 loc) · 2.84 KB
/
guard.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# frozen_string_literal: true
require "graphql"
require "graphql/guard/version"
module GraphQL
class Guard
NotAuthorizedError = Class.new(StandardError)
ANY_FIELD_NAME = :'*'
DEFAULT_NOT_AUTHORIZED = ->(type, field) do
raise NotAuthorizedError.new("Not authorized to access: #{type}.#{field}")
end
MASKING_FILTER = ->(schema_member, ctx) do
mask = schema_member.graphql_definition.metadata[:mask]
mask ? mask.call(ctx) : true
end
attr_reader :policy_object, :not_authorized
def initialize(policy_object: nil, not_authorized: DEFAULT_NOT_AUTHORIZED)
@policy_object = policy_object
@not_authorized = not_authorized
end
def use(schema_definition)
if schema_definition.interpreter?
schema_definition.tracer(self)
else
raise "Please use the graphql gem version >= 1.10 with GraphQL::Execution::Interpreter"
end
add_schema_masking!(schema_definition)
end
def trace(event, trace_data)
if event == 'execute_field'
ensure_guarded(trace_data) { yield }
else
yield
end
end
def find_guard_proc(type, field)
return unless type.respond_to?(:type_class)
inline_guard(field) ||
policy_object_guard(type.type_class, field.name.to_sym) ||
inline_guard(type) ||
policy_object_guard(type.type_class, ANY_FIELD_NAME)
end
private
def add_schema_masking!(schema_definition)
schema_definition.class_eval do
def self.default_filter
GraphQL::Filter.new(except: default_mask).merge(only: MASKING_FILTER)
end
end
end
def ensure_guarded(trace_data)
field = trace_data[:field]
guard_proc = find_guard_proc(field.owner, field)
return yield unless guard_proc
if guard_proc.call(trace_data[:object], args(trace_data), trace_data[:query].context)
yield
else
not_authorized.call(field.owner.graphql_definition, field.name.to_sym)
end
end
def args(trace_data)
if trace_data[:arguments].key?(:input) && !trace_data[:arguments][:input].is_a?(Hash)
return trace_data[:arguments][:input] # Relay mutation input
end
trace_data[:arguments]
end
def policy_object_guard(type, field_name)
@policy_object && @policy_object.guard(type, field_name)
end
def inline_guard(type_or_field)
type_or_field.graphql_definition.metadata[:guard]
end
end
end
GraphQL::ObjectType.accepts_definitions(guard: GraphQL::Define.assign_metadata_key(:guard))
GraphQL::Field.accepts_definitions(guard: GraphQL::Define.assign_metadata_key(:guard))
GraphQL::Field.accepts_definitions(mask: GraphQL::Define.assign_metadata_key(:mask))
GraphQL::Schema::Object.accepts_definition(:guard)
GraphQL::Schema::Field.accepts_definition(:guard)
GraphQL::Schema::Field.accepts_definition(:mask)