diff --git a/ee/app/controllers/ee/sessions_controller.rb b/ee/app/controllers/ee/sessions_controller.rb index 49f86271a66041695f999d4c6b4f63670bb28709..f33527408e39f037d99c57fded665ab6e182bf40 100644 --- a/ee/app/controllers/ee/sessions_controller.rb +++ b/ee/app/controllers/ee/sessions_controller.rb @@ -103,7 +103,9 @@ def check_arkose_captcha end def verify_arkose_token(user) - if Arkose::UserVerificationService.new(session_token: params[:arkose_labs_token], user: user).execute + result = Arkose::TokenVerificationService.new(session_token: params[:arkose_labs_token], user: user).execute + + if result.success? && result.payload[:low_risk] increment_successful_login_captcha_counter else failed_login_captcha diff --git a/ee/app/services/arkose/record_user_data_service.rb b/ee/app/services/arkose/record_user_data_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..5861927bcbc95e2befe7605e2699ed9ebb15ae83 --- /dev/null +++ b/ee/app/services/arkose/record_user_data_service.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Arkose + class RecordUserDataService + attr_reader :response, :user + + def initialize(response:, user:) + @response = response + @user = user + end + + def execute + return ServiceResponse.error(message: 'user is required') unless user.present? + return ServiceResponse.error(message: 'Invalid Arkose Labs token') if response.invalid_token? + + add_or_update_arkose_attributes + + ServiceResponse.success + end + + private + + def add_or_update_arkose_attributes + return if Gitlab::Database.read_only? + + UserCustomAttribute.upsert_custom_attributes(custom_attributes) + end + + def custom_attributes + custom_attributes = [] + custom_attributes.push({ key: 'arkose_session', value: response.session_id }) + custom_attributes.push({ key: 'arkose_risk_band', value: response.risk_band }) + custom_attributes.push({ key: 'arkose_global_score', value: response.global_score }) + custom_attributes.push({ key: 'arkose_custom_score', value: response.custom_score }) + + custom_attributes.map! { |custom_attribute| custom_attribute.merge({ user_id: user.id }) } + custom_attributes + end + end +end diff --git a/ee/app/services/arkose/user_verification_service.rb b/ee/app/services/arkose/token_verification_service.rb similarity index 53% rename from ee/app/services/arkose/user_verification_service.rb rename to ee/app/services/arkose/token_verification_service.rb index fbb777131c60f946bc6832be975f81f61fa82e3e..2b97df00d03b3b4da76e7a1af19b6d949fbbedd3 100644 --- a/ee/app/services/arkose/user_verification_service.rb +++ b/ee/app/services/arkose/token_verification_service.rb @@ -1,78 +1,71 @@ # frozen_string_literal: true + module Arkose - class UserVerificationService - attr_reader :url, :session_token, :user, :response + class TokenVerificationService + attr_reader :session_token, :user ARKOSE_LABS_DEFAULT_NAMESPACE = 'client' ARKOSE_LABS_DEFAULT_SUBDOMAIN = 'verify-api' - def initialize(session_token:, user:) + def initialize(session_token:, user: nil) @session_token = session_token @user = user end def execute - json_response = Gitlab::HTTP.perform_request(Net::HTTP::Post, arkose_verify_url, body: body).parsed_response - @response = VerifyResponse.new(json_response) + response = Gitlab::HTTP.perform_request(Net::HTTP::Post, arkose_verify_url, body: body).parsed_response + response = ::Arkose::VerifyResponse.new(response) - logger.info(build_message) + logger.info(build_message(response)) - return false if response.invalid_token? + return ServiceResponse.error(message: response.error) if response.invalid_token? - add_or_update_arkose_attributes + RecordUserDataService.new(response: response, user: user).execute - response.allowlisted? || (response.challenge_solved? && response.low_risk?) + if response.allowlisted? || response.challenge_solved? + ServiceResponse.success(payload: { low_risk: response.allowlisted? || response.low_risk? }) + else + ServiceResponse.error(message: 'Captcha was not solved') + end rescue StandardError => error - payload = { session_token: session_token, log_data: user.id } + payload = { + # Allow user to proceed when we can't verify the token for some + # unexpected reason (e.g. ArkoseLabs is down) + low_risk: true, + session_token: session_token, + log_data: user&.id + }.compact Gitlab::ExceptionLogFormatter.format!(error, payload) Gitlab::ErrorTracking.track_exception(error) logger.error("Error verifying user on Arkose: #{payload}") - true + ServiceResponse.success(payload: payload) end private - def add_or_update_arkose_attributes - return if Gitlab::Database.read_only? - - UserCustomAttribute.upsert_custom_attributes(custom_attributes) - end - - def custom_attributes - custom_attributes = [] - custom_attributes.push({ key: 'arkose_session', value: response.session_id }) - custom_attributes.push({ key: 'arkose_risk_band', value: response.risk_band }) - custom_attributes.push({ key: 'arkose_global_score', value: response.global_score }) - custom_attributes.push({ key: 'arkose_custom_score', value: response.custom_score }) - - custom_attributes.map! { |custom_attribute| custom_attribute.merge({ user_id: user.id }) } - custom_attributes - end - def body { private_key: Settings.arkose_private_api_key, session_token: session_token, - log_data: user.id - } + log_data: user&.id + }.compact end def logger Gitlab::AppLogger end - def build_message + def build_message(response) Gitlab::ApplicationContext.current.symbolize_keys.merge( { message: 'Arkose verify response', response: response.response, - username: user.username - }.merge(arkose_payload) - ) + username: user&.username + }.compact).merge(arkose_payload(response)) end - def arkose_payload + def arkose_payload(response) { 'arkose.session_id': response.session_id, 'arkose.global_score': response.global_score, diff --git a/ee/lib/arkose/verify_response.rb b/ee/lib/arkose/verify_response.rb index 60e7fc05d9ac67a78f6ebfdffe7a081d3a50ae75..f5c4748b157249db1785c00e1c67ba10808a7837 100644 --- a/ee/lib/arkose/verify_response.rb +++ b/ee/lib/arkose/verify_response.rb @@ -4,9 +4,15 @@ module Arkose class VerifyResponse attr_reader :response + InvalidResponseFormatError = Class.new(StandardError) + ALLOWLIST_TELLTALE = 'gitlab1-whitelist-qa-team' def initialize(response) + unless response.is_a? Hash + raise InvalidResponseFormatError, "Arkose Labs Verify API returned a #{response.class} instead of of an object" + end + @response = response end diff --git a/ee/spec/controllers/ee/sessions_controller_spec.rb b/ee/spec/controllers/ee/sessions_controller_spec.rb index 24c08377a8e4808f887a27273a187f7c8d7b38ff..abf0c7826b40de3688043411bd2407cbd2971175 100644 --- a/ee/spec/controllers/ee/sessions_controller_spec.rb +++ b/ee/spec/controllers/ee/sessions_controller_spec.rb @@ -175,20 +175,38 @@ def authenticate_2fa(otp_user_id: user.id, **user_params) end context 'when the user was verified by Arkose' do - it 'successfully logs in a user when reCAPTCHA is solved' do - allow_next_instance_of(Arkose::UserVerificationService) do |instance| - allow(instance).to receive(:execute).and_return(true) + before do + allow_next_instance_of(Arkose::TokenVerificationService) do |instance| + response = ServiceResponse.success(payload: { low_risk: low_risk }) + allow(instance).to receive(:execute).and_return(response) end + post(:create, params: params, session: {}) + end - expect(subject.current_user).to eq user + context 'when user is low risk' do + let(:low_risk) { true } + + it 'successfully logs in the user' do + expect(subject.current_user).to eq user + end + end + + context 'when user is NOT low risk' do + let(:low_risk) { false } + + it 'prevents the user from logging in' do + expect(response).to render_template(:new) + expect(flash[:alert]).to include 'Login failed. Please retry from your primary device and network' + expect(subject.current_user).to be_nil + end end end context 'when the user was not verified by Arkose' do before do - allow_next_instance_of(Arkose::UserVerificationService) do |instance| - allow(instance).to receive(:execute).and_return(false) + allow_next_instance_of(Arkose::TokenVerificationService) do |instance| + allow(instance).to receive(:execute).and_return(ServiceResponse.error(message: 'Captcha was not solved')) end end diff --git a/ee/spec/lib/arkose/verify_response_spec.rb b/ee/spec/lib/arkose/verify_response_spec.rb index 42814c422f22a331a9072c076b73c207b22d94c8..1aa1cdb315190d4a17359e32bf8fc030457b9848 100644 --- a/ee/spec/lib/arkose/verify_response_spec.rb +++ b/ee/spec/lib/arkose/verify_response_spec.rb @@ -27,6 +27,23 @@ def parse_json(file_path) parse_json('ee/spec/fixtures/arkose/allowlisted_response.json') end + describe '.new' do + context 'when response is not a Hash' do + it 'raises an InvalidResponseFormatError error' do + expect { described_class.new('a_string') }.to raise_error( + described_class::InvalidResponseFormatError, + "Arkose Labs Verify API returned a String instead of of an object" + ) + end + end + + context 'when response is a Hash' do + it 'does not raise an InvalidResponseFormatError error' do + expect { described_class.new({}) }.not_to raise_error + end + end + end + describe '#invalid_token?' do subject { described_class.new(json_response).invalid_token? } diff --git a/ee/spec/services/arkose/record_user_data_service_spec.rb b/ee/spec/services/arkose/record_user_data_service_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..3153fc695173999f7dfad931b6b224cb5a0d2713 --- /dev/null +++ b/ee/spec/services/arkose/record_user_data_service_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Arkose::RecordUserDataService do + let(:user) { create(:user) } + + let(:arkose_verify_response) do + Gitlab::Json.parse(File.read(Rails.root.join('ee/spec/fixtures/arkose/successfully_solved_ec_response.json'))) + end + + let(:response) { Arkose::VerifyResponse.new(arkose_verify_response) } + let(:service) { described_class.new(response: response, user: user) } + + describe '#execute' do + it 'adds new custom attributes to the user' do + expect { service.execute }.to change { user.custom_attributes.count }.from(0).to(4) + end + + it 'adds arkose data to custom attributes' do + service.execute + + expect(user.custom_attributes.find_by(key: 'arkose_session').value).to eq('22612c147bb418c8.2570749403') + expect(user.custom_attributes.find_by(key: 'arkose_risk_band').value).to eq('Low') + expect(user.custom_attributes.find_by(key: 'arkose_global_score').value).to eq('0') + expect(user.custom_attributes.find_by(key: 'arkose_custom_score').value).to eq('0') + end + + it 'returns a success response' do + expect(service.execute).to be_success + end + + context 'when response is from failed verification' do + let(:arkose_verify_response) do + Gitlab::Json.parse(File.read(Rails.root.join('ee/spec/fixtures/arkose/invalid_token.json'))) + end + + it 'does not add any custom attributes' do + expect { service.execute }.not_to change { user.custom_attributes.count } + end + + it 'returns an error response' do + expect(service.execute).to be_error + end + end + + context 'when user is nil' do + let(:user) { nil } + + it 'returns an error response' do + expect(service.execute).to be_error + end + end + end +end diff --git a/ee/spec/services/arkose/token_verification_service_spec.rb b/ee/spec/services/arkose/token_verification_service_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..e650249060291ec633475a6ed522711afdefad9d --- /dev/null +++ b/ee/spec/services/arkose/token_verification_service_spec.rb @@ -0,0 +1,287 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Arkose::TokenVerificationService do + let(:user) { create(:user) } + let(:session_token) { '22612c147bb418c8.2570749403' } + let(:service) { described_class.new(session_token: session_token, user: user) } + let(:verify_api_url) { "https://verify-api.arkoselabs.com/api/v4/verify/" } + let(:arkose_labs_private_api_key) { 'foo' } + + subject { service.execute } + + before do + stub_request(:post, verify_api_url) + .with( + body: /.*/, + headers: { + 'Accept' => '*/*' + } + ).to_return( + status: 200, + body: arkose_ec_response.to_json, + headers: { 'Content-Type' => 'application/json' } + ) + end + + describe '#execute' do + shared_examples_for 'interacting with Arkose verify API' do |url| + let(:verify_api_url) { url } + + context 'when the user did not solve the challenge' do + let(:arkose_ec_response) do + Gitlab::Json.parse(File.read(Rails.root.join('ee/spec/fixtures/arkose/failed_ec_response.json'))) + end + + it 'returns an error response' do + expect(subject).to be_error + end + + it 'returns an error message' do + expect(subject.message).to eq 'Captcha was not solved' + end + end + + context 'when feature arkose_labs_prevent_login is enabled' do + context 'when the user solved the challenge' do + context 'when the risk score is low' do + let(:arkose_ec_response) do + Gitlab::Json.parse( + File.read(Rails.root.join('ee/spec/fixtures/arkose/successfully_solved_ec_response.json')) + ) + end + + it 'makes a request to the Verify API' do + subject + + expect(WebMock).to have_requested(:post, verify_api_url) + end + + it 'returns a success response' do + expect(subject).to be_success + end + + it 'returns { low_risk: true } payload' do + expect(subject.payload[:low_risk]).to eq true + end + + it 'logs Arkose verify response' do + allow(Gitlab::AppLogger).to receive(:info) + allow(Gitlab::ApplicationContext).to receive(:current).and_return( + { 'correlation_id': 'be025cf83013ac4f52ffd2bf712b11a2' } + ) + + subject + + expect(Gitlab::AppLogger).to have_received(:info).with( + correlation_id: 'be025cf83013ac4f52ffd2bf712b11a2', + message: 'Arkose verify response', + response: arkose_ec_response, + username: user.username, + 'arkose.session_id': '22612c147bb418c8.2570749403', + 'arkose.global_score': '0', + 'arkose.global_telltale_list': [], + 'arkose.custom_score': '0', + 'arkose.custom_telltale_list': [], + 'arkose.risk_band': 'Low', + 'arkose.risk_category': 'NO-THREAT' + ) + end + + it "records user's Arkose data" do + mock_response = Arkose::VerifyResponse.new(arkose_ec_response) + expect(Arkose::VerifyResponse).to receive(:new).with(arkose_ec_response).and_return(mock_response) + + expect_next_instance_of(Arkose::RecordUserDataService, response: mock_response, user: user) do |service| + expect(service).to receive(:execute) + end + + subject + end + + context 'when the session is allowlisted' do + let(:arkose_ec_response) do + json = Gitlab::Json.parse( + File.read(Rails.root.join('ee/spec/fixtures/arkose/successfully_solved_ec_response_high_risk.json')) + ) + json['session_details']['telltale_list'].push(Arkose::VerifyResponse::ALLOWLIST_TELLTALE) + json + end + + it 'returns a success response' do + expect(subject).to be_success + end + + it 'returns { low_risk: true } payload' do + expect(subject.payload[:low_risk]).to eq true + end + end + + context 'when the risk score is high' do + let(:arkose_ec_response) do + Gitlab::Json.parse( + File.read(Rails.root.join('ee/spec/fixtures/arkose/successfully_solved_ec_response_high_risk.json')) + ) + end + + it 'returns a success response' do + expect(subject).to be_success + end + + it 'returns { low_risk: false } payload' do + expect(subject.payload[:low_risk]).to eq false + end + end + end + end + + context 'when the response does not include the risk session' do + context 'when the user solved the challenge' do + let(:arkose_ec_response) do + Gitlab::Json.parse( + File.read( + Rails.root.join('ee/spec/fixtures/arkose/successfully_solved_ec_response_without_session_risk.json') + ) + ) + end + + it 'returns a success response' do + expect(subject).to be_success + end + + it 'returns { low_risk: true } payload' do + expect(subject.payload[:low_risk]).to eq true + end + end + + context 'when the user did not solve the challenge' do + let(:arkose_ec_response) do + Gitlab::Json.parse( + File.read(Rails.root.join('ee/spec/fixtures/arkose/failed_ec_response_without_risk_session.json')) + ) + end + + it 'returns an error response' do + expect(subject).to be_error + end + + it 'returns an error message' do + expect(subject.message).to eq 'Captcha was not solved' + end + end + end + end + + context 'when response from Arkose is not what we expect' do + # For example: https://gitlab.com/gitlab-org/modelops/anti-abuse/team-tasks/-/issues/54 + + let(:arkose_ec_response) { 'unexpected_from_arkose' } + + it 'returns a success response' do + expect(subject).to be_success + end + + it 'returns { low_risk: true } payload' do + expect(subject.payload[:low_risk]).to eq true + end + + it 'logs the error' do + expect(Gitlab::AppLogger).to receive(:error).with(a_string_matching(/Error verifying user on Arkose: /)) + + subject + end + end + + context 'when an error occurs during the Arkose request' do + let(:arkose_ec_response) { {} } + + before do + allow(Gitlab::HTTP).to receive(:perform_request).and_raise(Errno::ECONNREFUSED.new('bad connection')) + end + + it 'returns a success response' do + expect(subject).to be_success + end + + it 'returns { low_risk: true } payload' do + expect(subject.payload[:low_risk]).to eq true + end + + it 'logs the error' do + expect(Gitlab::AppLogger).to receive(:error).with(a_string_matching(/Error verifying user on Arkose: /)) + + subject + end + end + + context 'when user is nil' do + let(:user) { nil } + let(:arkose_ec_response) do + Gitlab::Json.parse(File.read(Rails.root.join('ee/spec/fixtures/arkose/successfully_solved_ec_response.json'))) + end + + it 'logs Arkose verify response without username' do + allow(Gitlab::AppLogger).to receive(:info) + allow(Gitlab::ApplicationContext).to receive(:current).and_return( + { 'correlation_id': 'be025cf83013ac4f52ffd2bf712b11a2' } + ) + + subject + + expect(Gitlab::AppLogger).to have_received(:info).with( + correlation_id: 'be025cf83013ac4f52ffd2bf712b11a2', + message: 'Arkose verify response', + response: arkose_ec_response, + 'arkose.session_id': '22612c147bb418c8.2570749403', + 'arkose.global_score': '0', + 'arkose.global_telltale_list': [], + 'arkose.custom_score': '0', + 'arkose.custom_telltale_list': [], + 'arkose.risk_band': 'Low', + 'arkose.risk_category': 'NO-THREAT' + ) + end + end + end + + context 'when Arkose is configured using application settings' do + before do + stub_application_setting(arkose_labs_private_api_key: arkose_labs_private_api_key) + stub_application_setting(arkose_labs_namespace: "gitlab") + end + + it_behaves_like 'interacting with Arkose verify API', "https://gitlab-verify.arkoselabs.com/api/v4/verify/" + end + + context 'when Arkose application settings are not present, fallback to environment variables' do + before do + stub_env('ARKOSE_LABS_PRIVATE_KEY': arkose_labs_private_api_key) + end + + it_behaves_like 'interacting with Arkose verify API', "https://verify-api.arkoselabs.com/api/v4/verify/" + end + + context 'when feature arkose_labs_prevent_login is disabled' do + before do + stub_feature_flags(arkose_labs_prevent_login: false) + end + + context 'when the risk score is high' do + let(:arkose_ec_response) do + Gitlab::Json.parse( + File.read(Rails.root.join('ee/spec/fixtures/arkose/successfully_solved_ec_response_high_risk.json')) + ) + end + + it 'returns a success response' do + expect(subject).to be_success + end + + it 'returns { low_risk: true } payload' do + expect(subject.payload[:low_risk]).to eq true + end + end + end + end +end diff --git a/ee/spec/services/arkose/user_verification_service_spec.rb b/ee/spec/services/arkose/user_verification_service_spec.rb deleted file mode 100644 index fbd4d9475e0ca4ce7cc040932cdbe7bceef3e008..0000000000000000000000000000000000000000 --- a/ee/spec/services/arkose/user_verification_service_spec.rb +++ /dev/null @@ -1,166 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Arkose::UserVerificationService do - let(:session_token) { '22612c147bb418c8.2570749403' } - let_it_be_with_reload(:user) { create(:user, id: '1999') } - - let(:service) { Arkose::UserVerificationService.new(session_token: session_token, user: user) } - let(:verify_api_url) { "https://verify-api.arkoselabs.com/api/v4/verify/" } - let(:response_status_code) { 200 } - let(:arkose_labs_private_api_key) { 'foo' } - - subject { service.execute } - - before do - stub_request(:post, verify_api_url) - .with( - body: /.*/, - headers: { - 'Accept' => '*/*' - } - ).to_return(status: response_status_code, body: arkose_ec_response.to_json, headers: { - 'Content-Type' => 'application/json' - }) - end - - describe '#execute' do - shared_examples_for 'interacting with Arkose verify API' do |url| - let(:verify_api_url) { url } - - context 'when the user did not solve the challenge' do - let(:arkose_ec_response) { Gitlab::Json.parse(File.read(Rails.root.join('ee/spec/fixtures/arkose/failed_ec_response.json'))) } - - it 'returns false' do - expect(subject).to be_falsey - end - end - - context 'when feature arkose_labs_prevent_login is enabled' do - context 'when the user solved the challenge' do - context 'when the risk score is not high' do - let(:arkose_ec_response) { Gitlab::Json.parse(File.read(Rails.root.join('ee/spec/fixtures/arkose/successfully_solved_ec_response.json'))) } - - it 'makes a request to the Verify API' do - subject - - expect(WebMock).to have_requested(:post, verify_api_url) - end - - it 'returns true' do - expect(subject).to be_truthy - end - - it 'adds arkose data to custom attributes' do - subject - expect(user.custom_attributes.count).to eq(4) - - expect(user.custom_attributes.find_by(key: 'arkose_session').value).to eq('22612c147bb418c8.2570749403') - expect(user.custom_attributes.find_by(key: 'arkose_risk_band').value).to eq('Low') - expect(user.custom_attributes.find_by(key: 'arkose_global_score').value).to eq('0') - expect(user.custom_attributes.find_by(key: 'arkose_custom_score').value).to eq('0') - end - - it 'logs Arkose verify response' do - allow(Gitlab::AppLogger).to receive(:info) - allow(Gitlab::ApplicationContext).to receive(:current).and_return({ 'correlation_id': 'be025cf83013ac4f52ffd2bf712b11a2' }) - - subject - - expect(Gitlab::AppLogger).to have_received(:info).with(correlation_id: 'be025cf83013ac4f52ffd2bf712b11a2', - message: 'Arkose verify response', - response: arkose_ec_response, - username: user.username, - 'arkose.session_id': '22612c147bb418c8.2570749403', - 'arkose.global_score': '0', - 'arkose.global_telltale_list': [], - 'arkose.custom_score': '0', - 'arkose.custom_telltale_list': [], - 'arkose.risk_band': 'Low', - 'arkose.risk_category': 'NO-THREAT') - end - - context 'when the risk score is high' do - let(:arkose_ec_response) { Gitlab::Json.parse(File.read(Rails.root.join('ee/spec/fixtures/arkose/successfully_solved_ec_response_high_risk.json'))) } - - it 'returns false' do - expect(subject).to be_falsey - end - - context 'when the session is allowlisted' do - let(:arkose_ec_response) do - json = Gitlab::Json.parse(File.read(Rails.root.join('ee/spec/fixtures/arkose/successfully_solved_ec_response.json'))) - json['session_details']['telltale_list'].push(Arkose::VerifyResponse::ALLOWLIST_TELLTALE) - json - end - - it 'returns true' do - expect(subject).to be_truthy - end - end - end - end - end - - context 'when the response does not include the risk session' do - context 'when the user solved the challenge' do - let(:arkose_ec_response) { Gitlab::Json.parse(File.read(Rails.root.join('ee/spec/fixtures/arkose/successfully_solved_ec_response_without_session_risk.json'))) } - - it 'returns true' do - expect(subject).to be_truthy - end - end - - context 'when the user did not solve the challenge' do - let(:arkose_ec_response) { Gitlab::Json.parse(File.read(Rails.root.join('ee/spec/fixtures/arkose/failed_ec_response_without_risk_session.json'))) } - - it 'returns false' do - expect(subject).to be_falsey - end - end - end - end - - context 'when an error occurs during the Arkose request' do - let(:arkose_ec_response) { {} } - let(:response_status_code) { 500 } - - it 'returns true' do - expect(subject).to be_truthy - end - end - end - - context 'when Arkose is configured using application settings' do - before do - stub_application_setting(arkose_labs_private_api_key: arkose_labs_private_api_key) - stub_application_setting(arkose_labs_namespace: "gitlab") - end - - it_behaves_like 'interacting with Arkose verify API', "https://gitlab-verify.arkoselabs.com/api/v4/verify/" - end - - context 'when Arkose application settings are not present, fallback to environment variables' do - before do - stub_env('ARKOSE_LABS_PRIVATE_KEY': arkose_labs_private_api_key) - end - - it_behaves_like 'interacting with Arkose verify API', "https://verify-api.arkoselabs.com/api/v4/verify/" - end - - context 'when feature arkose_labs_prevent_login is disabled' do - before do - stub_feature_flags(arkose_labs_prevent_login: false) - end - - context 'when the risk score is high' do - let(:arkose_ec_response) { Gitlab::Json.parse(File.read(Rails.root.join('ee/spec/fixtures/arkose/successfully_solved_ec_response_high_risk.json'))) } - - it 'returns true' do - expect(subject).to be_truthy - end - end - end - end -end