graphql_devise 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f126f974c5e4f4bcd57e9a9713dcb018e0abfba31c8896390224004c30aefec
4
- data.tar.gz: f1a9bf4b8eca414586a4379d19b87318de2546920ac2fec45a274b996f276379
3
+ metadata.gz: a254ff0aef5cb9ce45ee53100dccecd33a2b0898afc1261d668be20b4cd8a739
4
+ data.tar.gz: d062d1b78f625585c0e613d99b5a252c76d1e16f58aa75711fb63197241d59b6
5
5
  SHA512:
6
- metadata.gz: 760a06cc2d976db391b2df28422592b28f1e65fcdbd5e4f9973341158417839d99d05a2ea1bba2ef94fa481207bbb97baabd7c2ec7bb7011f3e516dc64c6c5bd
7
- data.tar.gz: 32fb591a9b10c8c9d4f8f8990b7dd7943990c80d45eba355748d7780caa4704303b33623ef9b0ff340f30b2319c0a0473e7c775bb1cdb30e17c368a8f18c621e
6
+ metadata.gz: 826361a8cd110e817da03f60383513e02a06e7847543c9d183500ed86b381419fe44742607b6c5b5f4bb18afd985b80dc9f68c152ba99e10ca237f3c0442ab56
7
+ data.tar.gz: 49a8b2cec607525cc84682cf797d05456c6d462751ac5857dd1a157d72766b2a6654cc74ce40c8a9e34847ab66dd441d1d96d6c696ea526ac2f8c8ec7668bcca
data/.circleci/config.yml CHANGED
@@ -13,6 +13,7 @@ jobs:
13
13
  - image: 'ruby:<< parameters.ruby-version >>'
14
14
  environment:
15
15
  BUNDLE_GEMFILE: << parameters.gemfile >>
16
+ BUNDLE_JOBS: '2'
16
17
  COVERALLS_PARALLEL: 'true'
17
18
  EAGER_LOAD: 'true'
