graphql_devise 0.13.3 → 0.14.1

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -0
  3. data/Appraisals +14 -0
  4. data/CHANGELOG.md +47 -0
  5. data/README.md +35 -22
  6. data/app/models/graphql_devise/concerns/model.rb +6 -6
  7. data/app/views/graphql_devise/mailer/reset_password_instructions.html.erb +7 -1
  8. data/config/locales/en.yml +2 -1
  9. data/docs/usage/reset_password_flow.md +90 -0
  10. data/graphql_devise.gemspec +1 -1
  11. data/lib/graphql_devise/concerns/controller_methods.rb +7 -1
  12. data/lib/graphql_devise/default_operations/mutations.rb +10 -6
  13. data/lib/graphql_devise/mutations/resend_confirmation.rb +15 -5
  14. data/lib/graphql_devise/mutations/send_password_reset.rb +2 -0
  15. data/lib/graphql_devise/mutations/send_password_reset_with_token.rb +37 -0
  16. data/lib/graphql_devise/mutations/sign_up.rb +1 -3
  17. data/lib/graphql_devise/mutations/update_password_with_token.rb +38 -0
  18. data/lib/graphql_devise/resolvers/check_password_token.rb +1 -0
  19. data/lib/graphql_devise/resolvers/confirm_account.rb +2 -0
  20. data/lib/graphql_devise/schema_plugin.rb +12 -10
  21. data/lib/graphql_devise/version.rb +1 -1
  22. data/spec/dummy/app/graphql/mutations/reset_admin_password_with_token.rb +13 -0
  23. data/spec/dummy/config/initializers/devise_token_auth.rb +2 -0
  24. data/spec/dummy/config/routes.rb +2 -1
  25. data/spec/graphql/user_queries_spec.rb +118 -0
  26. data/spec/requests/mutations/additional_mutations_spec.rb +0 -1
  27. data/spec/requests/mutations/resend_confirmation_spec.rb +42 -4
  28. data/spec/requests/mutations/send_password_reset_spec.rb +16 -1
  29. data/spec/requests/mutations/send_password_reset_with_token_spec.rb +78 -0
  30. data/spec/requests/mutations/sign_up_spec.rb +19 -1
  31. data/spec/requests/mutations/update_password_with_token_spec.rb +119 -0
  32. data/spec/requests/queries/check_password_token_spec.rb +16 -1
  33. data/spec/requests/queries/confirm_account_spec.rb +17 -2
  34. data/spec/requests/user_controller_spec.rb +9 -9
  35. data/spec/support/contexts/schema_test.rb +14 -0
  36. metadata +21 -8
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe 'Send Password Reset Requests' do
6
+ include_context 'with graphql query request'
7
+
8
+ let!(:user) { create(:user, :confirmed, email: 'jwinnfield@wallaceinc.com') }
9
+ let(:email) { user.email }
10
+ let(:redirect_url) { 'https://google.com' }
11
+ let(:query) do
12
+ <<-GRAPHQL
13
+ mutation {
14
+ userSendPasswordResetWithToken(
15
+ email: "#{email}",
16
+ redirectUrl: "#{redirect_url}"
17
+ ) {
18
+ message
19
+ }
20
+ }
21
+ GRAPHQL
22
+ end
23
+
24
+ context 'when redirect_url is not whitelisted' do
25
+ let(:redirect_url) { 'https://not-safe.com' }
26
+
27
+ it 'returns a not whitelisted redirect url error' do
28
+ expect { post_request }.to not_change(ActionMailer::Base.deliveries, :count)
29
+
30
+ expect(json_response[:errors]).to containing_exactly(
31
+ hash_including(
32
+ message: "Redirect to '#{redirect_url}' not allowed.",
33
+ extensions: { code: 'USER_ERROR' }
34
+ )
35
+ )
36
+ end
37
+ end
38
+
39
+ context 'when params are correct' do
40
+ context 'when using the gem schema' do
41
+ it 'sends password reset email' do
42
+ expect { post_request }.to change(ActionMailer::Base.deliveries, :count).by(1)
43
+
44
+ expect(json_response[:data][:userSendPasswordResetWithToken]).to include(
45
+ message: 'You will receive an email with instructions on how to reset your password in a few minutes.'
46
+ )
47
+
48
+ email = Nokogiri::HTML(ActionMailer::Base.deliveries.last.body.encoded)
49
+ link = email.css('a').first
50
+
51
+ expect(link['href']).to include(redirect_url + '?reset_password_token')
52
+ end
53
+ end
54
+ end
55
+
56
+ context 'when email address uses different casing' do
57
+ let(:email) { 'jWinnfield@wallaceinc.com' }
58
+
59
+ it 'honors devise configuration for case insensitive fields' do
60
+ expect { post_request }.to change(ActionMailer::Base.deliveries, :count).by(1)
61
+ expect(json_response[:data][:userSendPasswordResetWithToken]).to include(
62
+ message: 'You will receive an email with instructions on how to reset your password in a few minutes.'
63
+ )
64
+ end
65
+ end
66
+
67
+ context 'when user email is not found' do
68
+ let(:email) { 'nothere@gmail.com' }
69
+
70
+ before { post_request }
71
+
72
+ it 'returns an error' do
73
+ expect(json_response[:errors]).to contain_exactly(
74
+ hash_including(message: 'User was not found or was not logged in.', extensions: { code: 'USER_ERROR' })
75
+ )
76
+ end
77
+ end
78
+ end
@@ -8,7 +8,7 @@ RSpec.describe 'Sign Up process' do
8
8
  let(:name) { Faker::Name.name }
