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