Skip to content

Commit

Permalink
Fix compatibility with GraphQL::Schema::RelayClassicMutation
Browse files Browse the repository at this point in the history
  • Loading branch information
exAspArk committed Apr 20, 2020
1 parent aba85fc commit 3adf425
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 5 deletions.
18 changes: 14 additions & 4 deletions lib/graphql/guard.rb
Expand Up @@ -44,10 +44,12 @@ def trace(event, trace_data)
end

def find_guard_proc(type, field)
return unless type.respond_to?(:type_class)

inline_guard(field) ||
policy_object_guard(type, field.name.to_sym) ||
policy_object_guard(type.type_class, field.name.to_sym) ||
inline_guard(type) ||
policy_object_guard(type, ANY_FIELD_NAME)
policy_object_guard(type.type_class, ANY_FIELD_NAME)
end

private
Expand All @@ -65,15 +67,23 @@ def ensure_guarded(trace_data)
guard_proc = find_guard_proc(field.owner, field)
return yield unless guard_proc

if guard_proc.call(trace_data[:object], trace_data[:arguments], trace_data[:query].context)
if guard_proc.call(trace_data[:object], args(trace_data), trace_data[:query].context)
yield
else
not_authorized.call(field.owner, 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.type_class, field_name)
@policy_object && @policy_object.guard(type, field_name)
end

def inline_guard(type_or_field)
Expand Down
20 changes: 20 additions & 0 deletions spec/fixtures/inline_schema.rb
Expand Up @@ -27,10 +27,30 @@ def posts_with_mask(user_id:)
end
end

class BaseMutationType < GraphQL::Schema::RelayClassicMutation
end

class CreatePostMutation < BaseMutationType
null true
argument :user_id, ID, required: true
field :post, PostType, null: true

def resolve(user_id:)
{post: Post.new(user_id: user_id)}
end
end

class MutationType < GraphQL::Schema::Object
field :create_post, mutation: CreatePostMutation do
guard ->(_obj, args, ctx) { args[:user_id] == ctx[:current_user].id }
end
end

class Schema < GraphQL::Schema
use GraphQL::Execution::Interpreter
use GraphQL::Analysis::AST
query QueryType
mutation MutationType
use GraphQL::Guard.new
end

Expand Down
21 changes: 21 additions & 0 deletions spec/fixtures/policy_object_schema.rb
Expand Up @@ -16,13 +16,33 @@ def posts(user_id:)
end
end

class BaseMutationType < GraphQL::Schema::RelayClassicMutation
end

class CreatePostMutation < BaseMutationType
null true
argument :user_id, ID, required: true
field :post, PostType, null: true

def resolve(user_id:)
{post: Post.new(user_id: user_id)}
end
end

class MutationType < GraphQL::Schema::Object
field :create_post, mutation: CreatePostMutation
end

class GraphqlPolicy
RULES = {
QueryType => {
posts: ->(_obj, args, ctx) { args[:user_id] == ctx[:current_user].id }
},
PostType => {
'*': ->(_post, args, ctx) { ctx[:current_user].admin? }
},
MutationType => {
createPost: ->(_obj, args, ctx) { args[:user_id] == ctx[:current_user].id }
}
}

Expand All @@ -35,6 +55,7 @@ class Schema < GraphQL::Schema
use GraphQL::Execution::Interpreter
use GraphQL::Analysis::AST
query QueryType
mutation MutationType
use GraphQL::Guard.new(policy_object: GraphqlPolicy)
end
end
3 changes: 2 additions & 1 deletion spec/fixtures/user.rb
Expand Up @@ -2,10 +2,11 @@

class User
ADMIN_ROLE = 'admin'
NOT_ADMIN_ROLE = 'not_admin'

attr_accessor :id, :role

def initialize(id:, role:)
def initialize(id:, role: NOT_ADMIN_ROLE)
self.id = id
self.role = role
end
Expand Down
36 changes: 36 additions & 0 deletions spec/graphql/guard_spec.rb
Expand Up @@ -49,6 +49,24 @@
])
expect(result['data']).to eq(nil)
end

it 'authorizes to execute a mutation' do
user = User.new(id: '1', role: 'admin')
query = "mutation($userId: ID!) { createPost(input: {userId: $userId}) { post { id title } } }"

result = Inline::Schema.execute(query, variables: {userId: user.id}, context: {current_user: user})

expect(result).to eq({"data" => {"createPost" => {"post" => {"id" => "1", "title" => "Post Title"}}}})
end

it 'does not authorize to execute a mutation' do
user = User.new(id: '1')
query = "mutation($userId: ID!) { createPost(input: {userId: $userId}) { post { id title } } }"

expect {
Inline::Schema.execute(query, variables: {userId: 2}, context: {current_user: user})
}.to raise_error(GraphQL::Guard::NotAuthorizedError, 'Not authorized to access: Mutation.createPost')
end
end

context 'inline mask' do
Expand Down Expand Up @@ -103,6 +121,24 @@
PolicyObject::Schema.execute(query, variables: {userId: 1}, context: {current_user: user})
}.to raise_error(GraphQL::Guard::NotAuthorizedError, 'Not authorized to access: Post.id')
end

it 'authorizes to execute a mutation' do
user = User.new(id: '1', role: 'admin')
query = "mutation($userId: ID!) { createPost(input: {userId: $userId}) { post { id title } } }"

result = PolicyObject::Schema.execute(query, variables: {userId: user.id}, context: {current_user: user})

expect(result).to eq({"data" => {"createPost" => {"post" => {"id" => "1", "title" => "Post Title"}}}})
end

it 'does not authorize to execute a mutation' do
user = User.new(id: '1')
query = "mutation($userId: ID!) { createPost(input: {userId: $userId}) { post { id title } } }"

expect {
PolicyObject::Schema.execute(query, variables: {userId: 2}, context: {current_user: user})
}.to raise_error(GraphQL::Guard::NotAuthorizedError, 'Not authorized to access: Mutation.createPost')
end
end

context 'schema without interpreter' do
Expand Down

0 comments on commit 3adf425

Please sign in to comment.