diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml index e0de8824625e65f4473e61bffe24ef5bacf205d3..3aeb89979bb66659ffcc37153ee57a58eb6c22a8 100644 --- a/app/views/devise/sessions/_new_base.html.haml +++ b/app/views/devise/sessions/_new_base.html.haml @@ -1,4 +1,4 @@ -= gitlab_ui_form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user gl-show-field-errors js-sign-in-form', aria: { live: 'assertive' }, data: { testid: 'sign-in-form' }}) do |f| += gitlab_ui_form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user gl-show-field-errors js-arkose-labs-form', aria: { live: 'assertive' }, data: { testid: 'sign-in-form' }}) do |f| .form-group.gl-px-5.gl-pt-5 = render_if_exists 'devise/sessions/new_base_user_login_label', form: f = f.text_field :login, value: @invite_email, class: 'form-control gl-form-input top js-username-field', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off', required: true, title: _('This field is required.'), data: { qa_selector: 'login_field', testid: 'username-field' } diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 991af1eea0ccb454b69f0e5b8d106507db78b1cf..b9c9c99bf1a238dd5ee230ad41357ab89c68ee6a 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -6,7 +6,7 @@ - if show_omniauth_providers && omniauth_providers_placement == :top = render 'devise/shared/signup_omniauth_providers_top' - = form_for(resource, as: "new_#{resource_name}", url: url, html: { class: 'new_user gl-show-field-errors', 'aria-live' => 'assertive' }) do |f| + = form_for(resource, as: "new_#{resource_name}", url: url, html: { class: 'new_user gl-show-field-errors js-arkose-labs-form', 'aria-live' => 'assertive' }, data: { testid: 'signup-form' }) do |f| .devise-errors = render 'devise/shared/error_messages', resource: resource - if Gitlab::CurrentSettings.invisible_captcha_enabled @@ -66,8 +66,12 @@ = render_if_exists 'shared/password_requirements_list' = render_if_exists 'devise/shared/phone_verification', form: f %div - - if show_recaptcha_sign_up? + + - if Feature.enabled?(:arkose_labs_signup_challenge) + = render_if_exists 'devise/registrations/arkose_labs' + - elsif show_recaptcha_sign_up? = recaptcha_tags nonce: content_security_policy_nonce + .submit-container.gl-mt-5 = f.submit button_text, class: 'btn gl-button btn-confirm gl-display-block gl-w-full', data: { qa_selector: 'new_user_register_button' } - if Gitlab::CurrentSettings.sign_in_text.present? && Feature.enabled?(:restyle_login_page, @project) diff --git a/config/feature_flags/development/arkose_labs_signup_challenge.yml b/config/feature_flags/development/arkose_labs_signup_challenge.yml new file mode 100644 index 0000000000000000000000000000000000000000..8b40ce5d0294c083939b01a133fc56553dc47e23 --- /dev/null +++ b/config/feature_flags/development/arkose_labs_signup_challenge.yml @@ -0,0 +1,8 @@ +--- +name: arkose_labs_signup_challenge +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95560 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370932 +milestone: '15.4' +type: development +group: group::anti-abuse +default_enabled: false diff --git a/ee/app/assets/javascripts/arkose_labs/components/sign_up_arkose_app.vue b/ee/app/assets/javascripts/arkose_labs/components/sign_up_arkose_app.vue new file mode 100644 index 0000000000000000000000000000000000000000..658067856b7245b45c5c5bb5be12ed16ece19906 --- /dev/null +++ b/ee/app/assets/javascripts/arkose_labs/components/sign_up_arkose_app.vue @@ -0,0 +1,100 @@ + + + diff --git a/ee/app/assets/javascripts/arkose_labs/constants.js b/ee/app/assets/javascripts/arkose_labs/constants.js new file mode 100644 index 0000000000000000000000000000000000000000..91c5c6614ead11271f4ed24ff09ce9905a42b317 --- /dev/null +++ b/ee/app/assets/javascripts/arkose_labs/constants.js @@ -0,0 +1,7 @@ +import { __ } from '~/locale'; + +export const VERIFICATION_REQUIRED_MESSAGE = __('Complete verification to sign up.'); +export const VERIFICATION_LOADING_MESSAGE = __('Please wait while we prepare for verification.'); + +export const CHALLENGE_CONTAINER_CLASS = 'js-arkose-labs-container-'; +export const VERIFICATION_TOKEN_INPUT_NAME = 'arkose_labs_token'; diff --git a/ee/app/assets/javascripts/arkose_labs/index.js b/ee/app/assets/javascripts/arkose_labs/index.js index 43d3ab95b11f2f3cba984974c8e5c4ef00522fff..1ecf784bb926576c2c69f673f545199ec69d18d5 100644 --- a/ee/app/assets/javascripts/arkose_labs/index.js +++ b/ee/app/assets/javascripts/arkose_labs/index.js @@ -1,13 +1,14 @@ import Vue from 'vue'; import SignInArkoseApp from './components/sign_in_arkose_app.vue'; +import SignUpArkoseApp from './components/sign_up_arkose_app.vue'; -const FORM_SELECTOR = '.js-sign-in-form'; +const FORM_SELECTOR = '.js-arkose-labs-form'; const USERNAME_SELECTOR = `${FORM_SELECTOR} .js-username-field`; const SUBMIT_SELECTOR = `${FORM_SELECTOR} .js-sign-in-button`; export const setupArkoseLabs = () => { const signInForm = document.querySelector(FORM_SELECTOR); - const el = signInForm?.querySelector('.js-arkose-labs-challenge'); + const el = signInForm?.querySelector('#js-arkose-labs-challenge'); if (!el) { return null; @@ -30,3 +31,26 @@ export const setupArkoseLabs = () => { }, }); }; + +export const setupArkoseLabsForSignup = () => { + const el = document.querySelector('#js-arkose-labs-challenge'); + + if (!el) { + return null; + } + + const { apiKey, domain } = el.dataset; + + return new Vue({ + el, + render(h) { + return h(SignUpArkoseApp, { + props: { + formSelector: FORM_SELECTOR, + publicKey: apiKey, + domain, + }, + }); + }, + }); +}; diff --git a/ee/app/assets/javascripts/pages/registrations/new/index.js b/ee/app/assets/javascripts/pages/registrations/new/index.js index 3bb5c2c782958c3a3d0c451d6835785056210d66..d4aa724a36f967956f527af20c23713f86c77bd4 100644 --- a/ee/app/assets/javascripts/pages/registrations/new/index.js +++ b/ee/app/assets/javascripts/pages/registrations/new/index.js @@ -1,4 +1,9 @@ import '~/pages/registrations/new/index'; import PasswordValidator from 'ee/password/password_validator'; +import { setupArkoseLabsForSignup } from 'ee/arkose_labs'; new PasswordValidator(); // eslint-disable-line no-new + +if (gon.features.arkoseLabsSignupChallenge) { + setupArkoseLabsForSignup(); +} diff --git a/ee/app/controllers/concerns/arkose_labs_csp.rb b/ee/app/controllers/concerns/arkose_labs_csp.rb index 8d770378cd01d26243450c1ce69f4cbc1949a89d..c78ccbf604d2f4d8400712b678cb61b37424372c 100644 --- a/ee/app/controllers/concerns/arkose_labs_csp.rb +++ b/ee/app/controllers/concerns/arkose_labs_csp.rb @@ -5,7 +5,10 @@ module ArkoseLabsCSP included do content_security_policy do |policy| - next unless Feature.enabled?(:arkose_labs_login_challenge) + allow_for_login = self == SessionsController && Feature.enabled?(:arkose_labs_login_challenge) + allow_for_signup = self == RegistrationsController && Feature.enabled?(:arkose_labs_signup_challenge) + + next unless allow_for_login || allow_for_signup default_script_src = policy.directives['script-src'] || policy.directives['default-src'] script_src_values = Array.wrap(default_script_src) | ["https://*.arkoselabs.com"] diff --git a/ee/app/controllers/ee/registrations_controller.rb b/ee/app/controllers/ee/registrations_controller.rb index b41e603727799f7a5023bfffff1a6701b1845fa3..56ec85196235a3459a71c24e55e5568b1d081fc6 100644 --- a/ee/app/controllers/ee/registrations_controller.rb +++ b/ee/app/controllers/ee/registrations_controller.rb @@ -6,9 +6,28 @@ module RegistrationsController extend ::Gitlab::Utils::Override prepended do + include ArkoseLabsCSP + + skip_before_action :check_captcha, if: -> { ::Feature.enabled?(:arkose_labs_signup_challenge) } + before_action only: [:new, :create] do + push_frontend_feature_flag(:arkose_labs_signup_challenge) + end before_action :ensure_can_remove_self, only: [:destroy] end + override :create + def create + ensure_correct_params! + + unless verify_arkose_labs_token + flash[:alert] = _('Complete verification to sign up.') + render action: 'new' + return + end + + super + end + private override :after_request_hook @@ -41,5 +60,13 @@ def log_audit_event(user) custom_message: _('Instance access request') ).for_user.security_event end + + def verify_arkose_labs_token + return true unless ::Feature.enabled?(:arkose_labs_signup_challenge) + + # TODO: verify the token with Arkose Labs Verify API + # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96243 + params[:arkose_labs_token].present? + end end end diff --git a/ee/app/helpers/ee/registrations_helper.rb b/ee/app/helpers/ee/registrations_helper.rb index a8ea7c2c18e6c105e7ffad0655eece7dcdd22806..3db528ef113e44f656fa817e78f5ac1ac3cb2867 100644 --- a/ee/app/helpers/ee/registrations_helper.rb +++ b/ee/app/helpers/ee/registrations_helper.rb @@ -46,6 +46,15 @@ def credit_card_verification_data } end + def arkose_labs_data + return {} unless ::Feature.enabled?(:arkose_labs_signup_challenge) + + { + api_key: Arkose::Settings.arkose_public_api_key, + domain: Arkose::Settings.arkose_labs_domain + } + end + private def redirect_path diff --git a/ee/app/views/devise/registrations/_arkose_labs.html.haml b/ee/app/views/devise/registrations/_arkose_labs.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..b35e2941da9ca98896cbaf6ebfaf3720725a7b74 --- /dev/null +++ b/ee/app/views/devise/registrations/_arkose_labs.html.haml @@ -0,0 +1 @@ +#js-arkose-labs-challenge{ data: arkose_labs_data } diff --git a/ee/app/views/devise/sessions/_arkose_labs.html.haml b/ee/app/views/devise/sessions/_arkose_labs.html.haml index 1390680b461134febd176e7aa5799dc204d35080..6f8d9d0e5e1c65088a51f60d70ff9de9ab4a9b9e 100644 --- a/ee/app/views/devise/sessions/_arkose_labs.html.haml +++ b/ee/app/views/devise/sessions/_arkose_labs.html.haml @@ -1 +1 @@ -.js-arkose-labs-challenge{ data: { api_key: @arkose_labs_public_key, domain: @arkose_labs_domain } } +#js-arkose-labs-challenge{ data: { api_key: @arkose_labs_public_key, domain: @arkose_labs_domain } } diff --git a/ee/spec/controllers/ee/registrations_controller_spec.rb b/ee/spec/controllers/ee/registrations_controller_spec.rb index 215ecf7f4bfd4d119adb066dc2a765815ab82663..9a88148e93421ba28993a590e4e25088c167d12e 100644 --- a/ee/spec/controllers/ee/registrations_controller_spec.rb +++ b/ee/spec/controllers/ee/registrations_controller_spec.rb @@ -8,6 +8,10 @@ let_it_be(:new_user_email) { 'new@user.com' } let_it_be(:user_params) { { user: base_user_params.merge(email: new_user_email) } } + before do + stub_feature_flags(arkose_labs_signup_challenge: false) + end + subject { post :create, params: user_params } shared_examples 'blocked user by default' do diff --git a/ee/spec/controllers/trial_registrations_controller_spec.rb b/ee/spec/controllers/trial_registrations_controller_spec.rb index e55d1c9df0ec7a38e94b189033dcaabe64d80a1c..744805b7aa4b109ae1e9f3440f75dbc3b3583120 100644 --- a/ee/spec/controllers/trial_registrations_controller_spec.rb +++ b/ee/spec/controllers/trial_registrations_controller_spec.rb @@ -7,6 +7,7 @@ before do allow(Gitlab).to receive(:com?).and_return(com) + stub_feature_flags(arkose_labs_signup_challenge: false) end shared_examples 'a dot-com only feature' do diff --git a/ee/spec/features/invites_spec.rb b/ee/spec/features/invites_spec.rb index 1fd7677bc76bea7c784908ff9cab2ec859f790ba..81868b3cde29354dd216359140f767dc4f8292b3 100644 --- a/ee/spec/features/invites_spec.rb +++ b/ee/spec/features/invites_spec.rb @@ -10,6 +10,7 @@ let(:com) { true } before do + stub_feature_flags(arkose_labs_signup_challenge: false) stub_application_setting(require_admin_approval_after_user_signup: false) allow(::Gitlab).to receive(:com?).and_return(com) diff --git a/ee/spec/features/registrations/saas_user_registration_spec.rb b/ee/spec/features/registrations/saas_user_registration_spec.rb index ff9029cd6d78ea89cc913423978529c7d3b9d824..543cc081640e88f3556921ead97a9905ecb5a3b2 100644 --- a/ee/spec/features/registrations/saas_user_registration_spec.rb +++ b/ee/spec/features/registrations/saas_user_registration_spec.rb @@ -21,7 +21,9 @@ # This is a feature flag to update the single-sign on registration flow # to match the standard registration flow - update_oauth_registration_flow: true + update_oauth_registration_flow: true, + + arkose_labs_signup_challenge: false ) stub_application_setting( diff --git a/ee/spec/features/signup_spec.rb b/ee/spec/features/signup_spec.rb index 329a1004d63cc11a6f01a442a649d3aebbedd5a2..34a8c80fa47c12a60b9a9ea3267d7e6fb4c55cc7 100644 --- a/ee/spec/features/signup_spec.rb +++ b/ee/spec/features/signup_spec.rb @@ -7,6 +7,7 @@ before do stub_application_setting(require_admin_approval_after_user_signup: false) + stub_feature_flags(arkose_labs_signup_challenge: false) end def fill_in_signup_form diff --git a/ee/spec/features/trial_registrations/signup_spec.rb b/ee/spec/features/trial_registrations/signup_spec.rb index f63eaf06ddda95eeac4464bc4dde3085f01120ba..b54e99649f1237f39702892a83b1bedf35ac5175 100644 --- a/ee/spec/features/trial_registrations/signup_spec.rb +++ b/ee/spec/features/trial_registrations/signup_spec.rb @@ -7,6 +7,7 @@ before do stub_application_setting(require_admin_approval_after_user_signup: false) + stub_feature_flags(arkose_labs_signup_challenge: false) end describe 'on GitLab.com', :saas do diff --git a/ee/spec/features/users/arkose_labs_csp_spec.rb b/ee/spec/features/users/arkose_labs_csp_spec.rb index 76e87d15cbd72525cc52a352174e0b38aa28ffb6..be00ae9ff600fbc45bee2da0b3ceb8872ce238f9 100644 --- a/ee/spec/features/users/arkose_labs_csp_spec.rb +++ b/ee/spec/features/users/arkose_labs_csp_spec.rb @@ -3,15 +3,51 @@ require 'spec_helper' RSpec.describe 'ArkoseLabs content security policy' do - let(:user) { create(:user) } + shared_examples 'configures Content Security Policy headers correctly' do + context 'when feature flag is enabled' do + let(:feature_flag_state) { true } - before do - stub_feature_flags(arkose_labs_login_challenge: true) + it 'adds ArkoseLabs URL to Content Security Policy headers' do + visit page_path + + expect(response_headers['Content-Security-Policy']).to include('https://*.arkoselabs.com') + end + end + + context 'when feature flag is disabled' do + let(:feature_flag_state) { false } + + it 'does not add ArkoseLabs URL to Content Security Policy headers' do + visit page_path + + expect(response_headers['Content-Security-Policy']).not_to include('https://*.arkoselabs.com') + end + end end - it 'has proper Content Security Policy headers' do - visit root_path + context 'when in login page' do + let(:page_path) { root_path } + + before do + stub_feature_flags( + arkose_labs_signup_challenge: false, + arkose_labs_login_challenge: feature_flag_state + ) + end + + it_behaves_like 'configures Content Security Policy headers correctly' + end + + context 'when in registration page' do + let(:page_path) { new_user_registration_path } + + before do + stub_feature_flags( + arkose_labs_login_challenge: false, + arkose_labs_signup_challenge: feature_flag_state + ) + end - expect(response_headers['Content-Security-Policy']).to include('https://*.arkoselabs.com') + it_behaves_like 'configures Content Security Policy headers correctly' end end diff --git a/ee/spec/frontend/arkose_labs/components/sign_up_arkose_app_spec.js b/ee/spec/frontend/arkose_labs/components/sign_up_arkose_app_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..cd9b1a747b1219ad00d262d51489bba7da8b549f --- /dev/null +++ b/ee/spec/frontend/arkose_labs/components/sign_up_arkose_app_spec.js @@ -0,0 +1,149 @@ +import { nextTick } from 'vue'; +import { createAlert } from '~/flash'; +import DomElementListener from '~/vue_shared/components/dom_element_listener.vue'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import SignUpArkoseApp from 'ee/arkose_labs/components/sign_up_arkose_app.vue'; +import { initArkoseLabsScript } from 'ee/arkose_labs/init_arkose_labs_script'; +import { + VERIFICATION_LOADING_MESSAGE, + VERIFICATION_REQUIRED_MESSAGE, + VERIFICATION_TOKEN_INPUT_NAME, +} from 'ee/arkose_labs/constants'; + +jest.mock('~/flash'); +jest.mock('ee/arkose_labs/init_arkose_labs_script'); +let onShown; +let onCompleted; +initArkoseLabsScript.mockImplementation(() => ({ + setConfig: ({ onShown: shownHandler, onCompleted: completedHandler }) => { + onShown = shownHandler; + onCompleted = completedHandler; + }, +})); + +const MOCK_ARKOSE_RESPONSE = { token: 'verification-token' }; +const MOCK_PUBLIC_KEY = 'arkose-labs-public-api-key'; +const MOCK_DOMAIN = 'client-api.arkoselabs.com'; + +describe('SignUpArkoseApp', () => { + let wrapper; + + const findChallengeContainer = () => wrapper.findByTestId('arkose-labs-challenge'); + const findArkoseLabsVerificationTokenInput = () => + wrapper.find(`input[name="${VERIFICATION_TOKEN_INPUT_NAME}"]`); + + const submitForm = async (event) => { + wrapper.findComponent(DomElementListener).vm.$emit('submit', event); + await nextTick(); + }; + + const createComponent = () => { + wrapper = mountExtended(SignUpArkoseApp, { + propsData: { + publicKey: MOCK_PUBLIC_KEY, + domain: MOCK_DOMAIN, + formSelector: 'dummy', + }, + }); + }; + + afterEach(() => { + wrapper?.destroy(); + }); + + beforeEach(() => { + createComponent(); + }); + + it("includes Arkose Labs' script", () => { + expect(initArkoseLabsScript).toHaveBeenCalledWith({ + publicKey: MOCK_PUBLIC_KEY, + domain: MOCK_DOMAIN, + }); + }); + + it('creates a hidden input for the verification token', () => { + const input = findArkoseLabsVerificationTokenInput(); + + expect(input.exists()).toBe(true); + expect(input.element.value).toBe(''); + }); + + it('shows the challenge container when Arkose Labs calls `onShown`', async () => { + expect(findChallengeContainer().isVisible()).toBe(false); + + onShown(); + await nextTick(); + + expect(findChallengeContainer().isVisible()).toBe(true); + }); + + describe('when Arkose Labs calls `onCompleted`', () => { + beforeEach(() => { + onCompleted(MOCK_ARKOSE_RESPONSE); + }); + + it("sets the verification token input's value", () => { + expect(findArkoseLabsVerificationTokenInput().element.value).toBe(MOCK_ARKOSE_RESPONSE.token); + }); + }); + + describe('when form is submitted', () => { + let mockSubmitEvent; + + beforeEach(() => { + mockSubmitEvent = { preventDefault: jest.fn(), stopPropagation: jest.fn() }; + }); + + describe('when challenge was not completed', () => { + beforeEach(async () => { + onShown(); + + await submitForm(mockSubmitEvent); + }); + + it('shows verification required error message', async () => { + expect(createAlert).toHaveBeenCalledWith({ + message: VERIFICATION_REQUIRED_MESSAGE, + }); + }); + + it('stops the submit event', async () => { + expect(mockSubmitEvent.preventDefault).toHaveBeenCalledTimes(1); + expect(mockSubmitEvent.stopPropagation).toHaveBeenCalledTimes(1); + }); + }); + + describe('when challenge was completed', () => { + beforeEach(async () => { + onShown(); + onCompleted(MOCK_ARKOSE_RESPONSE); + + submitForm(mockSubmitEvent); + + await nextTick(); + }); + + it('does not show verification required error message', async () => { + expect(createAlert).not.toHaveBeenCalled(); + }); + + it('does not stop the submit event', async () => { + expect(mockSubmitEvent.preventDefault).not.toHaveBeenCalled(); + expect(mockSubmitEvent.stopPropagation).not.toHaveBeenCalled(); + }); + }); + + describe('when challenge has not been shown yet (loading)', () => { + beforeEach(async () => { + await submitForm(mockSubmitEvent); + }); + + it('shows verification loading message', async () => { + expect(createAlert).toHaveBeenCalledWith({ + message: VERIFICATION_LOADING_MESSAGE, + }); + }); + }); + }); +}); diff --git a/ee/spec/helpers/ee/registrations_helper_spec.rb b/ee/spec/helpers/ee/registrations_helper_spec.rb index 335b3c7a8dff360f8ef5a2a92ef4d653f3e30525..98fb1d956fdb897fd9a7bd0b9b3de9316160d11a 100644 --- a/ee/spec/helpers/ee/registrations_helper_spec.rb +++ b/ee/spec/helpers/ee/registrations_helper_spec.rb @@ -107,4 +107,23 @@ ) end end + + describe '#arkose_labs_data' do + before do + allow(::Arkose::Settings).to receive(:arkose_public_api_key).and_return('api-key') + allow(::Arkose::Settings).to receive(:arkose_labs_domain).and_return('domain') + end + + subject(:data) { helper.arkose_labs_data } + + it { is_expected.to eq({ api_key: 'api-key', domain: 'domain' }) } + + context 'when :arkose_labs_signup_challenge is disabled' do + before do + stub_feature_flags(arkose_labs_signup_challenge: false) + end + + it { is_expected.to eq({}) } + end + end end diff --git a/ee/spec/requests/registrations_controller_spec.rb b/ee/spec/requests/registrations_controller_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..498ef566cb8f06f2016353cb281de69b03b2cd44 --- /dev/null +++ b/ee/spec/requests/registrations_controller_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RegistrationsController, type: :request do + describe 'POST #create' do + let_it_be(:base_user_params) { build_stubbed(:user).slice(:first_name, :last_name, :username, :email, :password) } + + let(:arkose_labs_params) { { arkose_labs_token: 'arkose-labs-token' } } + let(:user_params) { { user: base_user_params }.merge(arkose_labs_params) } + + subject(:request) { post user_registration_path, params: user_params } + + context 'when arkose_labs_token verification succeeds' do + it 'does not render new action', :aggregate_failures do + request + + expect(flash[:alert]).to be_nil + expect(response).not_to render_template(:new) + end + end + + context 'when arkose_labs_token verification fails' do + let(:arkose_labs_params) { {} } + + it 'renders new action with an alert flash', :aggregate_failures do + request + + expect(flash[:alert]).to include(_('Complete verification to sign up.')) + expect(response).to render_template(:new) + end + end + end +end diff --git a/ee/spec/requests/trial_registrations_controller_spec.rb b/ee/spec/requests/trial_registrations_controller_spec.rb index e054b0a1a75dd81fb3006bbb1242774531391acd..c20a4e4700598772c4d0ce67189648ea39e5fb94 100644 --- a/ee/spec/requests/trial_registrations_controller_spec.rb +++ b/ee/spec/requests/trial_registrations_controller_spec.rb @@ -7,6 +7,7 @@ before do allow(Gitlab).to receive(:com?).and_return(com) + stub_feature_flags(arkose_labs_signup_challenge: false) end describe 'POST new' do diff --git a/ee/spec/views/devise/registrations/new.html.haml_spec.rb b/ee/spec/views/devise/registrations/new.html.haml_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..2c881b1bbb5eeac63dccfb3c286d1ad1382230dc --- /dev/null +++ b/ee/spec/views/devise/registrations/new.html.haml_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'devise/registrations/new' do + let(:arkose_labs_api_key) { "api-key" } + let(:arkose_labs_domain) { "domain" } + + subject { render(template: 'devise/registrations/new') } + + before do + stub_devise + + allow(::Arkose::Settings).to receive(:arkose_public_api_key).and_return(arkose_labs_api_key) + allow(::Arkose::Settings).to receive(:arkose_labs_domain).and_return(arkose_labs_domain) + end + + it 'renders challenge container with the correct data attributes', :aggregate_failures do + subject + + expect(rendered).to have_selector('#js-arkose-labs-challenge') + expect(rendered).to have_selector("[data-api-key='#{arkose_labs_api_key}']") + expect(rendered).to have_selector("[data-domain='#{arkose_labs_domain}']") + end + + context 'when the :arkose_labs_signup_challenge feature flag is disabled' do + before do + stub_feature_flags(arkose_labs_signup_challenge: false) + end + + it 'does not render challenge container', :aggregate_failures do + subject + + expect(rendered).not_to have_selector('#js-arkose-labs-challenge') + expect(rendered).not_to have_selector("[data-api-key='#{arkose_labs_api_key}']") + expect(rendered).not_to have_selector("[data-domain='#{arkose_labs_domain}']") + end + end + + def stub_devise + allow(view).to receive(:devise_mapping).and_return(Devise.mappings[:user]) + allow(view).to receive(:resource).and_return(build(:user)) + allow(view).to receive(:resource_name).and_return(:user) + end +end diff --git a/ee/spec/views/devise/sessions/new.html.haml_spec.rb b/ee/spec/views/devise/sessions/new.html.haml_spec.rb index 6356b53a5d4af2e90d2d1dcb053a5a7133aef527..53d56ee0553d7e3e03bc35317b7a5f98bac86f4b 100644 --- a/ee/spec/views/devise/sessions/new.html.haml_spec.rb +++ b/ee/spec/views/devise/sessions/new.html.haml_spec.rb @@ -25,15 +25,15 @@ end it 'renders the challenge container' do - expect(rendered).to have_css('.js-arkose-labs-challenge') + expect(rendered).to have_css('#js-arkose-labs-challenge') end it 'passes the API key to the challenge container' do - expect(rendered).to have_selector('.js-arkose-labs-challenge[data-api-key="arkose-api-key"]') + expect(rendered).to have_selector('#js-arkose-labs-challenge[data-api-key="arkose-api-key"]') end it 'passes the ArkoseLabs domain to the challenge container' do - expect(rendered).to have_selector('.js-arkose-labs-challenge[data-domain="gitlab-api.arkoselab.com"]') + expect(rendered).to have_selector('#js-arkose-labs-challenge[data-domain="gitlab-api.arkoselab.com"]') end end @@ -45,7 +45,7 @@ end it 'does not render challenge container' do - expect(rendered).not_to have_css('.js-arkose-labs-challenge') + expect(rendered).not_to have_css('#js-arkose-labs-challenge') end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 65281a6bc1577307abf9ffd49f1d1f0cc7d6bbc9..ec6a1c98e12d1975293dc47b1f174d378d296f07 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -9704,6 +9704,9 @@ msgstr "" msgid "Complete verification to sign in." msgstr "" +msgid "Complete verification to sign up." +msgstr "" + msgid "Completed" msgstr "" @@ -29845,6 +29848,9 @@ msgstr "" msgid "Please wait while we import the repository for you. Refresh at will." msgstr "" +msgid "Please wait while we prepare for verification." +msgstr "" + msgid "Pods in use" msgstr "" diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb index 70d4559edc10c4cbf487a152edd3e64d08069284..fcf7331423cdfa7e4c811506053c805ad2fba62f 100644 --- a/spec/controllers/registrations_controller_spec.rb +++ b/spec/controllers/registrations_controller_spec.rb @@ -7,6 +7,7 @@ before do stub_application_setting(require_admin_approval_after_user_signup: false) + stub_feature_flags(arkose_labs_signup_challenge: false) end describe '#new' do diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb index 1baa97096d9b340f786e6d3c352a83972b10b153..a86e4cead48c46307be522e700befb547665c88d 100644 --- a/spec/features/invites_spec.rb +++ b/spec/features/invites_spec.rb @@ -10,6 +10,7 @@ let(:group_invite) { group.group_members.invite.last } before do + stub_feature_flags(arkose_labs_signup_challenge: false) stub_application_setting(require_admin_approval_after_user_signup: false) project.add_maintainer(owner) group.add_owner(owner) diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb index fa6a917a0597bd86c9e732c223c9be5bdaa4ad6f..998570da8b29c957c41647559d47fab2879863fb 100644 --- a/spec/features/users/signup_spec.rb +++ b/spec/features/users/signup_spec.rb @@ -66,6 +66,7 @@ def confirm_email flag_values = [true, false] flag_values.each do |val| before do + stub_feature_flags(arkose_labs_signup_challenge: false) stub_feature_flags(restyle_login_page: val) stub_application_setting(require_admin_approval_after_user_signup: false) end