diff --git a/app/helpers/graphql_devise/mailer_helper.rb b/app/helpers/graphql_devise/mailer_helper.rb deleted file mode 100644 index d8c7faee..00000000 --- a/app/helpers/graphql_devise/mailer_helper.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -module GraphqlDevise - module MailerHelper - def confirmation_query(resource_name:, token:, redirect_url:) - name = "#{GraphqlDevise.to_mapping_name(resource_name).camelize(:lower)}ConfirmAccount" - raw = <<-GRAPHQL - query($token:String!,$redirectUrl:String!){ - #{name}(confirmationToken:$token,redirectUrl:$redirectUrl){ - email - } - } - GRAPHQL - - { - query: raw.delete("\n").delete(' ').html_safe, - variables: { token: token, redirectUrl: redirect_url } - } - end - - def password_reset_query(token:, redirect_url:, resource_name:) - name = "#{GraphqlDevise.to_mapping_name(resource_name).camelize(:lower)}CheckPasswordToken" - raw = <<-GRAPHQL - query($token:String!,$redirectUrl:String!){ - #{name}(resetPasswordToken:$token,redirectUrl:$redirectUrl){ - email - } - } - GRAPHQL - - { - query: raw.delete("\n").delete(' ').html_safe, - variables: { token: token, redirectUrl: redirect_url } - } - end - end -end diff --git a/app/views/graphql_devise/mailer/confirmation_instructions.html.erb b/app/views/graphql_devise/mailer/confirmation_instructions.html.erb index 88f12751..55b8c65a 100644 --- a/app/views/graphql_devise/mailer/confirmation_instructions.html.erb +++ b/app/views/graphql_devise/mailer/confirmation_instructions.html.erb @@ -3,9 +3,5 @@

<%= t('.confirm_link_msg') %>

- <% if message['schema_url'].present? %> - <%= link_to t('.confirm_account_link'), "#{message['schema_url']}?#{confirmation_query(resource_name: @resource.class.to_s, redirect_url: message['redirect-url'], token: @token).to_query}" %> - <% else %> - <%= link_to t('.confirm_account_link'), "#{CGI.escape(message['redirect-url'].to_s)}?#{{ confirmationToken: @token }.to_query}" %> - <% end %> + <%= link_to t('.confirm_account_link'), "#{message['redirect-url'].to_s}?#{{ confirmationToken: @token }.to_query}" %>

diff --git a/config/routes.rb b/config/routes.rb index cdfaaad1..a31d7b46 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,7 +14,5 @@ GraphqlDevise::Schema.query(GraphqlDevise::Types::QueryType) GraphqlDevise.load_schema - - Devise.mailer.helper(GraphqlDevise::MailerHelper) end end diff --git a/lib/graphql_devise/default_operations/mutations.rb b/lib/graphql_devise/default_operations/mutations.rb index f7e7504b..55e1bcf3 100644 --- a/lib/graphql_devise/default_operations/mutations.rb +++ b/lib/graphql_devise/default_operations/mutations.rb @@ -3,13 +3,9 @@ require 'graphql_devise/mutations/base' require 'graphql_devise/mutations/login' require 'graphql_devise/mutations/logout' -require 'graphql_devise/mutations/resend_confirmation' require 'graphql_devise/mutations/resend_confirmation_with_token' -require 'graphql_devise/mutations/send_password_reset' require 'graphql_devise/mutations/send_password_reset_with_token' -require 'graphql_devise/mutations/sign_up' require 'graphql_devise/mutations/register' -require 'graphql_devise/mutations/update_password' require 'graphql_devise/mutations/update_password_with_token' require 'graphql_devise/mutations/confirm_registration_with_token' @@ -18,13 +14,9 @@ module DefaultOperations MUTATIONS = { login: { klass: GraphqlDevise::Mutations::Login, authenticatable: true }, logout: { klass: GraphqlDevise::Mutations::Logout, authenticatable: true }, - sign_up: { klass: GraphqlDevise::Mutations::SignUp, authenticatable: true, deprecation_reason: 'use register instead' }, register: { klass: GraphqlDevise::Mutations::Register, authenticatable: true }, - update_password: { klass: GraphqlDevise::Mutations::UpdatePassword, authenticatable: true, deprecation_reason: 'use update_password_with_token instead' }, update_password_with_token: { klass: GraphqlDevise::Mutations::UpdatePasswordWithToken, authenticatable: true }, - send_password_reset: { klass: GraphqlDevise::Mutations::SendPasswordReset, authenticatable: false, deprecation_reason: 'use send_password_reset_with_token instead' }, send_password_reset_with_token: { klass: GraphqlDevise::Mutations::SendPasswordResetWithToken, authenticatable: false }, - resend_confirmation: { klass: GraphqlDevise::Mutations::ResendConfirmation, authenticatable: false, deprecation_reason: 'use resend_confirmation_with_token instead' }, resend_confirmation_with_token: { klass: GraphqlDevise::Mutations::ResendConfirmationWithToken, authenticatable: false }, confirm_registration_with_token: { klass: GraphqlDevise::Mutations::ConfirmRegistrationWithToken, authenticatable: true } }.freeze diff --git a/lib/graphql_devise/default_operations/resolvers.rb b/lib/graphql_devise/default_operations/resolvers.rb index 28c7775d..4d0a9225 100644 --- a/lib/graphql_devise/default_operations/resolvers.rb +++ b/lib/graphql_devise/default_operations/resolvers.rb @@ -1,14 +1,9 @@ # frozen_string_literal: true require 'graphql_devise/resolvers/base' -require 'graphql_devise/resolvers/check_password_token' -require 'graphql_devise/resolvers/confirm_account' module GraphqlDevise module DefaultOperations - QUERIES = { - confirm_account: { klass: GraphqlDevise::Resolvers::ConfirmAccount, deprecation_reason: 'use the new confirmation flow as it does not require this query anymore' }, - check_password_token: { klass: GraphqlDevise::Resolvers::CheckPasswordToken, deprecation_reason: 'use the new password reset flow as it does not require this query anymore' } - }.freeze + QUERIES = {}.freeze end end diff --git a/lib/graphql_devise/model/with_email_updater.rb b/lib/graphql_devise/model/with_email_updater.rb index cc93d648..fe284e16 100644 --- a/lib/graphql_devise/model/with_email_updater.rb +++ b/lib/graphql_devise/model/with_email_updater.rb @@ -9,9 +9,7 @@ def initialize(resource, attributes) end def call - check_deprecated_attributes - - resource_attributes = @attributes.except(:schema_url, :confirmation_success_url, :confirmation_url) + resource_attributes = @attributes.except(:confirmation_url) return @resource.update(resource_attributes) unless requires_reconfirmation?(resource_attributes) @resource.assign_attributes(resource_attributes) @@ -36,24 +34,8 @@ def call private - def check_deprecated_attributes - if [@attributes[:schema_url], @attributes[:confirmation_success_url]].any?(&:present?) - ActiveSupport::Deprecation.warn(<<-DEPRECATION.strip_heredoc, caller) - Providing `schema_url` and `confirmation_success_url` to `update_with_email` is deprecated and will be - removed in a future version of this gem. - - Now you must only provide `confirmation_url` and the email will contain the new format of the confirmation - url that needs to be used with the new `confirmRegistrationWithToken` on the client application. - DEPRECATION - end - end - def required_reconfirm_attributes? - if @attributes[:schema_url].present? - [@attributes[:confirmation_success_url], DeviseTokenAuth.default_confirm_success_url].any?(&:present?) - else - [@attributes[:confirmation_url], DeviseTokenAuth.default_confirm_success_url].any?(&:present?) - end + [@attributes[:confirmation_url], DeviseTokenAuth.default_confirm_success_url].any?(&:present?) end def requires_reconfirmation?(resource_attributes) @@ -78,14 +60,7 @@ def email_in_database end def confirmation_method_params - if @attributes[:schema_url].present? - { - redirect_url: @attributes[:confirmation_success_url] || DeviseTokenAuth.default_confirm_success_url, - schema_url: @attributes[:schema_url] - } - else - { redirect_url: @attributes[:confirmation_url] || DeviseTokenAuth.default_confirm_success_url } - end + { redirect_url: @attributes[:confirmation_url] || DeviseTokenAuth.default_confirm_success_url } end def send_confirmation_instructions(saved) diff --git a/lib/graphql_devise/mutations/resend_confirmation.rb b/lib/graphql_devise/mutations/resend_confirmation.rb deleted file mode 100644 index a6c58f1a..00000000 --- a/lib/graphql_devise/mutations/resend_confirmation.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -module GraphqlDevise - module Mutations - class ResendConfirmation < Base - argument :email, String, required: true, prepare: ->(email, _) { email.downcase } - argument :redirect_url, String, required: true - - field :message, String, null: false - - def resolve(email:, redirect_url:) - check_redirect_url_whitelist!(redirect_url) - - resource = find_confirmable_resource(email) - - if resource - yield resource if block_given? - - if resource.confirmed? && !resource.pending_reconfirmation? - raise_user_error(I18n.t('graphql_devise.confirmations.already_confirmed')) - end - - resource.send_confirmation_instructions( - redirect_url: redirect_url, - template_path: ['graphql_devise/mailer'], - schema_url: controller.full_url_without_params - ) - - { message: I18n.t('graphql_devise.confirmations.send_instructions', email: email) } - else - raise_user_error(I18n.t('graphql_devise.confirmations.user_not_found', email: email)) - end - end - - private - - def find_confirmable_resource(email) - email_insensitive = get_case_insensitive_field(:email, email) - resource = find_resource(:unconfirmed_email, email_insensitive) if resource_class.reconfirmable - resource ||= find_resource(:email, email_insensitive) - resource - end - end - end -end diff --git a/lib/graphql_devise/mutations/send_password_reset.rb b/lib/graphql_devise/mutations/send_password_reset.rb deleted file mode 100644 index e2dda246..00000000 --- a/lib/graphql_devise/mutations/send_password_reset.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module GraphqlDevise - module Mutations - class SendPasswordReset < Base - argument :email, String, required: true - argument :redirect_url, String, required: true - - field :message, String, null: false - - def resolve(email:, redirect_url:) - check_redirect_url_whitelist!(redirect_url) - - resource = find_resource(:email, get_case_insensitive_field(:email, email)) - - if resource - yield resource if block_given? - - resource.send_reset_password_instructions( - email: email, - provider: 'email', - redirect_url: redirect_url, - template_path: ['graphql_devise/mailer'], - schema_url: controller.full_url_without_params - ) - - if resource.errors.empty? - { message: I18n.t('graphql_devise.passwords.send_instructions') } - else - raise_user_error_list(I18n.t('graphql_devise.invalid_resource'), errors: resource.errors.full_messages) - end - else - raise_user_error(I18n.t('graphql_devise.user_not_found')) - end - end - end - end -end diff --git a/lib/graphql_devise/mutations/sign_up.rb b/lib/graphql_devise/mutations/sign_up.rb deleted file mode 100644 index 3b19917e..00000000 --- a/lib/graphql_devise/mutations/sign_up.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -module GraphqlDevise - module Mutations - class SignUp < Base - argument :email, String, required: true - argument :password, String, required: true - argument :password_confirmation, String, required: true - argument :confirm_success_url, String, required: false - - field :credentials, - GraphqlDevise::Types::CredentialType, - null: true, - description: 'Authentication credentials. Null if after signUp resource is not active for authentication (e.g. Email confirmation required).' - - def resolve(confirm_success_url: nil, **attrs) - resource = build_resource(attrs.merge(provider: provider)) - raise_user_error(I18n.t('graphql_devise.resource_build_failed')) if resource.blank? - - redirect_url = confirm_success_url || DeviseTokenAuth.default_confirm_success_url - if confirmable_enabled? && redirect_url.blank? - raise_user_error(I18n.t('graphql_devise.registrations.missing_confirm_redirect_url')) - end - - check_redirect_url_whitelist!(redirect_url) - - resource.skip_confirmation_notification! if resource.respond_to?(:skip_confirmation_notification!) - - if resource.save - yield resource if block_given? - - unless resource.confirmed? - resource.send_confirmation_instructions( - redirect_url: redirect_url, - template_path: ['graphql_devise/mailer'], - schema_url: controller.full_url_without_params - ) - end - - response_payload = { authenticatable: resource } - - response_payload[:credentials] = set_auth_headers(resource) if resource.active_for_authentication? - - response_payload - else - resource.try(:clean_up_passwords) - raise_user_error_list( - I18n.t('graphql_devise.registration_failed'), - errors: resource.errors.full_messages - ) - end - end - - private - - def build_resource(attrs) - resource_class.new(attrs) - end - end - end -end diff --git a/lib/graphql_devise/mutations/update_password.rb b/lib/graphql_devise/mutations/update_password.rb deleted file mode 100644 index ce32c4d2..00000000 --- a/lib/graphql_devise/mutations/update_password.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -module GraphqlDevise - module Mutations - class UpdatePassword < Base - argument :password, String, required: true - argument :password_confirmation, String, required: true - argument :current_password, String, required: false - - def resolve(current_password: nil, **attrs) - if current_resource.blank? - raise_user_error(I18n.t('graphql_devise.not_authenticated')) - elsif current_resource.provider != 'email' - raise_user_error( - I18n.t('graphql_devise.passwords.password_not_required', provider: current_resource.provider.humanize) - ) - end - - if update_resource_password(current_password, attrs) - current_resource.allow_password_change = false if recoverable_enabled? - current_resource.save! - - yield current_resource if block_given? - - { authenticatable: current_resource } - else - raise_user_error_list( - I18n.t('graphql_devise.passwords.update_password_error'), - errors: current_resource.errors.full_messages - ) - end - end - - private - - def update_resource_password(current_password, attrs) - allow_password_change = recoverable_enabled? && current_resource.allow_password_change == true - if DeviseTokenAuth.check_current_password_before_update == false || allow_password_change - current_resource.public_send(:update, attrs) - else - current_resource.public_send(:update_with_password, attrs.merge(current_password: current_password)) - end - end - end - end -end diff --git a/lib/graphql_devise/resolvers/check_password_token.rb b/lib/graphql_devise/resolvers/check_password_token.rb deleted file mode 100644 index 4d91bbad..00000000 --- a/lib/graphql_devise/resolvers/check_password_token.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -module GraphqlDevise - module Resolvers - class CheckPasswordToken < Base - argument :reset_password_token, String, required: true - argument :redirect_url, String, required: false - - def resolve(reset_password_token:, redirect_url: nil) - resource = resource_class.with_reset_password_token(reset_password_token) - raise_user_error(I18n.t('graphql_devise.passwords.reset_token_not_found')) if resource.blank? - - if resource.reset_password_period_valid? - token_info = client_and_token(resource.create_token) - - resource.skip_confirmation! if confirmable_enabled? && !resource.confirmed_at - resource.allow_password_change = true if recoverable_enabled? - - resource.save! - - yield resource if block_given? - - redirect_header_options = { reset_password: true } - built_redirect_headers = redirect_headers( - token_info, - redirect_header_options - ) - - if redirect_url.present? - check_redirect_url_whitelist!(redirect_url) - controller.redirect_to(resource.build_auth_url(redirect_url, built_redirect_headers)) - else - set_auth_headers(resource) - end - - resource - else - raise_user_error(I18n.t('graphql_devise.passwords.reset_token_expired')) - end - end - end - end -end diff --git a/lib/graphql_devise/resolvers/confirm_account.rb b/lib/graphql_devise/resolvers/confirm_account.rb deleted file mode 100644 index 5bdf6018..00000000 --- a/lib/graphql_devise/resolvers/confirm_account.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true - -module GraphqlDevise - module Resolvers - class ConfirmAccount < Base - argument :confirmation_token, String, required: true - argument :redirect_url, String, required: true - - def resolve(confirmation_token:, redirect_url:) - check_redirect_url_whitelist!(redirect_url) - - resource = resource_class.confirm_by_token(confirmation_token) - - if resource.errors.empty? - yield resource if block_given? - - redirect_header_options = { account_confirmation_success: true } - - redirect_to_link = if controller.signed_in?(resource_name) - url = resource.build_auth_url( - redirect_url, - redirect_headers( - client_and_token(resource.create_token), - redirect_header_options - ) - ) - resource.save! - - url - else - DeviseTokenAuth::Url.generate(redirect_url, redirect_header_options) - end - - controller.redirect_to(redirect_to_link) - resource - else - raise_user_error(I18n.t('graphql_devise.confirmations.invalid_token')) - end - end - end - end -end diff --git a/spec/dummy/app/graphql/dummy_schema.rb b/spec/dummy/app/graphql/dummy_schema.rb index 896deda4..2ecd2193 100644 --- a/spec/dummy/app/graphql/dummy_schema.rb +++ b/spec/dummy/app/graphql/dummy_schema.rb @@ -10,11 +10,7 @@ class DummySchema < GraphQL::Schema User, only: [ :login, - :confirm_account, - :send_password_reset, - :resend_confirmation, :resend_confirmation_with_token, - :check_password_token ] ), GraphqlDevise::ResourceLoader.new(Guest, only: [:logout]), diff --git a/spec/dummy/app/graphql/mutations/sign_up.rb b/spec/dummy/app/graphql/mutations/sign_up.rb deleted file mode 100644 index 4c4926d0..00000000 --- a/spec/dummy/app/graphql/mutations/sign_up.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -module Mutations - class SignUp < GraphqlDevise::Mutations::SignUp - argument :name, String, required: false - - field :user, Types::UserType, null: true - - def resolve(email:, **attrs) - original_payload = super - original_payload.merge(user: original_payload[:authenticatable]) - end - end -end diff --git a/spec/dummy/app/graphql/mutations/update_user.rb b/spec/dummy/app/graphql/mutations/update_user.rb index 21e60990..f5e3b201 100644 --- a/spec/dummy/app/graphql/mutations/update_user.rb +++ b/spec/dummy/app/graphql/mutations/update_user.rb @@ -10,10 +10,8 @@ class UpdateUser < GraphQL::Schema::Mutation def resolve(**attrs) user = context[:current_resource] - schema_url = context[:controller].full_url_without_params - user.update_with_email( - attrs.merge(schema_url: schema_url, confirmation_success_url: 'https://google.com') + attrs.merge(confirmation_url: 'https://google.com') ) { user: user } diff --git a/spec/dummy/app/graphql/resolvers/confirm_admin_account.rb b/spec/dummy/app/graphql/resolvers/confirm_admin_account.rb deleted file mode 100644 index 7a2589ec..00000000 --- a/spec/dummy/app/graphql/resolvers/confirm_admin_account.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module Resolvers - class ConfirmAdminAccount < GraphqlDevise::Resolvers::ConfirmAccount - type Types::AdminType, null: false - - def resolve(confirmation_token:, redirect_url:) - super do |admin| - controller.sign_in(admin) - end - end - end -end diff --git a/spec/dummy/config/routes.rb b/spec/dummy/config/routes.rb index f47eec32..de58b1f5 100644 --- a/spec/dummy/config/routes.rb +++ b/spec/dummy/config/routes.rb @@ -3,7 +3,6 @@ Rails.application.routes.draw do mount_graphql_devise_for 'User', at: '/api/v1/graphql_auth', operations: { login: Mutations::Login, - sign_up: Mutations::SignUp, register: Mutations::Register }, additional_mutations: { register_confirmed_user: Mutations::RegisterConfirmedUser @@ -14,9 +13,8 @@ mount_graphql_devise_for( Admin, authenticatable_type: Types::CustomAdminType, - skip: [:sign_up, :register, :check_password_token], + skip: [:register], operations: { - confirm_account: Resolvers::ConfirmAdminAccount, update_password_with_token: Mutations::ResetAdminPasswordWithToken }, at: '/api/v1/admin/graphql_auth' @@ -24,7 +22,7 @@ mount_graphql_devise_for( 'Guest', - only: [:login, :logout, :sign_up, :register], + only: [:login, :logout, :register], at: '/api/v1/guest/graphql_auth' ) diff --git a/spec/graphql_devise/model/with_email_updater_spec.rb b/spec/graphql_devise/model/with_email_updater_spec.rb index 6f65cf45..4da238cd 100644 --- a/spec/graphql_devise/model/with_email_updater_spec.rb +++ b/spec/graphql_devise/model/with_email_updater_spec.rb @@ -61,7 +61,7 @@ let(:resource) { create(:admin, :confirmed) } context 'when attributes contain email' do - let(:attributes) { { email: 'new@gmail.com', schema_url: 'http://localhost/test', confirmation_success_url: 'https://google.com' } } + let(:attributes) { { email: 'new@gmail.com', confirmation_url: 'https://google.com' } } it 'does not postpone email update' do expect do @@ -78,7 +78,7 @@ let(:resource) { create(:user, :confirmed) } context 'when attributes do not contain email' do - let(:attributes) { { name: 'Updated Name', schema_url: 'http://localhost/test', confirmation_success_url: 'https://google.com' } } + let(:attributes) { { name: 'Updated Name', confirmation_url: 'https://google.com' } } it 'updates resource, ignores url params' do expect do @@ -89,38 +89,6 @@ end context 'when attributes contain email' do - context 'when confirmation_success_url is used' do - it_behaves_like 'all required arguments are provided', schema_url: 'http://localhost/test', confirmation_success_url: 'https://google.com' - - context 'when confirmation_success_url is missing and no default is set' do - let(:attributes) { { email: 'new@gmail.com', name: 'Updated Name', schema_url: 'http://localhost/test' } } - - before { allow(DeviseTokenAuth).to receive(:default_confirm_success_url).and_return(nil) } - - it 'raises an error' do - expect { updater }.to raise_error( - GraphqlDevise::Error, - 'Method `update_with_email` requires attribute `confirmation_url` for email reconfirmation to work' - ) - end - - context 'when email will not change' do - let(:attributes) { { email: resource.email, name: 'changed', confirmation_success_url: 'https://google.com' } } - - it 'updates name and does not raise an error' do - expect do - updater - resource.reload - end.to change(resource, :name).from(resource.name).to('changed').and( - not_change(resource, :email).from(resource.email) - ).and( - not_change(ActionMailer::Base.deliveries, :count).from(0) - ) - end - end - end - end - context 'when confirm_url is used' do it_behaves_like 'all required arguments are provided', confirmation_url: 'https://google.com' @@ -130,28 +98,14 @@ end context 'when no confirmation url is provided is provided' do - context 'when schema_url is provided' do - let(:attributes) { { email: 'new@gmail.com', name: 'Updated Name', schema_url: 'http://localhost/test' } } - - it 'uses DTA default_confirm_success_url on the email with redirect flow' do - expect { updater }.to change(ActionMailer::Base.deliveries, :count).by(1) - - email = ActionMailer::Base.deliveries.first - expect(email.body.decoded).to include(CGI.escape('https://google.com')) - expect(email.body.decoded).to include(CGI.escape('ConfirmAccount(')) - end - end - - context 'when schema_url is not provided' do - let(:attributes) { { email: 'new@gmail.com', name: 'Updated Name' } } + let(:attributes) { { email: 'new@gmail.com', name: 'Updated Name' } } - it 'uses DTA default_confirm_success_url on the email and new confirmation flow' do - expect { updater }.to change(ActionMailer::Base.deliveries, :count).by(1) + it 'uses DTA default_confirm_success_url on the email and new confirmation flow' do + expect { updater }.to change(ActionMailer::Base.deliveries, :count).by(1) - email = ActionMailer::Base.deliveries.first - expect(email.body.decoded).to include(CGI.escape('https://google.com')) - expect(email.body.decoded).to include('?confirmationToken=') - end + email = ActionMailer::Base.deliveries.first + expect(email.body.decoded).to include('https://google.com') + expect(email.body.decoded).to include('?confirmationToken=') end end end diff --git a/spec/requests/mutations/resend_confirmation_spec.rb b/spec/requests/mutations/resend_confirmation_spec.rb deleted file mode 100644 index a2ce888d..00000000 --- a/spec/requests/mutations/resend_confirmation_spec.rb +++ /dev/null @@ -1,153 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe 'Resend confirmation' do - include_context 'with graphql query request' - - let(:confirmed_at) { nil } - let!(:user) { create(:user, confirmed_at: nil, email: 'mwallace@wallaceinc.com') } - let(:email) { user.email } - let(:id) { user.id } - let(:redirect) { 'https://google.com' } - let(:query) do - <<-GRAPHQL - mutation { - userResendConfirmation( - email:"#{email}", - redirectUrl:"#{redirect}" - ) { - message - } - } - GRAPHQL - end - - context 'when redirect_url is not whitelisted' do - let(:redirect) { 'https://not-safe.com' } - - it 'returns a not whitelisted redirect url error' do - expect { post_request }.to not_change(ActionMailer::Base.deliveries, :count) - - expect(json_response[:errors]).to containing_exactly( - hash_including( - message: "Redirect to '#{redirect}' not allowed.", - extensions: { code: 'USER_ERROR' } - ) - ) - end - end - - context 'when params are correct' do - context 'when using the gem schema' do - it 'sends an email to the user with confirmation url and returns a success message' do - expect { post_request }.to change(ActionMailer::Base.deliveries, :count).by(1) - expect(json_response[:data][:userResendConfirmation]).to include( - message: 'You will receive an email with instructions for how to confirm your email address in a few minutes.' - ) - - email = Nokogiri::HTML(ActionMailer::Base.deliveries.last.body.encoded) - link = email.css('a').first - confirm_link_msg_text = email.css('p')[1].inner_html - confirm_account_link_text = link.inner_html - - expect(link['href']).to include('/api/v1/graphql_auth?') - expect(confirm_link_msg_text).to eq('You can confirm your account email through the link below:') - expect(confirm_account_link_text).to eq('Confirm my account') - - expect do - get link['href'] - user.reload - end.to change(user, :confirmed_at).from(NilClass).to(ActiveSupport::TimeWithZone) - end - end - - context 'when using a custom schema' do - let(:custom_path) { '/api/v1/graphql' } - - it 'sends an email to the user with confirmation url and returns a success message' do - expect { post_request(custom_path) }.to change(ActionMailer::Base.deliveries, :count).by(1) - expect(json_response[:data][:userResendConfirmation]).to include( - message: 'You will receive an email with instructions for how to confirm your email address in a few minutes.' - ) - - email = Nokogiri::HTML(ActionMailer::Base.deliveries.last.body.encoded) - link = email.css('a').first - confirm_link_msg_text = email.css('p')[1].inner_html - confirm_account_link_text = link.inner_html - - expect(link['href']).to include("#{custom_path}?") - expect(confirm_link_msg_text).to eq('You can confirm your account email through the link below:') - expect(confirm_account_link_text).to eq('Confirm my account') - - expect do - get link['href'] - user.reload - end.to change(user, :confirmed_at).from(NilClass).to(ActiveSupport::TimeWithZone) - end - end - - context 'when email address uses different casing' do - let(:email) { 'mWallace@wallaceinc.com' } - - it 'honors devise configuration for case insensitive fields' do - expect { post_request }.to change(ActionMailer::Base.deliveries, :count).by(1) - expect(json_response[:data][:userResendConfirmation]).to include( - message: 'You will receive an email with instructions for how to confirm your email address in a few minutes.' - ) - end - end - - context 'when the user has already been confirmed' do - before { user.confirm } - - it 'does *NOT* send an email and raises an error' do - expect { post_request }.to not_change(ActionMailer::Base.deliveries, :count) - expect(json_response[:data][:userResendConfirmation]).to be_nil - expect(json_response[:errors]).to contain_exactly( - hash_including( - message: 'Email was already confirmed, please try signing in', - extensions: { code: 'USER_ERROR' } - ) - ) - end - end - end - - context 'when the email was changed' do - let(:confirmed_at) { 2.seconds.ago } - let(:email) { 'new-email@wallaceinc.com' } - let(:new_email) { email } - - before do - user.update_with_email( - email: new_email, - schema_url: 'http://localhost/test', - confirmation_success_url: 'https://google.com' - ) - end - - it 'sends new confirmation email' do - expect { post_request }.to change(ActionMailer::Base.deliveries, :count).by(1) - expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(new_email) - expect(json_response[:data][:userResendConfirmation]).to include( - message: 'You will receive an email with instructions for how to confirm your email address in a few minutes.' - ) - end - end - - context "when the email isn't in the system" do - let(:email) { 'nothere@gmail.com' } - - it 'does *NOT* send an email and raises an error' do - expect { post_request }.to not_change(ActionMailer::Base.deliveries, :count) - expect(json_response[:data][:userResendConfirmation]).to be_nil - expect(json_response[:errors]).to contain_exactly( - hash_including( - message: "Unable to find user with email '#{email}'.", - extensions: { code: 'USER_ERROR' } - ) - ) - end - end -end diff --git a/spec/requests/mutations/resend_confirmation_with_token_spec.rb b/spec/requests/mutations/resend_confirmation_with_token_spec.rb index dc247f77..ac2ad2b6 100644 --- a/spec/requests/mutations/resend_confirmation_with_token_spec.rb +++ b/spec/requests/mutations/resend_confirmation_with_token_spec.rb @@ -105,9 +105,8 @@ before do user.update_with_email( - email: new_email, - schema_url: 'http://localhost/test', - confirmation_success_url: 'https://google.com' + email: new_email, + confirmation_url: 'https://google.com' ) end diff --git a/spec/requests/mutations/send_password_reset_spec.rb b/spec/requests/mutations/send_password_reset_spec.rb deleted file mode 100644 index 4a42d40f..00000000 --- a/spec/requests/mutations/send_password_reset_spec.rb +++ /dev/null @@ -1,103 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe 'Send Password Reset Requests' do - include_context 'with graphql query request' - - let!(:user) { create(:user, :confirmed, email: 'jwinnfield@wallaceinc.com') } - let(:email) { user.email } - let(:redirect_url) { 'https://google.com' } - let(:query) do - <<-GRAPHQL - mutation { - userSendPasswordReset( - email: "#{email}", - redirectUrl: "#{redirect_url}" - ) { - message - } - } - GRAPHQL - end - - context 'when redirect_url is not whitelisted' do - let(:redirect_url) { 'https://not-safe.com' } - - it 'returns a not whitelisted redirect url error' do - expect { post_request }.to not_change(ActionMailer::Base.deliveries, :count) - - expect(json_response[:errors]).to containing_exactly( - hash_including( - message: "Redirect to '#{redirect_url}' not allowed.", - extensions: { code: 'USER_ERROR' } - ) - ) - end - end - - context 'when params are correct' do - context 'when using the gem schema' do - it 'sends password reset email' do - expect { post_request }.to change(ActionMailer::Base.deliveries, :count).by(1) - - expect(json_response[:data][:userSendPasswordReset]).to include( - message: 'You will receive an email with instructions on how to reset your password in a few minutes.' - ) - - email = Nokogiri::HTML(ActionMailer::Base.deliveries.last.body.encoded) - link = email.css('a').first - expect(link['href']).to include('/api/v1/graphql_auth?') - - expect do - get link['href'] - user.reload - end.to change(user, :allow_password_change).from(false).to(true) - end - end - - context 'when using a custom schema' do - let(:custom_path) { '/api/v1/graphql' } - - it 'sends password reset email' do - expect { post_request(custom_path) }.to change(ActionMailer::Base.deliveries, :count).by(1) - - expect(json_response[:data][:userSendPasswordReset]).to include( - message: 'You will receive an email with instructions on how to reset your password in a few minutes.' - ) - - email = Nokogiri::HTML(ActionMailer::Base.deliveries.last.body.encoded) - link = email.css('a').first - expect(link['href']).to include("#{custom_path}?") - - expect do - get link['href'] - user.reload - end.to change(user, :allow_password_change).from(false).to(true) - end - end - end - - context 'when email address uses different casing' do - let(:email) { 'jWinnfield@wallaceinc.com' } - - it 'honors devise configuration for case insensitive fields' do - expect { post_request }.to change(ActionMailer::Base.deliveries, :count).by(1) - expect(json_response[:data][:userSendPasswordReset]).to include( - message: 'You will receive an email with instructions on how to reset your password in a few minutes.' - ) - end - end - - context 'when user email is not found' do - let(:email) { 'nothere@gmail.com' } - - before { post_request } - - it 'returns an error' do - expect(json_response[:errors]).to contain_exactly( - hash_including(message: 'User was not found or was not logged in.', extensions: { code: 'USER_ERROR' }) - ) - end - end -end diff --git a/spec/requests/mutations/sign_up_spec.rb b/spec/requests/mutations/sign_up_spec.rb deleted file mode 100644 index 7292d4b1..00000000 --- a/spec/requests/mutations/sign_up_spec.rb +++ /dev/null @@ -1,170 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe 'Sign Up process' do - include_context 'with graphql query request' - - let(:name) { Faker::Name.name } - let(:password) { Faker::Internet.password } - let(:email) { Faker::Internet.email } - let(:redirect) { 'https://google.com' } - - context 'when using the user model' do - let(:query) do - <<-GRAPHQL - mutation { - userSignUp( - email: "#{email}" - name: "#{name}" - password: "#{password}" - passwordConfirmation: "#{password}" - confirmSuccessUrl: "#{redirect}" - ) { - credentials { accessToken } - user { - email - name - } - } - } - GRAPHQL - end - - context 'when redirect_url is not whitelisted' do - let(:redirect) { 'https://not-safe.com' } - - it 'returns a not whitelisted redirect url error' do - expect { post_request }.to( - not_change(User, :count) - .and(not_change(ActionMailer::Base.deliveries, :count)) - ) - - expect(json_response[:errors]).to containing_exactly( - hash_including( - message: "Redirect to '#{redirect}' not allowed.", - extensions: { code: 'USER_ERROR' } - ) - ) - end - end - - context 'when params are correct' do - it 'creates a new resource that requires confirmation' do - expect { post_request }.to( - change(User, :count).by(1) - .and(change(ActionMailer::Base.deliveries, :count).by(1)) - ) - - user = User.last - - expect(user).not_to be_active_for_authentication - expect(user.confirmed_at).to be_nil - expect(user).to be_valid_password(password) - expect(json_response[:data][:userSignUp]).to include( - credentials: nil, - user: { - email: email, - name: name - } - ) - - email = Nokogiri::HTML(ActionMailer::Base.deliveries.last.body.encoded) - link = email.css('a').first - - expect do - get link['href'] - user.reload - end.to change { user.active_for_authentication? }.to(true) - end - - context 'when email address uses different casing' do - let(:email) { 'miaWallace@wallaceinc.com' } - - it 'honors devise configuration for case insensitive fields' do - expect { post_request }.to change(ActionMailer::Base.deliveries, :count).by(1) - expect(User.last.email).to eq('miawallace@wallaceinc.com') - expect(json_response[:data][:userSignUp]).to include(user: { email: 'miawallace@wallaceinc.com', name: name }) - end - end - end - - context 'when required params are missing' do - let(:email) { '' } - - it 'does *NOT* create resource a resource nor send an email' do - expect { post_request }.to( - not_change(User, :count) - .and(not_change(ActionMailer::Base.deliveries, :count)) - ) - - expect(json_response[:data][:userSignUp]).to be_nil - expect(json_response[:errors]).to containing_exactly( - hash_including( - message: "User couldn't be registered", - extensions: { code: 'USER_ERROR', detailed_errors: ["Email can't be blank"] } - ) - ) - end - end - end - - context 'when using the admin model' do - let(:query) do - <<-GRAPHQL - mutation { - adminSignUp( - email: "#{email}" - password: "#{password}" - passwordConfirmation: "#{password}" - confirmSuccessUrl: "#{redirect}" - ) { - authenticatable { - email - } - } - } - GRAPHQL - end - - before { post_request } - - it 'skips the sign up mutation' do - expect(json_response[:errors]).to contain_exactly( - hash_including(message: "Field 'adminSignUp' doesn't exist on type 'Mutation'") - ) - end - end - - context 'when using the guest model' do - let(:query) do - <<-GRAPHQL - mutation { - guestSignUp( - email: "#{email}" - password: "#{password}" - passwordConfirmation: "#{password}" - confirmSuccessUrl: "#{redirect}" - ) { - credentials { accessToken client uid } - authenticatable { - email - } - } - } - GRAPHQL - end - - it 'returns credentials as no confirmation is required' do - expect { post_request }.to change(Guest, :count).from(0).to(1) - - expect(json_response[:data][:guestSignUp]).to include( - authenticatable: { email: email }, - credentials: hash_including( - uid: email, - client: Guest.last.tokens.keys.first - ) - ) - end - end -end diff --git a/spec/requests/mutations/update_password_spec.rb b/spec/requests/mutations/update_password_spec.rb deleted file mode 100644 index bd8120bd..00000000 --- a/spec/requests/mutations/update_password_spec.rb +++ /dev/null @@ -1,116 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe 'Update Password Requests' do - shared_examples 'successful password update' do - it 'updates the password' do - expect do - post_request - user.reload - end.to change(user, :encrypted_password) - - expect(response).to include_auth_headers - expect(json_response[:data][:userUpdatePassword]).to match( - authenticatable: { email: user.email } - ) - expect(json_response[:errors]).to be_nil - expect(user).to be_valid_password(password) - end - end - - include_context 'with graphql query request' - - let(:password) { 'safePassw0rd!' } - let(:password_confirmation) { 'safePassw0rd!' } - let(:original_password) { 'current_password' } - let(:current_password) { original_password } - let(:allow_password_change) { false } - let(:user) { create(:user, :confirmed, password: original_password, allow_password_change: allow_password_change) } - let(:query) do - <<-GRAPHQL - mutation { - userUpdatePassword( - password: "#{password}", - passwordConfirmation: "#{password_confirmation}", - currentPassword: "#{current_password}" - ) { - authenticatable { email } - } - } - GRAPHQL - end - - context 'when user is logged in' do - let(:headers) { user.create_new_auth_token } - - context 'when currentPassword is not provided' do - let(:current_password) { nil } - - context 'when allow_password_change is true' do - let(:allow_password_change) { true } - - it_behaves_like 'successful password update' - - it 'sets allow_password_change to false' do - expect do - post_request - user.reload - end.to change(user, :allow_password_change).from(true).to(false) - end - end - - context 'when allow_password_change is false' do - it 'does not update the password' do - expect do - post_request - user.reload - end.not_to change(user, :encrypted_password) - - expect(response).to include_auth_headers - expect(json_response[:data][:userUpdatePassword]).to be_nil - expect(json_response[:errors]).to contain_exactly( - hash_including( - message: 'Unable to update user password', - extensions: { code: 'USER_ERROR', detailed_errors: ["Current password can't be blank"] } - ) - ) - end - end - end - - context 'when currentPassword is provided' do - context 'when allow_password_change is true' do - let(:allow_password_change) { true } - - it_behaves_like 'successful password update' - - it 'sets allow_password_change to false' do - expect do - post_request - user.reload - end.to change(user, :allow_password_change).from(true).to(false) - end - end - - context 'when allow_password_change is false' do - it_behaves_like 'successful password update' - end - end - end - - context 'when user is not logged in' do - it 'does not update the password' do - expect do - post_request - user.reload - end.not_to change(user, :encrypted_password) - - expect(response).not_to include_auth_headers - expect(json_response[:data][:userUpdatePassword]).to be_nil - expect(json_response[:errors]).to contain_exactly( - hash_including(message: 'User is not logged in.', extensions: { code: 'USER_ERROR' }) - ) - end - end -end diff --git a/spec/requests/queries/check_password_token_spec.rb b/spec/requests/queries/check_password_token_spec.rb deleted file mode 100644 index 9cfa3b17..00000000 --- a/spec/requests/queries/check_password_token_spec.rb +++ /dev/null @@ -1,149 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe 'Check Password Token Requests' do - include_context 'with graphql query request' - - let(:user) { create(:user, :confirmed) } - let(:redirect_url) { 'https://google.com' } - - context 'when using the user model' do - let(:query) do - <<-GRAPHQL - query { - userCheckPasswordToken( - resetPasswordToken: "#{token}", - redirectUrl: "#{redirect_url}" - ) { - email - } - } - GRAPHQL - end - - context 'when reset password token is valid' do - let(:token) { user.send(:set_reset_password_token) } - - context 'when redirect_url is not provided' do - let(:redirect_url) { nil } - - it 'returns authenticatable and credentials in the headers' do - get_request - - expect(response).to include_auth_headers - expect(json_response[:data][:userCheckPasswordToken]).to match( - email: user.email - ) - end - end - - context 'when redirect url is provided' do - it 'redirects to redirect url' do - expect do - get_request - - user.reload - end.to change { user.tokens.keys.count }.from(0).to(1).and( - change(user, :allow_password_change).from(false).to(true) - ) - - expect(response).to redirect_to %r{\Ahttps://google.com} - expect(response.body).to include("client=#{user.reload.tokens.keys.first}") - expect(response.body).to include('access-token=') - expect(response.body).to include('uid=') - expect(response.body).to include('expiry=') - end - - context 'when redirect_url is not whitelisted' do - let(:redirect_url) { 'https://not-safe.com' } - - before { post_request } - - it 'returns a not whitelisted redirect url error' do - expect(json_response[:errors]).to containing_exactly( - hash_including( - message: "Redirect to '#{redirect_url}' not allowed.", - extensions: { code: 'USER_ERROR' } - ) - ) - end - end - end - - context 'when token has expired' do - it 'returns an expired token error' do - travel_to 10.hours.ago do - token - end - - get_request - - expect(json_response[:errors]).to contain_exactly( - hash_including(message: 'Reset password token is no longer valid.', extensions: { code: 'USER_ERROR' }) - ) - end - end - end - - context 'when reset password token is not found' do - let(:token) { user.send(:set_reset_password_token) + 'invalid' } - - it 'returns an error message' do - get_request - - expect(json_response[:errors]).to contain_exactly( - hash_including(message: 'No user found for the specified reset token.', extensions: { code: 'USER_ERROR' }) - ) - end - end - end - - context 'when using the admin model' do - let(:token) { 'not_important' } - let(:query) do - <<-GRAPHQL - query { - adminCheckPasswordToken( - resetPasswordToken: "#{token}", - redirectUrl: "#{redirect_url}" - ) { - email - } - } - GRAPHQL - end - - before { post_request } - - it 'skips the sign up mutation' do - expect(json_response[:errors]).to contain_exactly( - hash_including(message: "Field 'adminCheckPasswordToken' doesn't exist on type 'Query'") - ) - end - end - - context 'when using the guest model' do - let(:token) { 'not_important' } - let(:query) do - <<-GRAPHQL - query { - guestCheckPasswordToken( - resetPasswordToken: "#{token}", - redirectUrl: "#{redirect_url}" - ) { - email - } - } - GRAPHQL - end - - before { post_request } - - it 'skips the sign up mutation' do - expect(json_response[:errors]).to contain_exactly( - hash_including(message: "Field 'guestCheckPasswordToken' doesn't exist on type 'Query'") - ) - end - end -end diff --git a/spec/requests/queries/confirm_account_spec.rb b/spec/requests/queries/confirm_account_spec.rb deleted file mode 100644 index 82b23358..00000000 --- a/spec/requests/queries/confirm_account_spec.rb +++ /dev/null @@ -1,137 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe 'Account confirmation' do - include_context 'with graphql query request' - - context 'when using the user model' do - let(:user) { create(:user, confirmed_at: nil) } - let(:redirect) { 'https://google.com' } - let(:query) do - <<-GRAPHQL - { - userConfirmAccount( - confirmationToken: "#{token}" - redirectUrl: "#{redirect}" - ) { - email - name - } - } - GRAPHQL - end - - context 'when confirmation token is correct' do - let(:token) { user.confirmation_token } - - before do - user.send_confirmation_instructions( - template_path: ['graphql_devise/mailer'], - controller: 'graphql_devise/graphql', - schema_url: 'http://not-using-this-value.com/gql' - ) - end - - it 'confirms the resource and redirects to the sent url' do - expect do - get_request - user.reload - end.to(change(user, :confirmed_at).from(nil)) - - expect(response).to redirect_to("#{redirect}?account_confirmation_success=true") - expect(user).to be_active_for_authentication - end - - context 'when redirect_url is not whitelisted' do - let(:redirect) { 'https://not-safe.com' } - - it 'returns a not whitelisted redirect url error' do - expect { post_request }.to not_change(ActionMailer::Base.deliveries, :count) - - expect(json_response[:errors]).to containing_exactly( - hash_including( - message: "Redirect to '#{redirect}' not allowed.", - extensions: { code: 'USER_ERROR' } - ) - ) - end - end - - context 'when unconfirmed_email is present' do - let(:user) { create(:user, :confirmed, unconfirmed_email: 'vvega@wallaceinc.com') } - - it 'confirms the unconfirmed email and redirects' do - expect do - get_request - user.reload - end.to change(user, :email).from(user.email).to('vvega@wallaceinc.com').and( - change(user, :unconfirmed_email).from('vvega@wallaceinc.com').to(nil) - ) - - expect(response).to redirect_to("#{redirect}?account_confirmation_success=true") - end - end - end - - context 'when reset password token is not found' do - let(:token) { "#{user.confirmation_token}-invalid" } - - it 'does *NOT* confirm the user nor does the redirection' do - expect do - get_request - user.reload - end.not_to(change(user, :confirmed_at).from(nil)) - - expect(response).not_to be_redirect - expect(json_response[:errors]).to contain_exactly( - hash_including( - message: 'Invalid confirmation token. Please try again', - extensions: { code: 'USER_ERROR' } - ) - ) - end - end - end - - context 'when using the admin model' do - let(:admin) { create(:admin, confirmed_at: nil) } - let(:redirect) { 'https://google.com' } - let(:query) do - <<-GRAPHQL - { - adminConfirmAccount( - confirmationToken: "#{token}" - redirectUrl: "#{redirect}" - ) { - email - } - } - GRAPHQL - end - - context 'when confirmation token is correct' do - let(:token) { admin.confirmation_token } - - before do - admin.send_confirmation_instructions( - template_path: ['graphql_devise/mailer'], - controller: 'graphql_devise/graphql', - schema_url: 'http://not-using-this-value.com/gql' - ) - end - - it 'confirms the resource, persists credentials on the DB and redirects to the sent url' do - expect do - get_request - admin.reload - end.to change(admin, :confirmed_at).from(nil).and( - change { admin.tokens.keys.count }.from(0).to(1) - ) - - expect(response).to redirect_to(/\A#{redirect}.+access\-token=/) - expect(admin).to be_active_for_authentication - end - end - end -end diff --git a/spec/requests/user_controller_spec.rb b/spec/requests/user_controller_spec.rb index e005fd79..4578a421 100644 --- a/spec/requests/user_controller_spec.rb +++ b/spec/requests/user_controller_spec.rb @@ -245,14 +245,7 @@ email = Nokogiri::HTML(ActionMailer::Base.deliveries.last.body.encoded) link = email.css('a').first - expect(link['href']).to include('/api/v1/graphql') - - expect do - get link['href'] - user.reload - end.to change(user, :email).from(original_email).to('updated@gmail.com').and( - change(user, :uid).from(original_email).to('updated@gmail.com') - ) + expect(link['href']).to include('https://google.com') end end diff --git a/spec/services/resource_loader_spec.rb b/spec/services/resource_loader_spec.rb index b2798526..19079512 100644 --- a/spec/services/resource_loader_spec.rb +++ b/spec/services/resource_loader_spec.rb @@ -11,7 +11,7 @@ let(:routing) { false } let(:mounted) { false } let(:resource) { 'User' } - let(:options) { { only: [:login, :confirm_account] } } + let(:options) { { only: [:login], additional_queries: { public_user: Class.new(GraphQL::Schema::Resolver) } } } before do allow(GraphqlDevise).to receive(:add_mapping).with(:user, resource) @@ -20,7 +20,7 @@ end it 'loads operations into the provided types' do - expect(query).to receive(:field).with(:user_confirm_account, resolver: instance_of(Class), authenticate: false) + expect(query).to receive(:field).with(:public_user, resolver: instance_of(Class), authenticate: false) expect(mutation).to receive(:field).with(:user_login, mutation: instance_of(Class), authenticate: false) expect(GraphqlDevise).to receive(:add_mapping).with(:user, resource) expect(GraphqlDevise).not_to receive(:mount_resource)