9
9
  let(:password) { Faker::Internet.password }
10
10
  let(:email) { Faker::Internet.email }
11
- let(:redirect) { Faker::Internet.url }
11
+ let(:redirect) { 'https://google.com' }
12
12
 
13
13
  context 'when using the user model' do
14
14
  let(:query) do
@@ -31,6 +31,24 @@ RSpec.describe 'Sign Up process' do
31
31
  GRAPHQL
32
32
  end
33
33
 
34
+ context 'when redirect_url is not whitelisted' do
35
+ let(:redirect) { 'https://not-safe.com' }
36
+
37
+ it 'returns a not whitelisted redirect url error' do
38
+ expect { post_request }.to(
39
+ not_change(User, :count)
40
+ .and(not_change(ActionMailer::Base.deliveries, :count))
41
+ )
42
+
43
+ expect(json_response[:errors]).to containing_exactly(
44
+ hash_including(
45
+ message: "Redirect to '#{redirect}' not allowed.",
46
+ extensions: { code: 'USER_ERROR' }
47
+ )
48
+ )
49
+ end
50
+ end
51
+
34
52
  context 'when params are correct' do
35
53
  it 'creates a new resource that requires confirmation' do
36
54
  expect { post_request }.to(
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe 'Update Password With Token' do
6
+ include_context 'with graphql query request'
7
+
8
+ let(:password) { '12345678' }
9
+ let(:password_confirmation) { password }
10
+
11
+ context 'when using the user model' do
12
+ let(:user) { create(:user, :confirmed) }
13
+ let(:query) do
14
+ <<-GRAPHQL
15
+ mutation {
16
+ userUpdatePasswordWithToken(
17
+ resetPasswordToken: "#{token}",
18
+ password: "#{password}",
19
+ passwordConfirmation: "#{password_confirmation}"
20
+ ) {
21
+ authenticatable { email }
22
+ credentials { accessToken }
23
+ }
24
+ }
25
+ GRAPHQL
26
+ end
27
+
28
+ context 'when reset password token is valid' do
29
+ let(:token) { user.send(:set_reset_password_token) }
30
+
31
+ it 'updates the password' do
32
+ expect do
33
+ post_request
34
+ user.reload
35
+ end.to change(user, :encrypted_password)
36
+
37
+ expect(user).to be_valid_password(password)
38
+ expect(json_response[:data][:userUpdatePasswordWithToken][:credentials]).to be_nil
39
+ expect(json_response[:data][:userUpdatePasswordWithToken][:authenticatable]).to include(email: user.email)
40
+ end
41
+
42
+ context 'when token has expired' do
43
+ it 'returns an expired token error' do
44
+ travel_to 10.hours.ago do
45
+ token
46
+ end
47
+
48
+ post_request
49
+
50
+ expect(json_response[:errors]).to contain_exactly(
51
+ hash_including(message: 'Reset password token is no longer valid.', extensions: { code: 'USER_ERROR' })
52
+ )
53
+ end
54
+ end
55
+
56
+ context 'when password confirmation does not match' do
57
+ let(:password_confirmation) { 'does not match' }
58
+
59
+ it 'returns an error' do
60
+ post_request
61
+
62
+ expect(json_response[:errors]).to contain_exactly(
63
+ hash_including(
64
+ message: 'Unable to update user password',
65
+ extensions: { code: 'USER_ERROR', detailed_errors: ["Password confirmation doesn't match Password"] }
66
+ )
67
+ )
68
+ end
69
+ end
70
+ end
71
+
72
+ context 'when reset password token is not found' do
73
+ let(:token) { user.send(:set_reset_password_token) + 'invalid' }
74
+
75
+ it 'returns an error' do
76
+ post_request
77
+
78
+ expect(json_response[:errors]).to contain_exactly(
79
+ hash_including(message: 'No user found for the specified reset token.', extensions: { code: 'USER_ERROR' })
80
+ )
81
+ end
82
+ end
83
+ end
84
+
85
+ context 'when using the admin model' do
86
+ let(:admin) { create(:admin, :confirmed) }
87
+ let(:query) do
88
+ <<-GRAPHQL
89
+ mutation {
90
+ adminUpdatePasswordWithToken(
91
+ resetPasswordToken: "#{token}",
92
+ password: "#{password}",
93
+ passwordConfirmation: "#{password_confirmation}"
94
+ ) {
95
+ authenticatable { email }
96
+ credentials { uid }
97
+ }
98
+ }
99
+ GRAPHQL
100
+ end
101
+
102
+ context 'when reset password token is valid' do
103
+ let(:token) { admin.send(:set_reset_password_token) }
104
+
105
+ it 'updates the password' do
106
+ expect do
107
+ post_request
108
+ admin.reload
109
+ end.to change(admin, :encrypted_password)
110
+
111
+ expect(admin).to be_valid_password(password)
112
+ expect(json_response[:data][:adminUpdatePasswordWithToken]).to include(
113
+ credentials: { uid: admin.email },
114
+ authenticatable: { email: admin.email }
115
+ )
116
+ end
117
+ end
118
+ end
119
+ end
@@ -54,6 +54,21 @@ RSpec.describe 'Check Password Token Requests' do
54
54
  expect(response.body).to include('uid=')
55
55
  expect(response.body).to include('expiry=')
56
56
  end
57
+
58
+ context 'when redirect_url is not whitelisted' do
59
+ let(:redirect_url) { 'https://not-safe.com' }
60
+
61
+ before { post_request }
62
+
63
+ it 'returns a not whitelisted redirect url error' do
64
+ expect(json_response[:errors]).to containing_exactly(
65
+ hash_including(
66
+ message: "Redirect to '#{redirect_url}' not allowed.",
67
+ extensions: { code: 'USER_ERROR' }
68
+ )
69
+ )
70
+ end
71
+ end
57
72
  end
58
73
 
59
74
  context 'when token has expired' do
@@ -74,7 +89,7 @@ RSpec.describe 'Check Password Token Requests' do
74
89
  context 'when reset password token is not found' do
75
90
  let(:token) { user.send(:set_reset_password_token) + 'invalid' }
76
91
 
77
- it 'redirects to redirect url' do
92
+ it 'returns an error message' do
78
93
  get_request
79
94
 
80
95
  expect(json_response[:errors]).to contain_exactly(
@@ -7,7 +7,7 @@ RSpec.describe 'Account confirmation' do
7
7
 
8
8
  context 'when using the user model' do
9
9
  let(:user) { create(:user, confirmed_at: nil) }
10
- let(:redirect) { Faker::Internet.url }
10
+ let(:redirect) { 'https://google.com' }
11
11
  let(:query) do
12
12
  <<-GRAPHQL
13
13
  {
@@ -43,6 +43,21 @@ RSpec.describe 'Account confirmation' do
43
43
  expect(user).to be_active_for_authentication
44
44
  end
45
45
 
46
+ context 'when redirect_url is not whitelisted' do
47
+ let(:redirect) { 'https://not-safe.com' }
48
+
49
+ it 'returns a not whitelisted redirect url error' do
50
+ expect { post_request }.to not_change(ActionMailer::Base.deliveries, :count)
51
+
52
+ expect(json_response[:errors]).to containing_exactly(
53
+ hash_including(
54
+ message: "Redirect to '#{redirect}' not allowed.",
55
+ extensions: { code: 'USER_ERROR' }
56
+ )
57
+ )
58
+ end
59
+ end
60
+
46
61
  context 'when unconfirmed_email is present' do
47
62
  let(:user) { create(:user, :confirmed, unconfirmed_email: 'vvega@wallaceinc.com') }
48
63
 
@@ -81,7 +96,7 @@ RSpec.describe 'Account confirmation' do
81
96
 
82
97
  context 'when using the admin model' do
83
98
  let(:admin) { create(:admin, confirmed_at: nil) }
84
- let(:redirect) { Faker::Internet.url }
99
+ let(:redirect) { 'https://google.com' }
85
100
  let(:query) do
86
101
  <<-GRAPHQL
87
102
  {
@@ -31,15 +31,6 @@ RSpec.describe "Integrations with the user's controller" do
31
31
  expect(json_response[:data][:publicField]).to eq('Field does not require authentication')
32
32
  end
33
33
  end
34
-
35
- context 'when using the failing route' do
36
- it 'raises an invalid resource_name error' do
37
- expect { post_request('/api/v1/failing') }.to raise_error(
38
- GraphqlDevise::Error,
39
- 'Invalid resource_name `fail` provided to `graphql_context`. Possible values are: [:user, :admin, :guest, :users_customer, :schema_user].'
40
- )
41
- end
42
- end
43
34
  end
44
35
 
45
36
  describe 'privateField' do
@@ -77,6 +68,15 @@ RSpec.describe "Integrations with the user's controller" do
77
68
  )
78
69
  end
79
70
  end
71
+
72
+ context 'when using the failing route' do
73
+ it 'raises an invalid resource_name error' do
74
+ expect { post_request('/api/v1/failing') }.to raise_error(
75
+ GraphqlDevise::Error,
76
+ 'Invalid resource_name `fail` provided to `graphql_context`. Possible values are: [:user, :admin, :guest, :users_customer, :schema_user].'
77
+ )
78
+ end
79
+ end
80
80
  end
81
81
 
82
82
  context 'when using an interpreter schema' do
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context 'with graphql schema test' do
4
+ let(:variables) { {} }
5
+ let(:resource_names) { [:user] }
6
+ let(:resource) { nil }
7
+ let(:controller) { instance_double(GraphqlDevise::GraphqlController) }
8
+ let(:context) do
9
+ { current_resource: resource, controller: controller, resource_name: resource_names }
10
+ end
11
+ let(:response) do
12
+ schema.execute(query, context: context, variables: variables).deep_symbolize_keys
13
+ end
14
+ end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql_devise
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.3
4
+ version: 0.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mario Celi
8
8
  - David Revelo
9
- autorequire:
9
+ autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-08-13 00:00:00.000000000 Z
12
+ date: 2021-02-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: devise_token_auth
@@ -40,7 +40,7 @@ dependencies:
40
40
  version: '1.8'
41
41
  - - "<"
42
42
  - !ruby/object:Gem::Version
43
- version: 1.12.0
43
+ version: 1.13.0
44
44
  type: :runtime
45
45
  prerelease: false
46
46
  version_requirements: !ruby/object:Gem::Requirement
@@ -50,7 +50,7 @@ dependencies:
50
50
  version: '1.8'
51
51
  - - "<"
52
52
  - !ruby/object:Gem::Version
53
- version: 1.12.0
53
+ version: 1.13.0
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: rails
56
56
  requirement: !ruby/object:Gem::Requirement
@@ -302,6 +302,7 @@ files:
302
302
  - bin/setup
303
303
  - config/locales/en.yml
304
304
  - config/routes.rb
305
+ - docs/usage/reset_password_flow.md
305
306
  - graphql_devise.gemspec
306
307
  - lib/generators/graphql_devise/install_generator.rb
307
308
  - lib/graphql_devise.rb
@@ -338,8 +339,10 @@ files:
338
339
  - lib/graphql_devise/mutations/logout.rb
339
340
  - lib/graphql_devise/mutations/resend_confirmation.rb
340
341
  - lib/graphql_devise/mutations/send_password_reset.rb
342
+ - lib/graphql_devise/mutations/send_password_reset_with_token.rb
341
343
  - lib/graphql_devise/mutations/sign_up.rb
342
344
  - lib/graphql_devise/mutations/update_password.rb
345
+ - lib/graphql_devise/mutations/update_password_with_token.rb
343
346
  - lib/graphql_devise/rails/routes.rb
344
347
  - lib/graphql_devise/resolvers/base.rb
345
348
  - lib/graphql_devise/resolvers/check_password_token.rb
@@ -362,6 +365,7 @@ files:
362
365
  - spec/dummy/app/graphql/interpreter_schema.rb
363
366
  - spec/dummy/app/graphql/mutations/login.rb
364
367
  - spec/dummy/app/graphql/mutations/register_confirmed_user.rb
368
+ - spec/dummy/app/graphql/mutations/reset_admin_password_with_token.rb
365
369
  - spec/dummy/app/graphql/mutations/sign_up.rb
366
370
  - spec/dummy/app/graphql/mutations/update_user.rb
367
371
  - spec/dummy/app/graphql/resolvers/confirm_admin_account.rb
@@ -428,6 +432,7 @@ files:
428
432
  - spec/factories/users.rb
429
433
  - spec/factories/users_customers.rb
430
434
  - spec/generators/graphql_devise/install_generator_spec.rb
435
+ - spec/graphql/user_queries_spec.rb
431
436
  - spec/graphql_devise/model/with_email_updater_spec.rb
432
437
  - spec/graphql_devise_spec.rb
433
438
  - spec/models/user_spec.rb
@@ -439,8 +444,10 @@ files:
439
444
  - spec/requests/mutations/logout_spec.rb
440
445
  - spec/requests/mutations/resend_confirmation_spec.rb
441
446
  - spec/requests/mutations/send_password_reset_spec.rb
447
+ - spec/requests/mutations/send_password_reset_with_token_spec.rb
442
448
  - spec/requests/mutations/sign_up_spec.rb
443
449
  - spec/requests/mutations/update_password_spec.rb
450
+ - spec/requests/mutations/update_password_with_token_spec.rb
444
451
  - spec/requests/queries/check_password_token_spec.rb
445
452
  - spec/requests/queries/confirm_account_spec.rb
446
453
  - spec/requests/user_controller_spec.rb
@@ -465,6 +472,7 @@ files:
465
472
  - spec/services/schema_plugin_spec.rb
466
473
  - spec/spec_helper.rb
467
474
  - spec/support/contexts/graphql_request.rb
475
+ - spec/support/contexts/schema_test.rb
468
476
  - spec/support/factory_bot.rb
469
477
  - spec/support/matchers/auth_headers_matcher.rb
470
478
  - spec/support/matchers/not_change_matcher.rb
@@ -476,7 +484,7 @@ licenses:
476
484
  metadata:
477
485
  homepage_uri: https://github.com/graphql-devise/graphql_devise
478
486
  source_code_uri: https://github.com/graphql-devise/graphql_devise
479
- post_install_message:
487
+ post_install_message:
480
488
  rdoc_options: []
481
489
  require_paths:
482
490
  - lib
@@ -491,8 +499,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
491
499
  - !ruby/object:Gem::Version
492
500
  version: '0'
493
501
  requirements: []
494
- rubygems_version: 3.1.4
495
- signing_key:
502
+ rubygems_version: 3.0.8
503
+ signing_key:
496
504
  specification_version: 4
497
505
  summary: GraphQL queries and mutations on top of devise_token_auth
498
506
  test_files:
@@ -505,6 +513,7 @@ test_files:
505
513
  - spec/dummy/app/graphql/interpreter_schema.rb
506
514
  - spec/dummy/app/graphql/mutations/login.rb
507
515
  - spec/dummy/app/graphql/mutations/register_confirmed_user.rb
516
+ - spec/dummy/app/graphql/mutations/reset_admin_password_with_token.rb
508
517
  - spec/dummy/app/graphql/mutations/sign_up.rb
509
518
  - spec/dummy/app/graphql/mutations/update_user.rb
510
519
  - spec/dummy/app/graphql/resolvers/confirm_admin_account.rb
@@ -571,6 +580,7 @@ test_files:
571
580
  - spec/factories/users.rb
572
581
  - spec/factories/users_customers.rb
573
582
  - spec/generators/graphql_devise/install_generator_spec.rb
583
+ - spec/graphql/user_queries_spec.rb
574
584
  - spec/graphql_devise/model/with_email_updater_spec.rb
575
585
  - spec/graphql_devise_spec.rb
576
586
  - spec/models/user_spec.rb
@@ -582,8 +592,10 @@ test_files:
582
592
  - spec/requests/mutations/logout_spec.rb
583
593
  - spec/requests/mutations/resend_confirmation_spec.rb
584
594
  - spec/requests/mutations/send_password_reset_spec.rb
595
+ - spec/requests/mutations/send_password_reset_with_token_spec.rb
585
596
  - spec/requests/mutations/sign_up_spec.rb
586
597
  - spec/requests/mutations/update_password_spec.rb
598
+ - spec/requests/mutations/update_password_with_token_spec.rb
587
599
  - spec/requests/queries/check_password_token_spec.rb
588
600
  - spec/requests/queries/confirm_account_spec.rb
589
601
  - spec/requests/user_controller_spec.rb
@@ -608,6 +620,7 @@ test_files:
608
620
  - spec/services/schema_plugin_spec.rb
609
621
  - spec/spec_helper.rb
610
622
  - spec/support/contexts/graphql_request.rb
623
+ - spec/support/contexts/schema_test.rb
611
624
  - spec/support/factory_bot.rb
612
625
  - spec/support/matchers/auth_headers_matcher.rb
613
626
  - spec/support/matchers/not_change_matcher.rb