Skip to content

Commit da41589

Browse files
authored
Merge pull request #4442 from rmosolgo/bypass-warden
Make a way to bypass type visibility
2 parents 551ef0e + 529ae9f commit da41589

File tree

11 files changed

+117
-16
lines changed

11 files changed

+117
-16
lines changed

guides/authorization/visibility.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,17 @@ end
9393
```
9494

9595
For this to work, the base argument class must be {% internal_link "configured with other GraphQL types", "/type_definitions/extensions.html#customizing-arguments" %}.
96+
97+
## Opting Out
98+
99+
By default, GraphQL-Ruby always runs visibility checks. You can opt out of this by adding to your schema class:
100+
101+
```ruby
102+
class MySchema < GraphQL::Schema
103+
# ...
104+
# Opt out of GraphQL-Ruby's visibility feature:
105+
use GraphQL::Schema::AlwaysVisible
106+
end
107+
```
108+
109+
For big schemas, this can be a worthwhile speed-up.

lib/graphql/language/document_from_schema_definition.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ def initialize(
3636
context: schema_context,
3737
)
3838
else
39-
GraphQL::Schema::Warden.new(schema: @schema, context: schema_context)
39+
@schema.warden_class.new(
40+
schema: @schema,
41+
context: schema_context,
42+
)
4043
end
4144

4245
schema_context.warden = @warden

lib/graphql/query.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ def find_operation(operations, operation_name)
385385

386386
def prepare_ast
387387
@prepared_ast = true
388-
@warden ||= GraphQL::Schema::Warden.new(@filter, schema: @schema, context: @context)
388+
@warden ||= @schema.warden_class.new(@filter, schema: @schema, context: @context)
389389
parse_error = nil
390390
@document ||= begin
391391
if query_string

lib/graphql/query/null_context.rb

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,6 @@ module GraphQL
33
class Query
44
# This object can be `ctx` in places where there is no query
55
class NullContext
6-
class NullWarden < GraphQL::Schema::Warden
7-
def visible_field?(field, ctx); true; end
8-
def visible_argument?(arg, ctx); true; end
9-
def visible_type?(type, ctx); true; end
10-
def visible_enum_value?(ev, ctx); true; end
11-
def visible_type_membership?(tm, ctx); true; end
12-
end
13-
146
class NullQuery
157
def after_lazy(value)
168
yield(value)
@@ -29,11 +21,7 @@ def initialize
2921
@query = NullQuery.new
3022
@dataloader = GraphQL::Dataloader::NullDataloader.new
3123
@schema = NullSchema
32-
@warden = NullWarden.new(
33-
GraphQL::Filter.new(silence_deprecation_warning: true),
34-
context: self,
35-
schema: @schema,
36-
)
24+
@warden = Schema::Warden::NullWarden.new(context: self, schema: @schema)
3725
end
3826

3927
def interpreter?

lib/graphql/schema.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# frozen_string_literal: true
22
require "graphql/schema/addition"
3+
require "graphql/schema/always_visible"
34
require "graphql/schema/base_64_encoder"
45
require "graphql/schema/find_inherited_value"
56
require "graphql/schema/finder"
@@ -420,6 +421,18 @@ def root_types
420421
@root_types
421422
end
422423

424+
def warden_class
425+
if defined?(@warden_class)
426+
@warden_class
427+
elsif superclass.respond_to?(:warden_class)
428+
superclass.warden_class
429+
else
430+
GraphQL::Schema::Warden
431+
end
432+
end
433+
434+
attr_writer :warden_class
435+
423436
# @param type [Module] The type definition whose possible types you want to see
424437
# @return [Hash<String, Module>] All possible types, if no `type` is given.
425438
# @return [Array<Module>] Possible types for `type`, if it's given.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
module GraphQL
3+
class Schema
4+
class AlwaysVisible
5+
def self.use(schema, **opts)
6+
schema.warden_class = GraphQL::Schema::Warden::NullWarden
7+
end
8+
end
9+
end
10+
end

lib/graphql/schema/warden.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,32 @@ def arguments(owner, ctx); owner.arguments(ctx); end
8787
end
8888
end
8989

90+
class NullWarden
91+
def initialize(_filter = nil, context:, schema:)
92+
@schema = schema
93+
end
94+
95+
def visible_field?(field_defn, _ctx = nil, owner = nil); true; end
96+
def visible_argument?(arg_defn, _ctx = nil); true; end
97+
def visible_type?(type_defn, _ctx = nil); true; end
98+
def visible_enum_value?(enum_value, _ctx = nil); true; end
99+
def visible_type_membership?(type_membership, _ctx = nil); true; end
100+
def interface_type_memberships(obj_type, _ctx = nil); obj_type.interface_type_memberships; end
101+
def get_type(type_name); @schema.get_type(type_name); end # rubocop:disable Development/ContextIsPassedCop
102+
def arguments(argument_owner, ctx = nil); argument_owner.arguments(ctx).values; end
103+
def enum_values(enum_defn); enum_defn.enum_values; end # rubocop:disable Development/ContextIsPassedCop
104+
def get_argument(parent_type, argument_name); parent_type.get_argument(argument_name); end # rubocop:disable Development/ContextIsPassedCop
105+
def types; @schema.types; end # rubocop:disable Development/ContextIsPassedCop
106+
def root_type_for_operation(op_name); @schema.root_type_for_operation(op_name); end
107+
def directives; @schema.directives.values; end
108+
def fields(type_defn); type_defn.fields; end # rubocop:disable Development/ContextIsPassedCop
109+
def get_field(parent_type, field_name); @schema.get_field(parent_type, field_name); end
110+
def reachable_type?(type_name); true; end
111+
def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop
112+
def possible_types(type_defn); @schema.possible_types(type_defn); end
113+
def interfaces(obj_type); obj_type.interfaces; end
114+
end
115+
90116
# @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
91117
# @param context [GraphQL::Query::Context]
92118
# @param schema [GraphQL::Schema]

spec/graphql/execution/interpreter_spec.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,8 @@ class Schema < GraphQL::Schema
278278
mutation(Mutation)
279279
lazy_resolve(Box, :value)
280280

281+
use GraphQL::Schema::AlwaysVisible
282+
281283
def self.object_from_id(id, ctx)
282284
OpenStruct.new(id: id)
283285
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# frozen_string_literal: true
2+
require "spec_helper"
3+
4+
describe GraphQL::Schema::AlwaysVisible do
5+
class AlwaysVisibleSchema < GraphQL::Schema
6+
class Query < GraphQL::Schema::Object
7+
def self.visible?(ctx)
8+
ctx[:visible_was_called] = true
9+
false
10+
end
11+
12+
field :one, Integer
13+
def one; 1; end
14+
end
15+
query(Query)
16+
use GraphQL::Schema::AlwaysVisible
17+
end
18+
19+
class NotAlwaysVisibleSchema < GraphQL::Schema
20+
query(AlwaysVisibleSchema::Query)
21+
end
22+
23+
it "Doesn't call visibility methods" do
24+
res = NotAlwaysVisibleSchema.execute("{ one }")
25+
assert res.context[:visible_was_called]
26+
assert_equal ["Schema is not configured for queries"], res["errors"].map { |err| err["message"] }
27+
28+
res = AlwaysVisibleSchema.execute("{ one }")
29+
refute res.context[:visible_was_called]
30+
assert_equal 1, res["data"]["one"]
31+
end
32+
end

spec/graphql/schema/warden_spec.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,4 +950,15 @@ def self.visible?(member, context)
950950
end
951951
end
952952
end
953+
954+
describe "NullWarden" do
955+
it "implements all Warden methods" do
956+
warden_methods = GraphQL::Schema::Warden.instance_methods - Object.methods
957+
warden_methods.each do |method_name|
958+
warden_params = GraphQL::Schema::Warden.instance_method(method_name).parameters
959+
assert GraphQL::Schema::Warden::NullWarden.method_defined?(method_name), "Null warden also responds to #{method_name} (#{warden_params})"
960+
assert_equal warden_params, GraphQL::Schema::Warden::NullWarden.instance_method(method_name).parameters,"#{method_name} has the same parameters"
961+
end
962+
end
963+
end
953964
end

0 commit comments

Comments
 (0)