18
19
  RUBYOPT: '-rostruct'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [v2.3.0](https://github.com/graphql-devise/graphql_devise/tree/v2.3.0) (2026-07-20)
4
+
5
+ [Full Changelog](https://github.com/graphql-devise/graphql_devise/compare/v2.2.0...v2.3.0)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Disable introspection [\#255](https://github.com/graphql-devise/graphql_devise/issues/255)
10
+ - Support public\_introspection option in mount\_graphql\_devise\_for [\#294](https://github.com/graphql-devise/graphql_devise/pull/294) ([mcelicalderon](https://github.com/mcelicalderon))
11
+
3
12
  ## [v2.2.0](https://github.com/graphql-devise/graphql_devise/tree/v2.2.0) (2026-05-13)
4
13
 
5
14
  [Full Changelog](https://github.com/graphql-devise/graphql_devise/compare/v2.1.1...v2.2.0)
@@ -463,10 +472,6 @@
463
472
 
464
473
  - mutation': Second definition of 'mutation\(...\)' \(GraphqlDevise::Types::MutationType\) is invalid [\#59](https://github.com/graphql-devise/graphql_devise/issues/59)
465
474
 
466
- **Merged pull requests:**
467
-
468
- - Add mailer locale doc [\#44](https://github.com/graphql-devise/graphql_devise/pull/44) ([aarona](https://github.com/aarona))
469
-
470
475
  ## [v0.9.1](https://github.com/graphql-devise/graphql_devise/tree/v0.9.1) (2019-12-26)
471
476
 
472
477
  [Full Changelog](https://github.com/graphql-devise/graphql_devise/compare/v0.9.0...v0.9.1)
data/README.md CHANGED
@@ -287,6 +287,13 @@ You need to provide a hash to this option, and
287
287
  each key will be the name of the query on the schema. Also, the value provided must be a valid Resolver.
288
288
  This is also similar to what you can accomplish with
289
289
  [devise_scope](https://www.rubydoc.info/github/heartcombo/devise/master/ActionDispatch/Routing/Mapper%3Adevise_for).
290
+ 1. `public_introspection`: Defaults to `true`. When set to `false`, introspection fields (`__schema`, `__type`)
291
+ will require authentication on the gem provided schema mounted on a separate route. Given that this schema never
292
+ sets a `current_resource` in the GraphQL context, this effectively disables introspection on that route.
293
+ Please note the gem provided schema is shared by all mounted resources, so setting this option to `false` on any
294
+ mount will disable introspection for all of them. **This option only works if you are using the mount method.**
295
+ If you are mounting the auth operations into your own schema, use the `public_introspection` option of the
296
+ `SchemaPlugin` instead.
290
297
 
291
298
  Additional mutations and queries will be added to the schema regardless
292
299
  of other options you might have specified like `skip` or `only`.
@@ -575,6 +582,9 @@ standard Devise templates.
575
582
  `GraphqlDevise::Schema` extends from `GraphQL::Schema`, which allows it to utilize the `disable_introspection_entry_points` method. By calling this method, you can disable introspection entry points in your schema. You can read more about it
576
583
  [here](https://github.com/rmosolgo/graphql-ruby/blob/master/guides/schema/introspection.md#disabling-introspection).
577
584
 
585
+ If you are mounting the auth schema on a separate route, you can also pass `public_introspection: false` to the
586
+ `mount_graphql_devise_for` method. Check the [available mount options](#available-mount-options) section for more information.
587
+
578
588
  If you are using the schema plugin and prefer to make introspection queries available only to authenticated users, you can do so by modifying the `public_introspection` option of the plugin. Check the [plugin config section](#mounting-operations-into-your-own-schema) for more information.
579
589
 
580
590
  ## Changelog
data/config/routes.rb CHANGED
@@ -4,7 +4,7 @@ GraphqlDevise::Engine.routes.draw do
4
4
  # Required as Devise forces routes to reload on eager_load
5
5
  unless GraphqlDevise.schema_loaded?
6
6
  if GraphqlDevise::Types::QueryType.fields.blank?
7
- GraphqlDevise::Types::QueryType.field(:dummy, resolver: GraphqlDevise::Resolvers::Dummy)
7
+ GraphqlDevise::Types::QueryType.field(:dummy, resolver: GraphqlDevise::Resolvers::Dummy, authenticate: false)
8
8
  end
9
9
 
10
10
  if GraphqlDevise::Types::MutationType.fields.present?
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlDevise
4
+ module MountMethod
5
+ module OptionSanitizers
6
+ class BooleanChecker
7
+ def initialize(default_boolean = nil)
8
+ @default_boolean = default_boolean
9
+ end
10
+
11
+ def call!(value, key)
12
+ return @default_boolean if value.nil?
13
+
14
+ unless value.instance_of?(TrueClass) || value.instance_of?(FalseClass)
15
+ raise InvalidMountOptionsError, "`#{key}` option has an invalid value. `true` or `false` expected."
16
+ end
17
+
18
+ value
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -9,7 +9,8 @@ module GraphqlDevise
9
9
  skip: OptionSanitizers::ArrayChecker.new(Symbol),
10
10
  additional_queries: OptionSanitizers::HashChecker.new(GraphQL::Schema::Resolver),
11
11
  additional_mutations: OptionSanitizers::HashChecker.new(GraphQL::Schema::Mutation),
12
- authenticatable_type: OptionSanitizers::ClassChecker.new(GraphQL::Schema::Member)
12
+ authenticatable_type: OptionSanitizers::ClassChecker.new(GraphQL::Schema::Member),
13
+ public_introspection: OptionSanitizers::BooleanChecker.new(true)
13
14
  }.freeze
14
15
  end
15
16
  end
@@ -1,23 +1,36 @@
1
1
  module GraphqlDevise
2
2
  module RouteMounter
3
3
  def mount_graphql_devise_for(resource, options = {})
4
- routing = 'graphql_devise/graphql#auth'
5
-
6
- if (base_controller = options.delete(:base_controller))
7
- new_controller = GraphqlDevise.const_set("#{resource}AuthController", Class.new(base_controller))
8
- new_controller.include(SetUserByToken)
9
- new_controller.include(AuthControllerMethods)
10
-
11
- routing = "#{new_controller.to_s.underscore.gsub('_controller','')}#auth"
12
- end
4
+ routing = auth_routing(resource, options.delete(:base_controller))
13
5
 
14
6
  clean_options = ResourceLoader.new(resource, options, true).call(
15
7
  Types::QueryType,
16
8
  Types::MutationType
17
9
  )
18
10
 
11
+ require_introspection_auth(clean_options)
12
+
19
13
  post clean_options.at, to: routing
20
14
  get clean_options.at, to: routing
21
15
  end
16
+
17
+ private
18
+
19
+ def auth_routing(resource, base_controller)
20
+ return 'graphql_devise/graphql#auth' if base_controller.blank?
21
+
22
+ new_controller = GraphqlDevise.const_set("#{resource}AuthController", Class.new(base_controller))
23
+ new_controller.include(SetUserByToken)
24
+ new_controller.include(AuthControllerMethods)
25
+
26
+ "#{new_controller.to_s.underscore.gsub('_controller', '')}#auth"
27
+ end
28
+
29
+ def require_introspection_auth(clean_options)
30
+ return if clean_options.public_introspection || GraphqlDevise.introspection_plugin_applied?
31
+
32
+ Schema.use(SchemaPlugin.new(authenticate_default: true, public_introspection: false))
33
+ GraphqlDevise.introspection_plugin_applied!
34
+ end
22
35
  end
23
36
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GraphqlDevise
4
- VERSION = '2.2.0'
4
+ VERSION = '2.3.0'
5
5
  end
@@ -27,8 +27,9 @@ module GraphqlDevise
27
27
 
28
28
  class InvalidMountOptionsError < ::GraphqlDevise::Error; end
29
29
 
30
- @schema_loaded = false
31
- @mounted_resources = []
30
+ @schema_loaded = false
31
+ @mounted_resources = []
32
+ @introspection_plugin_applied = false
32
33
 
33
34
  def self.schema_loaded?
34
35
  @schema_loaded
@@ -38,6 +39,14 @@ module GraphqlDevise
38
39
  @schema_loaded = true
39
40
  end
40
41
 
42
+ def self.introspection_plugin_applied?
43
+ @introspection_plugin_applied
44
+ end
45
+
46
+ def self.introspection_plugin_applied!
47
+ @introspection_plugin_applied = true
48
+ end
49
+
41
50
  def self.resource_mounted?(model)
42
51
  @mounted_resources.include?(model)
43
52
  end
@@ -32,6 +32,12 @@ Rails.application.routes.draw do
32
32
  at: '/api/v1/user_customer/graphql_auth'
33
33
  )
34
34
 
35
+ mount_graphql_devise_for(
36
+ User,
37
+ at: '/api/v1/no_introspection',
38
+ public_introspection: false
39
+ )
40
+
35
41
  get '/api/v1/graphql', to: 'api/v1/graphql#graphql'
36
42
  post '/api/v1/graphql', to: 'api/v1/graphql#graphql'
37
43
  post '/api/v1/interpreter', to: 'api/v1/graphql#interpreter'
@@ -152,4 +152,42 @@ RSpec.describe 'Login Requests' do
152
152
  end
153
153
  end
154
154
  end
155
+
156
+ context 'when using the gem provided schema mounted on a route with public_introspection: false' do
157
+ it 'return an error' do
158
+ post_request('/api/v1/no_introspection')
159
+
160
+ expect(json_response[:data]).to be_nil
161
+ expect(json_response[:errors]).to contain_exactly(
162
+ hash_including(
163
+ message: '__schema field requires authentication',
164
+ extensions: { code: 'AUTHENTICATION_ERROR' }
165
+ )
166
+ )
167
+ end
168
+
169
+ context 'when executing a regular operation on the same route' do
170
+ let(:password) { '12345678' }
171
+ let(:user) { create(:user, :confirmed, password: password) }
172
+ let(:query) do
173
+ <<-GRAPHQL
174
+ mutation {
175
+ userLogin(
176
+ email: "#{user.email}",
177
+ password: "#{password}"
178
+ ) {
179
+ user { email }
180
+ }
181
+ }
182
+ GRAPHQL
183
+ end
184
+
185
+ it 'executes the operation successfully' do
186
+ post_request('/api/v1/no_introspection')
187
+
188
+ expect(json_response[:errors]).to be_nil
189
+ expect(json_response[:data][:userLogin][:user][:email]).to eq(user.email)
190
+ end
191
+ end
192
+ end
155
193
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe GraphqlDevise::MountMethod::OptionSanitizers::BooleanChecker do
6
+ describe '#call!' do
7
+ subject(:clean_value) { described_class.new(default_boolean).call!(value, key) }
8
+
9
+ let(:key) { :any_option }
10
+ let(:default_boolean) { true }
11
+
12
+ context 'when no value is provided' do
13
+ let(:value) { nil }
14
+
15
+ it { is_expected.to eq(default_boolean) }
16
+ end
17
+
18
+ context 'when provided value is false' do
19
+ let(:value) { false }
20
+
21
+ it 'preserves the explicit false value instead of falling back to the default' do
22
+ expect(clean_value).to eq(false)
23
+ end
24
+ end
25
+
26
+ context 'when provided value is true' do
27
+ let(:value) { true }
28
+
29
+ it { is_expected.to eq(true) }
30
+ end
31
+
32
+ context 'when provided value is not a boolean' do
33
+ let(:value) { 'true' }
34
+
35
+ it 'raises an error' do
36
+ expect { clean_value }.to raise_error(GraphqlDevise::InvalidMountOptionsError, "`#{key}` option has an invalid value. `true` or `false` expected.")
37
+ end
38
+ end
39
+ end
40
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql_devise
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mario Celi
@@ -358,6 +358,7 @@ files:
358
358
  - lib/graphql_devise/mount_method/operation_sanitizer.rb
359
359
  - lib/graphql_devise/mount_method/option_sanitizer.rb
360
360
  - lib/graphql_devise/mount_method/option_sanitizers/array_checker.rb
361
+ - lib/graphql_devise/mount_method/option_sanitizers/boolean_checker.rb
361
362
  - lib/graphql_devise/mount_method/option_sanitizers/class_checker.rb
362
363
  - lib/graphql_devise/mount_method/option_sanitizers/hash_checker.rb
363
364
  - lib/graphql_devise/mount_method/option_sanitizers/string_checker.rb
@@ -492,6 +493,7 @@ files:
492
493
  - spec/services/mount_method/operation_sanitizer_spec.rb
493
494
  - spec/services/mount_method/option_sanitizer_spec.rb
494
495
  - spec/services/mount_method/option_sanitizers/array_checker_spec.rb
496
+ - spec/services/mount_method/option_sanitizers/boolean_checker_spec.rb
495
497
  - spec/services/mount_method/option_sanitizers/class_checker_spec.rb
496
498
  - spec/services/mount_method/option_sanitizers/hash_checker_spec.rb
497
499
  - spec/services/mount_method/option_sanitizers/string_checker_spec.rb
@@ -638,6 +640,7 @@ test_files:
638
640
  - spec/services/mount_method/operation_sanitizer_spec.rb
639
641
  - spec/services/mount_method/option_sanitizer_spec.rb
640
642
  - spec/services/mount_method/option_sanitizers/array_checker_spec.rb
643
+ - spec/services/mount_method/option_sanitizers/boolean_checker_spec.rb
641
644
  - spec/services/mount_method/option_sanitizers/class_checker_spec.rb
642
645
  - spec/services/mount_method/option_sanitizers/hash_checker_spec.rb
643
646
  - spec/services/mount_method/option_sanitizers/string_checker_spec.rb