diff --git a/.travis.yml b/.travis.yml index 35aca92..3c7589c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,11 @@ sudo: false language: ruby -before_install: gem install bundler -v 1.17.3 -matrix: - include: - - gemfile: graphql-1.7.gemfile - env: GRAPHQL_RUBY_VERSION=1_7 CI=true - rvm: 2.3.8 - - gemfile: graphql-latest.gemfile - env: GRAPHQL_RUBY_VERSION=LATEST CI=true - rvm: 2.3.8 - - gemfile: graphql-1.7.gemfile - env: GRAPHQL_RUBY_VERSION=1_7 CI=true - rvm: 2.4.5 - - gemfile: graphql-latest.gemfile - env: GRAPHQL_RUBY_VERSION=LATEST CI=true - rvm: 2.4.5 - - gemfile: graphql-1.7.gemfile - env: GRAPHQL_RUBY_VERSION=1_7 CI=true - rvm: 2.5.7 - - gemfile: graphql-latest.gemfile - env: GRAPHQL_RUBY_VERSION=LATEST CI=true - rvm: 2.5.7 - - gemfile: graphql-1.7.gemfile - env: GRAPHQL_RUBY_VERSION=1_7 CI=true - rvm: 2.6.5 - - gemfile: graphql-latest.gemfile - env: GRAPHQL_RUBY_VERSION=LATEST CI=true - rvm: 2.6.5 +before_install: gem install bundler -v 2.1.4 +rvm: + - 2.3.8 + - 2.4.9 + - 2.5.7 + - 2.6.5 + - 2.7.0 +env: + - CI=true diff --git a/Gemfile b/Gemfile index ade3400..e041f73 100644 --- a/Gemfile +++ b/Gemfile @@ -1,8 +1,9 @@ source "https://rubygems.org" -gem "pry" - gem "graphql", "~> 1.10" +gem "pry" +gem 'coveralls' + # Specify your gem's dependencies in graphql-guard.gemspec gemspec diff --git a/README.md b/README.md index df19715..2dc0757 100644 --- a/README.md +++ b/README.md @@ -36,30 +36,31 @@ Define a GraphQL schema: ```ruby # Define a type -PostType = GraphQL::ObjectType.define do - name "Post" - - field :id, !types.ID - field :title, types.String +class PostType < GraphQL::Schema::Object + field :id, ID, null: false + field :title, String, null: true end # Define a query -QueryType = GraphQL::ObjectType.define do - name "Query" +class QueryType < GraphQL::Schema::Object + field :posts, [PostType], null: false do + argument :user_id, ID, required: true + end - field :posts, !types[!PostType] do - argument :user_id, !types.ID - resolve ->(obj, args, ctx) { Post.where(user_id: args[:user_id]) } + def posts(user_id:) + Post.where(user_id: user_id) end end # Define a schema -Schema = GraphQL::Schema.define do +class Schema < GraphQL::Schema + use GraphQL::Execution::Interpreter + use GraphQL::Analysis::AST query QueryType end # Execute query -Schema.execute(query, variables: { user_id: 1 }, context: { current_user: current_user }) +Schema.execute(query, variables: { userId: 1 }, context: { current_user: current_user }) ``` ### Inline policies @@ -67,7 +68,9 @@ Schema.execute(query, variables: { user_id: 1 }, context: { current_user: curren Add `GraphQL::Guard` to your schema:
-Schema = GraphQL::Schema.define do +class Schema < GraphQL::Schema + use GraphQL::Execution::Interpreter + use GraphQL::Analysis::AST query QueryType use GraphQL::Guard.new end @@ -76,22 +79,19 @@ end Now you can define `guard` for a field, which will check permissions before resolving the field:-QueryType = GraphQL::ObjectType.define do - name "Query" - - field :posts, !types[!PostType] do - argument :user_id, !types.ID +class QueryType < GraphQL::Schema::Object + field :posts, [PostType], null: false do + argument :user_id, ID, required: true guard ->(obj, args, ctx) { args[:user_id] == ctx[:current_user].id } - ... end + ... endYou can also define `guard`, which will be executed for every `*` field in the type:-PostType = GraphQL::ObjectType.define do - name "Post" +class PostType < GraphQL::Schema::Object guard ->(obj, args, ctx) { ctx[:current_user].admin? } ... end @@ -120,27 +120,12 @@ class GraphqlPolicy end-With `graphql-ruby` gem version >= 1.8 and class-based type definitions, use `camelCased` field names in the policy object. -You'd also need to use `type.metadata` (related to [rmosolgo/graphql-ruby#1429](https://github.com/rmosolgo/graphql-ruby/issues/1429)) to get the type class: - --class GraphqlPolicy - RULES = { - MutationType => { - createPost: ->(obj, args, cts) { ctx[:current_user].admin? } - } - } - - def self.guard(type, field) - RULES.dig(type.metadata[:type_class], field) - end -end -- Pass this object to `GraphQL::Guard`:-Schema = GraphQL::Schema.define do +class Schema < GraphQL::Schema + use GraphQL::Execution::Interpreter + use GraphQL::Analysis::AST query QueryType use GraphQL::Guard.new(policy_object: GraphqlPolicy) end @@ -167,8 +152,8 @@ end class GraphqlPolicy RULES = { PostType => { - '*': ->(obj, args, ctx) { ctx[:current_user].admin? }, # <=== 4 - title: ->(obj, args, ctx) { ctx[:current_user].admin? } # <=== 2 + '*': ->(obj, args, ctx) { ctx[:current_user].admin? }, # <=== 4 + title: ->(obj, args, ctx) { ctx[:current_user].admin? } # <=== 2 } } @@ -177,13 +162,14 @@ class GraphqlPolicy end end -PostType = GraphQL::ObjectType.define do - name "Post" - guard ->(obj, args, ctx) { ctx[:current_user].admin? } # <=== 3 - field :title, !types.String, guard: ->(obj, args, ctx) { ctx[:current_user].admin? } # <=== 1 +class PostType < GraphQL::Schema::Object + guard ->(obj, args, ctx) { ctx[:current_user].admin? } # <=== 3 + field :title, String, null: true, guard: ->(obj, args, ctx) { ctx[:current_user].admin? } # <=== 1 end -Schema = GraphQL::Schema.define do +class Schema < GraphQL::Schema + use GraphQL::Execution::Interpreter + use GraphQL::Analysis::AST query QueryType use GraphQL::Guard.new(policy_object: GraphqlPolicy) end @@ -211,8 +197,7 @@ class Ability end # Use the ability in your guard -PostType = GraphQL::ObjectType.define do - name "Post" +class PostType < GraphQL::Schema::Object guard ->(post, args, ctx) { ctx[:current_ability].can?(:read, post) } ... end @@ -232,8 +217,7 @@ class PostPolicy < ApplicationPolicy end # Use the ability in your guard -PostType = GraphQL::ObjectType.define do - name "Post" +class PostType < GraphQL::Schema::Object guard ->(post, args, ctx) { PostPolicy.new(ctx[:current_user], post).show? } ... end @@ -248,14 +232,20 @@ By default `GraphQL::Guard` raises a `GraphQL::Guard::NotAuthorizedError` except You can change this behavior, by passing custom `not_authorized` lambda. For example:-SchemaWithErrors = GraphQL::Schema.define do +class SchemaWithErrors < GraphQL::Schema + use GraphQL::Execution::Interpreter + use GraphQL::Analysis::AST query QueryType use GraphQL::Guard.new( # By default it raises an error - # not_authorized: ->(type, field) { raise GraphQL::Guard::NotAuthorizedError.new("#{type}.#{field}") } + # not_authorized: ->(type, field) do + # raise GraphQL::Guard::NotAuthorizedError.new("#{type.graphql_definition}.#{field}") + # end # Returns an error in the response - not_authorized: ->(type, field) { GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") } + not_authorized: ->(type, field) do + GraphQL::ExecutionError.new("Not authorized to access #{type.graphql_definition}.#{field}") + end ) end@@ -300,7 +290,9 @@ class GraphqlPolicy end end -Schema = GraphQL::Schema.define do +class Schema < GraphQL::Schema + use GraphQL::Execution::Interpreter + use GraphQL::Analysis::AST query QueryType mutation MutationType @@ -319,11 +311,9 @@ end It's possible to hide fields from being introspectable and accessible based on the context. For example:-PostType = GraphQL::ObjectType.define do - name "Post" - - field :id, !types.ID - field :title, types.String do +class PostType < GraphQL::Schema::Object + field :id, ID, null: false + field :title, String, null: true do # The field "title" is accessible only for beta testers mask ->(ctx) { ctx[:current_user].beta_tester? } end @@ -352,9 +342,8 @@ It's possible to test fields with `guard` in isolation:# Your type -QueryType = GraphQL::ObjectType.define do - name "Query" - field :posts, !types[!PostType], guard ->(obj, args, ctx) { ... } +class QueryType < GraphQL::Schema::Object + field :posts, [PostType], null: false, guard ->(obj, args, ctx) { ... } end # Your test @@ -370,9 +359,8 @@ If you would like to test your fields with policy objects:# Your type -QueryType = GraphQL::ObjectType.define do - name "Query" - field :posts, !types[!PostType] +class QueryType < GraphQL::Schema::Object + field :posts, [PostType], null: false end # Your policy object diff --git a/graphql-1.7.gemfile b/graphql-1.7.gemfile deleted file mode 100644 index e24d295..0000000 --- a/graphql-1.7.gemfile +++ /dev/null @@ -1,8 +0,0 @@ -source "https://rubygems.org" - -gem "pry" -gem 'coveralls' - -gem "graphql", "~> 1.7.14" - -gemspec diff --git a/graphql-guard.gemspec b/graphql-guard.gemspec index 377f387..f8dcec2 100644 --- a/graphql-guard.gemspec +++ b/graphql-guard.gemspec @@ -23,9 +23,9 @@ Gem::Specification.new do |spec| spec.required_ruby_version = '>= 2.1.0' # keyword args - spec.add_runtime_dependency "graphql", ">= 1.6.0", "< 2" + spec.add_runtime_dependency "graphql", ">= 1.10.0", "< 2" - spec.add_development_dependency "bundler", "~> 1.15" - spec.add_development_dependency "rake", "~> 10.0" + spec.add_development_dependency "bundler", "~> 2.1" + spec.add_development_dependency "rake", "~> 13.0" spec.add_development_dependency "rspec", "~> 3.0" end diff --git a/graphql-latest.gemfile b/graphql-latest.gemfile deleted file mode 100644 index 67ae9ad..0000000 --- a/graphql-latest.gemfile +++ /dev/null @@ -1,8 +0,0 @@ -source "https://rubygems.org" - -gem "pry" -gem 'coveralls' - -gem "graphql", "~> 1.10" - -gemspec diff --git a/lib/graphql/guard.rb b/lib/graphql/guard.rb index b059293..f94464d 100644 --- a/lib/graphql/guard.rb +++ b/lib/graphql/guard.rb @@ -5,10 +5,19 @@ module GraphQL class Guard + NotAuthorizedError = Class.new(StandardError) + ANY_FIELD_NAME = :'*' - DEFAULT_NOT_AUTHORIZED = ->(type, field) { raise NotAuthorizedError.new("Not authorized to access: #{type}.#{field}") } + EXECUTE_FIELD_EVENTS = %w[execute_field execute_field_lazy] - NotAuthorizedError = Class.new(StandardError) + DEFAULT_NOT_AUTHORIZED = ->(type, field) do + raise NotAuthorizedError.new("Not authorized to access: #{type.graphql_definition}.#{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 @@ -18,75 +27,65 @@ def initialize(policy_object: nil, not_authorized: DEFAULT_NOT_AUTHORIZED) end def use(schema_definition) - schema_definition.instrument(:field, self) + 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 instrument(type, field) - guard_proc = guard_proc(type, field) - return field unless guard_proc - - old_resolve_proc = field.resolve_proc - new_resolve_proc = ->(object, arguments, context) do - authorized = guard_proc.call(object, arguments, context) - - if authorized - old_resolve_proc.call(object, arguments, context) - else - not_authorized.call(type, field.name.to_sym) - end + def trace(event, trace_data) + if EXECUTE_FIELD_EVENTS.include?(event) + ensure_guarded(trace_data) { yield } + else + yield end - - field.redefine { resolve(new_resolve_proc) } end - def guard_proc(type, field) - inline_field_guard(field) || + def find_guard_proc(type, field) + inline_guard(field) || policy_object_guard(type, field.name.to_sym) || - inline_type_guard(type) || + inline_guard(type) || policy_object_guard(type, ANY_FIELD_NAME) end private def add_schema_masking!(schema_definition) - default_filter_proc = Proc.new do - def default_filter - GraphQL::Filter.new(except: default_mask).merge(only: ->(schema_member, ctx) { - schema_member.metadata[:mask] ? schema_member.metadata[:mask].call(ctx) : true - }) + 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 schema_definition.is_a?(Class) # GraphQL-Ruby version >= 1.10 - schema_definition.class_eval(&default_filter_proc) + if guard_proc.call(trace_data[:object], trace_data[:arguments], trace_data[:query].context) + yield else - schema_definition.target.instance_eval(&default_filter_proc) + not_authorized.call(field.owner, field.name.to_sym) end end def policy_object_guard(type, field_name) - policy_object && policy_object.guard(type, field_name) + @policy_object && @policy_object.guard(type.type_class, field_name) end - def inline_field_guard(field) - field.metadata[:guard] - end - - def inline_type_guard(type) - type.metadata[:guard] + def inline_guard(type_or_field) + type_or_field.graphql_definition.metadata[:guard] end end end -if GraphQL::ObjectType.respond_to?(:accepts_definitions) # GraphQL-Ruby version < 1.8 - 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)) -end - -if defined?(GraphQL::Schema::Object) && GraphQL::Schema::Object.respond_to?(:accepts_definition) # GraphQL-Ruby version >= 1.8 - GraphQL::Schema::Object.accepts_definition(:guard) - GraphQL::Schema::Field.accepts_definition(:guard) - GraphQL::Schema::Field.accepts_definition(:mask) -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) diff --git a/lib/graphql/guard/testing.rb b/lib/graphql/guard/testing.rb index e944d10..7c447bb 100644 --- a/lib/graphql/guard/testing.rb +++ b/lib/graphql/guard/testing.rb @@ -5,32 +5,18 @@ class Field NoGuardError = Class.new(StandardError) def guard(*args) - raise NoGuardError.new("Get your field by calling: Type.field_with_guard('#{name}')") unless @__guard_type - guard_proc = @__guard_object.guard_proc(@__guard_type, self) + raise NoGuardError.new("Get your field by calling: Type.field_with_guard('#{name}')") unless @__guard_instance + + guard_proc = @__guard_instance.find_guard_proc(@__guard_type, self) raise NoGuardError.new("Guard lambda does not exist for #{@__guard_type}.#{name}") unless guard_proc guard_proc.call(*args) end - def __policy_object=(policy_object) + def __set_guard_instance(policy_object, guard_type) @__policy_object = policy_object - @__guard_object = GraphQL::Guard.new(policy_object: policy_object) - end - - def __guard_type=(guard_type) @__guard_type = guard_type - end - end - - class ObjectType - def field_with_guard(field_name, policy_object = nil) - field = get_field(field_name) - return unless field - - field.clone.tap do |f| - f.__policy_object = policy_object - f.__guard_type = self - end + @__guard_instance = GraphQL::Guard.new(policy_object: policy_object) end end @@ -41,8 +27,7 @@ def self.field_with_guard(field_name, policy_object = nil) return unless field field.to_graphql.clone.tap do |f| - f.__policy_object = policy_object - f.__guard_type = self.to_graphql + f.__set_guard_instance(policy_object, self.to_graphql) end end end diff --git a/spec/fixtures/inline_schema.rb b/spec/fixtures/inline_schema.rb index 5b6a6fc..1b7d7ad 100644 --- a/spec/fixtures/inline_schema.rb +++ b/spec/fixtures/inline_schema.rb @@ -1,78 +1,45 @@ # frozen_string_literal: true module Inline - case ENV['GRAPHQL_RUBY_VERSION'] - when '1_7' - PostType = GraphQL::ObjectType.define do - name "Post" - guard ->(_post, _args, ctx) { ctx[:current_user].admin? } - field :id, !types.ID - field :title, types.String - end - - QueryType = GraphQL::ObjectType.define do - name "Query" - field :posts, !types[!PostType] do - argument :userId, !types.ID - guard ->(_obj, args, ctx) { args[:userId] == ctx[:current_user].id } - resolve ->(_obj, args, _ctx) { Post.where(user_id: args[:userId]) } - end + class PostType < GraphQL::Schema::Object + guard ->(_post, _args, ctx) { ctx[:current_user].admin? } + field :id, ID, null: false + field :title, String, null: true + end - field :postsWithMask, !types[!PostType] do - argument :userId, !types.ID - mask ->(ctx) { ctx[:current_user].admin? } - resolve ->(_obj, args, _ctx) { Post.where(user_id: args[:userId]) } - end + class QueryType < GraphQL::Schema::Object + field :posts, [PostType], null: false do + argument :user_id, ID, required: true + guard ->(_obj, args, ctx) { args[:user_id] == ctx[:current_user].id } end - Schema = GraphQL::Schema.define do - query QueryType - use GraphQL::Guard.new + field :posts_with_mask, [PostType], null: false do + argument :user_id, ID, required: true + mask ->(ctx) { ctx[:current_user].admin? } end - SchemaWithoutExceptions = GraphQL::Schema.define do - query QueryType - use GraphQL::Guard.new(not_authorized: ->(type, field) { - GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") - }) + def posts(user_id:) + Post.where(user_id: user_id) end - when 'LATEST' - class PostType < GraphQL::Schema::Object - guard ->(_post, _args, ctx) { ctx[:current_user].admin? } - field :id, ID, null: false - field :title, String, null: true - end - - class QueryType < GraphQL::Schema::Object - field :posts, [PostType], null: false do - argument :user_id, ID, required: true - guard ->(_obj, args, ctx) { args[:userId] == ctx[:current_user].id } - end - - field :posts_with_mask, [PostType], null: false do - argument :user_id, ID, required: true - mask ->(ctx) { ctx[:current_user].admin? } - end - def posts(user_id:) - Post.where(user_id: user_id) - end - - def posts_with_mask(user_id:) - Post.where(user_id: user_id) - end + def posts_with_mask(user_id:) + Post.where(user_id: user_id) end + end - class Schema < GraphQL::Schema - query QueryType - use GraphQL::Guard.new - end + class Schema < GraphQL::Schema + use GraphQL::Execution::Interpreter + use GraphQL::Analysis::AST + query QueryType + use GraphQL::Guard.new + end - class SchemaWithoutExceptions < GraphQL::Schema - query QueryType - use GraphQL::Guard.new(not_authorized: ->(type, field) { - GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") - }) - end + class SchemaWithoutExceptions < GraphQL::Schema + use GraphQL::Execution::Interpreter + use GraphQL::Analysis::AST + query QueryType + use GraphQL::Guard.new(not_authorized: ->(type, field) { + GraphQL::ExecutionError.new("Not authorized to access #{type.graphql_definition}.#{field}") + }) end end diff --git a/spec/fixtures/policy_object_schema.rb b/spec/fixtures/policy_object_schema.rb index 9e2942c..fa9e67a 100644 --- a/spec/fixtures/policy_object_schema.rb +++ b/spec/fixtures/policy_object_schema.rb @@ -1,75 +1,40 @@ # frozen_string_literal: true module PolicyObject - case ENV['GRAPHQL_RUBY_VERSION'] - when '1_7' - PostType = GraphQL::ObjectType.define do - name "Post" - field :id, !types.ID - field :title, types.String - end - - QueryType = GraphQL::ObjectType.define do - name "Query" - field :posts, !types[!PostType] do - argument :userId, !types.ID - resolve ->(_obj, args, _ctx) { Post.where(user_id: args[:userId]) } - end - end - - class GraphqlPolicy - RULES = { - QueryType => { - posts: ->(_obj, args, ctx) { args[:userId] == ctx[:current_user].id } - }, - PostType => { - '*': ->(_post, args, ctx) { ctx[:current_user].admin? } - } - } - - def self.guard(type, field) - RULES.dig(type, field) - end - end + class PostType < GraphQL::Schema::Object + field :id, ID, null: false + field :title, String, null: true + end - Schema = GraphQL::Schema.define do - query QueryType - use GraphQL::Guard.new(policy_object: GraphqlPolicy) - end - when 'LATEST' - class PostType < GraphQL::Schema::Object - field :id, ID, null: false - field :title, String, null: true + class QueryType < GraphQL::Schema::Object + field :posts, [PostType], null: false do + argument :user_id, ID, required: true end - class QueryType < GraphQL::Schema::Object - field :posts, [PostType], null: false do - argument :user_id, ID, required: true - end - - def posts(user_id:) - Post.where(user_id: user_id) - end + def posts(user_id:) + Post.where(user_id: user_id) end + end - class GraphqlPolicy - RULES = { - QueryType => { - posts: ->(_obj, args, ctx) { args[:userId] == ctx[:current_user].id } - }, - PostType => { - '*': ->(_post, args, ctx) { ctx[:current_user].admin? } - } + class GraphqlPolicy + RULES = { + QueryType => { + posts: ->(_obj, args, ctx) { args[:user_id] == ctx[:current_user].id } + }, + PostType => { + '*': ->(_post, args, ctx) { ctx[:current_user].admin? } } + } - def self.guard(type, field) - RULES.dig(type.metadata[:type_class], field) - end + def self.guard(type, field) + RULES.dig(type, field) end + end - class Schema < GraphQL::Schema - query QueryType - use GraphQL::Guard.new(policy_object: GraphqlPolicy) - end + class Schema < GraphQL::Schema + use GraphQL::Execution::Interpreter + use GraphQL::Analysis::AST + query QueryType + use GraphQL::Guard.new(policy_object: GraphqlPolicy) end end diff --git a/spec/fixtures/without_interpreter_schema.rb b/spec/fixtures/without_interpreter_schema.rb new file mode 100644 index 0000000..c332bd1 --- /dev/null +++ b/spec/fixtures/without_interpreter_schema.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module WithoutInterpreter + class QueryType < GraphQL::Schema::Object + field :userIds, [String], null: false + + def user_ids + ['1', '2'] + end + end + + class Schema < GraphQL::Schema + query QueryType + use GraphQL::Guard.new + end +end diff --git a/spec/graphql/guard_spec.rb b/spec/graphql/guard_spec.rb index 0b4dab0..158394e 100644 --- a/spec/graphql/guard_spec.rb +++ b/spec/graphql/guard_spec.rb @@ -13,7 +13,7 @@ user = User.new(id: '1', role: 'admin') query = "query($userId: ID!) { posts(userId: $userId) { id title } }" - result = Inline::Schema.execute(query, variables: {'userId' => user.id}, context: {current_user: user}) + result = Inline::Schema.execute(query, variables: {userId: user.id}, context: {current_user: user}) expect(result).to eq({"data" => {"posts" => [{"id" => "1", "title" => "Post Title"}]}}) end @@ -23,7 +23,7 @@ query = "query($userId: ID!) { posts(userId: $userId) { id title } }" expect { - Inline::Schema.execute(query, variables: {'userId' => '2'}, context: {current_user: user}) + Inline::Schema.execute(query, variables: {userId: 2}, context: {current_user: user}) }.to raise_error(GraphQL::Guard::NotAuthorizedError, 'Not authorized to access: Query.posts') end @@ -32,7 +32,7 @@ query = "query($userId: ID!) { posts(userId: $userId) { id title } }" expect { - Inline::Schema.execute(query, variables: {'userId' => '1'}, context: {current_user: user}) + Inline::Schema.execute(query, variables: {userId: 1}, context: {current_user: user}) }.to raise_error(GraphQL::Guard::NotAuthorizedError, 'Not authorized to access: Post.id') end @@ -40,7 +40,7 @@ user = User.new(id: '1', role: 'not_admin') query = "query($userId: ID!) { posts(userId: $userId) { id title } }" - result = Inline::SchemaWithoutExceptions.execute(query, variables: {'userId' => '1'}, context: {current_user: user}) + result = Inline::SchemaWithoutExceptions.execute(query, variables: {userId: 1}, context: {current_user: user}) expect(result['errors']).to eq([{ "message" => "Not authorized to access Post.id", @@ -56,7 +56,7 @@ user = User.new(id: '1', role: 'admin') query = "query($userId: ID!) { postsWithMask(userId: $userId) { id } }" - result = Inline::Schema.execute(query, variables: {'userId' => user.id}, context: {current_user: user}) + result = Inline::Schema.execute(query, variables: {userId: user.id}, context: {current_user: user}) expect(result.to_h).to eq({"data" => {"postsWithMask" => [{"id" => "1"}]}}) end @@ -65,23 +65,14 @@ user = User.new(id: '1', role: 'not_admin') query = "query($userId: ID!) { postsWithMask(userId: $userId) { id } }" - result = Inline::Schema.execute(query, variables: {'userId' => user.id}, context: {current_user: user}) - - case ENV['GRAPHQL_RUBY_VERSION'] - when '1_7' - expect(result['errors']).to include({ - "message" => "Field 'postsWithMask' doesn't exist on type 'Query'", - "locations" => [{"line" => 1, "column" => 23}], - "fields" => ["query", "postsWithMask"] - }) - when 'LATEST' - expect(result['errors']).to include({ - "message" => "Field 'postsWithMask' doesn't exist on type 'Query'", - "locations" => [{"line" => 1, "column" => 23}], - "path" => ["query", "postsWithMask"], - "extensions" => {"code" => "undefinedField", "typeName" => "Query", "fieldName" => "postsWithMask"} - }) - end + result = Inline::Schema.execute(query, variables: {userId: user.id}, context: {current_user: user}) + + expect(result['errors']).to include({ + "message" => "Field 'postsWithMask' doesn't exist on type 'Query'", + "locations" => [{"line" => 1, "column" => 23}], + "path" => ["query", "postsWithMask"], + "extensions" => {"code" => "undefinedField", "typeName" => "Query", "fieldName" => "postsWithMask"} + }) end end @@ -90,7 +81,7 @@ user = User.new(id: '1', role: 'admin') query = "query($userId: ID!) { posts(userId: $userId) { id title } }" - result = PolicyObject::Schema.execute(query, variables: {'userId' => user.id}, context: {current_user: user}) + result = PolicyObject::Schema.execute(query, variables: {userId: user.id}, context: {current_user: user}) expect(result).to eq({"data" => {"posts" => [{"id" => "1", "title" => "Post Title"}]}}) end @@ -100,7 +91,7 @@ query = "query($userId: ID!) { posts(userId: $userId) { id title } }" expect { - PolicyObject::Schema.execute(query, variables: {'userId' => '2'}, context: {current_user: user}) + PolicyObject::Schema.execute(query, variables: {userId: 2}, context: {current_user: user}) }.to raise_error(GraphQL::Guard::NotAuthorizedError, 'Not authorized to access: Query.posts') end @@ -109,8 +100,18 @@ query = "query($userId: ID!) { posts(userId: $userId) { id title } }" expect { - PolicyObject::Schema.execute(query, variables: {'userId' => '1'}, context: {current_user: user}) + PolicyObject::Schema.execute(query, variables: {userId: 1}, context: {current_user: user}) }.to raise_error(GraphQL::Guard::NotAuthorizedError, 'Not authorized to access: Post.id') end end + + context 'schema without interpreter' do + it 'raises an exception' do + query = "query { userIds }" + + expect { + require 'fixtures/without_interpreter_schema' + }.to raise_error('Please use the graphql gem version >= 1.10 with GraphQL::Execution::Interpreter') + end + end end diff --git a/spec/graphql/testing_spec.rb b/spec/graphql/testing_spec.rb index 939c020..302f69f 100644 --- a/spec/graphql/testing_spec.rb +++ b/spec/graphql/testing_spec.rb @@ -15,7 +15,7 @@ posts_field = Inline::QueryType.field_with_guard('posts') user = User.new(id: '1', role: 'admin') - result = posts_field.guard(nil, {userId: user.id}, {current_user: user}) + result = posts_field.guard(nil, {user_id: user.id}, {current_user: user}) expect(result).to eq(true) end @@ -24,7 +24,7 @@ posts_field = Inline::QueryType.field_with_guard('posts') user = User.new(id: '1', role: 'admin') - result = posts_field.guard(nil, {userId: '2'}, {current_user: user}) + result = posts_field.guard(nil, {user_id: '2'}, {current_user: user}) expect(result).to eq(false) end @@ -44,7 +44,7 @@ posts_field = PolicyObject::QueryType.field_with_guard('posts', PolicyObject::GraphqlPolicy) user = User.new(id: '1', role: 'admin') - result = posts_field.guard(nil, {userId: user.id}, {current_user: user}) + result = posts_field.guard(nil, {user_id: user.id}, {current_user: user}) expect(result).to eq(true) end @@ -54,26 +54,15 @@ user = User.new(id: '1', role: 'admin') expect { - posts_field.guard(nil, {userId: user.id}, {current_user: user}) + posts_field.guard(nil, {user_id: user.id}, {current_user: user}) }.to raise_error(GraphQL::Field::NoGuardError, "Guard lambda does not exist for Query.posts") end - if ENV['GRAPHQL_RUBY_VERSION'] == '1_7' - it 'raises an error if the field was fetched without guard' do - posts_field = PolicyObject::QueryType.get_field('posts') - user = User.new(id: '1', role: 'admin') - - expect { - posts_field.guard(nil, {userId: user.id}, {current_user: user}) - }.to raise_error(GraphQL::Field::NoGuardError, "Get your field by calling: Type.field_with_guard('posts')") - end - end - it 'returns false for a not authorized field' do posts_field = PolicyObject::QueryType.field_with_guard('posts', PolicyObject::GraphqlPolicy) user = User.new(id: '1', role: 'admin') - result = posts_field.guard(nil, {userId: '2'}, {current_user: user}) + result = posts_field.guard(nil, {user_id: '2'}, {current_user: user}) expect(result).to eq(false) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 129e314..f19d8ec 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,8 +2,6 @@ require "bundler/setup" -ENV['GRAPHQL_RUBY_VERSION'] ||= 'LATEST' - if ENV['CI'] require 'simplecov' SimpleCov.add_filter('spec')