Commit 36bc109f authored by Gosia Ksionek's avatar Gosia Ksionek Committed by GitLab Release Tools Bot

Create migration for token-iv pair

And new model

Add index to new table

Add saving token with iv

Add changelog entry

Add new column to new table

WIP

Change method to look for iv

Fix rubocop offences

Fix some rubocop offences

Fix more specs

Fix migration file and spec

Fix specs in migrations

WiP on specs

Add specs for model

WIP

Add spec for new method behauviour

Fix rubocop offences

Add cr remarks

Fix migration file

Add cr remark

Simplify one method

Add option for read only db

Add cr remarks

Add cr remarks

Add cr remarks
parent 24590717
......@@ -85,10 +85,17 @@ module TokenAuthenticatableStrategies
end
def find_by_encrypted_token(token, unscoped)
encrypted_value = Gitlab::CryptoHelper.aes256_gcm_encrypt(token)
encrypted_value = Gitlab::CryptoHelper.aes256_gcm_encrypt(token, nonce: find_hashed_iv(token) || Gitlab::CryptoHelper::AES256_GCM_IV_STATIC)
relation(unscoped).find_by(encrypted_field => encrypted_value)
end
def find_hashed_iv(token)
token_record = TokenWithIv.find_by_plaintext_token(token)
token_record&.iv
end
def insecure_strategy
@insecure_strategy ||= TokenAuthenticatableStrategies::Insecure
.new(klass, token_field, options)
......
# frozen_string_literal: true
# rubocop: todo Gitlab/NamespacedClass
class TokenWithIv < ApplicationRecord
validates :hashed_token, presence: true
validates :iv, presence: true
validates :hashed_plaintext_token, presence: true
def self.find_by_hashed_token(value)
find_by(hashed_token: ::Digest::SHA256.digest(value))
end
def self.find_by_plaintext_token(value)
find_by(hashed_plaintext_token: ::Digest::SHA256.digest(value))
end
end
---
title: Fix AES-GCM issues in lib/gitlab/crypto_helper.rb
merge_request:
author:
type: security
# frozen_string_literal: true
class CreateTokensWithIv < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table :token_with_ivs do |t|
t.binary :hashed_token, null: false
t.binary :hashed_plaintext_token, null: false
t.binary :iv, null: false
t.index :hashed_token, name: 'index_token_with_ivs_on_hashed_token', unique: true, using: :btree
t.index :hashed_plaintext_token, name: 'index_token_with_ivs_on_hashed_plaintext_token', unique: true, using: :btree
end
end
end
......@@ -10,7 +10,7 @@ class EncryptFeatureFlagsClientsTokens < ActiveRecord::Migration[5.1]
def up
say_with_time("Encrypting tokens from operations_feature_flags_clients") do
FeatureFlagsClient.where('token_encrypted is NULL AND token IS NOT NULL').find_each do |feature_flags_client|
token_encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(feature_flags_client.token)
token_encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(feature_flags_client.token, nonce: Gitlab::CryptoHelper::AES256_GCM_IV_STATIC)
feature_flags_client.update!(token_encrypted: token_encrypted)
end
end
......
......@@ -10,7 +10,7 @@ class EncryptDeployTokensTokens < ActiveRecord::Migration[5.1]
def up
say_with_time("Encrypting tokens from deploy_tokens") do
DeploymentTokens.where('token_encrypted is NULL AND token IS NOT NULL').find_each(batch_size: 10000) do |deploy_token|
token_encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(deploy_token.token)
token_encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(deploy_token.token, nonce: Gitlab::CryptoHelper::AES256_GCM_IV_STATIC)
deploy_token.update!(token_encrypted: token_encrypted)
end
end
......
dde424c434c78e22087123fa30eec75c07268a9079fea44339915747aae235e0
\ No newline at end of file
......@@ -17372,6 +17372,22 @@ CREATE SEQUENCE todos_id_seq
ALTER SEQUENCE todos_id_seq OWNED BY todos.id;
CREATE TABLE token_with_ivs (
id bigint NOT NULL,
hashed_token bytea NOT NULL,
hashed_plaintext_token bytea NOT NULL,
iv bytea NOT NULL
);
CREATE SEQUENCE token_with_ivs_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE token_with_ivs_id_seq OWNED BY token_with_ivs.id;
CREATE TABLE trending_projects (
id integer NOT NULL,
project_id integer NOT NULL
......@@ -19087,6 +19103,8 @@ ALTER TABLE ONLY timelogs ALTER COLUMN id SET DEFAULT nextval('timelogs_id_seq':
ALTER TABLE ONLY todos ALTER COLUMN id SET DEFAULT nextval('todos_id_seq'::regclass);
ALTER TABLE ONLY token_with_ivs ALTER COLUMN id SET DEFAULT nextval('token_with_ivs_id_seq'::regclass);
ALTER TABLE ONLY trending_projects ALTER COLUMN id SET DEFAULT nextval('trending_projects_id_seq'::regclass);
ALTER TABLE ONLY u2f_registrations ALTER COLUMN id SET DEFAULT nextval('u2f_registrations_id_seq'::regclass);
......@@ -20606,6 +20624,9 @@ ALTER TABLE ONLY timelogs
ALTER TABLE ONLY todos
ADD CONSTRAINT todos_pkey PRIMARY KEY (id);
ALTER TABLE ONLY token_with_ivs
ADD CONSTRAINT token_with_ivs_pkey PRIMARY KEY (id);
ALTER TABLE ONLY trending_projects
ADD CONSTRAINT trending_projects_pkey PRIMARY KEY (id);
......@@ -23124,6 +23145,10 @@ CREATE INDEX index_todos_on_user_id_and_id_done ON todos USING btree (user_id, i
CREATE INDEX index_todos_on_user_id_and_id_pending ON todos USING btree (user_id, id) WHERE ((state)::text = 'pending'::text);
CREATE UNIQUE INDEX index_token_with_ivs_on_hashed_plaintext_token ON token_with_ivs USING btree (hashed_plaintext_token);
CREATE UNIQUE INDEX index_token_with_ivs_on_hashed_token ON token_with_ivs USING btree (hashed_token);
CREATE UNIQUE INDEX index_trending_projects_on_project_id ON trending_projects USING btree (project_id);
CREATE INDEX index_u2f_registrations_on_key_handle ON u2f_registrations USING btree (key_handle);
......
......@@ -23,7 +23,7 @@ RSpec.describe 'Geo read-only message', :geo do
context 'when in maintenance mode' do
before do
stub_application_setting(maintenance_mode: true)
stub_maintenance_mode_setting(true)
end
it_behaves_like 'Read-only instance', /This GitLab instance is undergoing maintenance and is operating in read\-only mode./
......
......@@ -22,7 +22,7 @@ RSpec.describe ApplicationHelper do
context 'maintenance mode' do
context 'enabled' do
before do
stub_application_setting(maintenance_mode: true)
stub_maintenance_mode_setting(true)
end
it 'returns default message' do
......@@ -48,7 +48,7 @@ RSpec.describe ApplicationHelper do
context 'disabled' do
it 'returns nil' do
stub_application_setting(maintenance_mode: false)
stub_maintenance_mode_setting(false)
expect(helper.read_only_message).to be_nil
end
......@@ -60,7 +60,7 @@ RSpec.describe ApplicationHelper do
context 'maintenance mode on' do
it 'returns messages for both' do
expect(Gitlab::Geo).to receive(:secondary?).twice { true }
stub_application_setting(maintenance_mode: true)
stub_maintenance_mode_setting(true)
expect(helper.read_only_message).to match(/you must visit the primary site/)
expect(helper.read_only_message).to match(/#{default_maintenance_mode_message}/)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::CryptoHelper do
include ::EE::GeoHelpers
describe '.read_only?' do
context 'with Geo enabled' do
before do
allow(Gitlab::Geo).to receive(:enabled?) { true }
allow(Gitlab::Geo).to receive(:current_node) { geo_node }
end
context 'is Geo secondary node' do
let(:geo_node) { create(:geo_node) }
it 'returns true' do
expect(described_class.read_only?).to be_truthy
end
end
context 'is Geo primary node' do
let(:geo_node) { create(:geo_node, :primary) }
it 'returns false when is Geo primary node' do
expect(described_class.read_only?).to be_falsey
end
end
end
end
end
......@@ -37,7 +37,7 @@ RSpec.describe Gitlab::Database do
context 'in maintenance mode' do
before do
stub_application_setting(maintenance_mode: true)
stub_maintenance_mode_setting(true)
end
it 'returns true' do
......
......@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Middleware::ReadOnly do
context 'when maintenance mode is on' do
before do
stub_application_setting(maintenance_mode: true)
stub_maintenance_mode_setting(true)
end
it_behaves_like 'write access for a read-only GitLab (EE) instance in maintenance mode'
......@@ -13,7 +13,7 @@ RSpec.describe Gitlab::Middleware::ReadOnly do
context 'when maintenance mode is not on' do
before do
stub_application_setting(maintenance_mode: false)
stub_maintenance_mode_setting(false)
end
it_behaves_like 'write access for a read-only GitLab (EE) instance'
......
......@@ -758,7 +758,7 @@ RSpec.describe Gitlab::GitAccess do
context 'when maintenance mode is enabled' do
before do
stub_application_setting(maintenance_mode: true)
stub_maintenance_mode_setting(true)
end
it 'blocks git push' do
......@@ -770,7 +770,7 @@ RSpec.describe Gitlab::GitAccess do
context 'when maintenance mode is disabled' do
before do
stub_application_setting(maintenance_mode: false)
stub_maintenance_mode_setting(false)
end
it 'allows git push' do
......
......@@ -12,8 +12,8 @@ RSpec.describe NullifyFeatureFlagPlaintextTokens do
let!(:project1) { projects.create!(namespace_id: namespace.id, name: 'Project 1') }
let!(:project2) { projects.create!(namespace_id: namespace.id, name: 'Project 2') }
let(:secret1_encrypted) { Gitlab::CryptoHelper.aes256_gcm_encrypt('secret1') }
let(:secret2_encrypted) { Gitlab::CryptoHelper.aes256_gcm_encrypt('secret2') }
let(:secret1_encrypted) { Gitlab::CryptoHelper.aes256_gcm_encrypt('secret1', nonce: Gitlab::CryptoHelper::AES256_GCM_IV_STATIC) }
let(:secret2_encrypted) { Gitlab::CryptoHelper.aes256_gcm_encrypt('secret2', nonce: Gitlab::CryptoHelper::AES256_GCM_IV_STATIC) }
before do
feature_flags_clients.create!(token: 'secret1', token_encrypted: secret1_encrypted, project_id: project1.id)
......
......@@ -248,7 +248,7 @@ RSpec.describe API::Internal::Base do
let_it_be(:project) { create(:project, :repository) }
before do
stub_application_setting(maintenance_mode: true)
stub_maintenance_mode_setting(true)
project.add_developer(user)
end
......
......@@ -19,7 +19,7 @@ RSpec.describe Auth::ContainerRegistryAuthenticationService do
end
before do
stub_application_setting(maintenance_mode: true)
stub_maintenance_mode_setting(true)
project.add_developer(current_user)
end
......
......@@ -7,7 +7,7 @@ RSpec.shared_examples 'write access for a read-only GitLab (EE) instance in main
include_context 'with a mocked GitLab instance'
before do
stub_application_setting(maintenance_mode: true)
stub_maintenance_mode_setting(true)
end
context 'normal requests to a read-only GitLab instance' do
......
......@@ -118,6 +118,7 @@ module Gitlab
def self.maintenance_mode?
return false unless ::Feature.enabled?(:maintenance_mode)
return false unless ::Gitlab::CurrentSettings.current_application_settings?
::Gitlab::CurrentSettings.maintenance_mode
end
......
......@@ -6,25 +6,74 @@ module Gitlab
AES256_GCM_OPTIONS = {
algorithm: 'aes-256-gcm',
key: Settings.attr_encrypted_db_key_base_32,
iv: Settings.attr_encrypted_db_key_base_12
key: Settings.attr_encrypted_db_key_base_32
}.freeze
AES256_GCM_IV_STATIC = Settings.attr_encrypted_db_key_base_12
def sha256(value)
salt = Settings.attr_encrypted_db_key_base_truncated
::Digest::SHA256.base64digest("#{value}#{salt}")
end
def aes256_gcm_encrypt(value)
encrypted_token = Encryptor.encrypt(AES256_GCM_OPTIONS.merge(value: value))
Base64.strict_encode64(encrypted_token)
def aes256_gcm_encrypt(value, nonce: nil)
return aes256_gcm_encrypt_for_non_read_db(value) if read_only?
found_nonce = nonce || find_nonce_by_token(value)
iv = found_nonce || create_nonce
encrypted_token = create_encrypted_token(value, iv)
save_token_with_nonce!(encrypted_token, value, iv) unless found_nonce
encrypted_token
end
def aes256_gcm_decrypt(value)
return unless value
nonce = find_nonce_by_hashed_token(value)
encrypted_token = Base64.decode64(value)
Encryptor.decrypt(AES256_GCM_OPTIONS.merge(value: encrypted_token))
decrypted_token = Encryptor.decrypt(AES256_GCM_OPTIONS.merge(value: encrypted_token, iv: nonce || AES256_GCM_IV_STATIC))
aes256_gcm_encrypt(value) unless nonce
decrypted_token
end
def read_only?
Gitlab::Database.read_only?
end
def aes256_gcm_encrypt_for_non_read_db(value)
create_encrypted_token(value, AES256_GCM_IV_STATIC)
end
def create_encrypted_token(value, iv)
encrypted_token = Encryptor.encrypt(AES256_GCM_OPTIONS.merge(value: value, iv: iv))
Base64.strict_encode64(encrypted_token)
end
def save_token_with_nonce!(encrypted_token, plaintext_token, nonce)
return unless TokenWithIv.table_exists?
TokenWithIv.create!(hashed_token: Digest::SHA256.digest(encrypted_token), hashed_plaintext_token: Digest::SHA256.digest(plaintext_token), iv: nonce)
end
def create_nonce
cipher = OpenSSL::Cipher.new('aes-256-gcm')
cipher.encrypt # Required before '#random_iv' can be called
cipher.random_iv # Ensures that the IV is the correct length respective to the algorithm used.
end
def find_nonce_by_hashed_token(value)
return unless TokenWithIv.table_exists?
token_record = TokenWithIv.find_by_hashed_token(value)
token_record&.iv
end
def find_nonce_by_token(value)
return unless TokenWithIv.table_exists?
token_record = TokenWithIv.find_by_plaintext_token(value)
token_record&.iv
end
end
end
......@@ -7,6 +7,10 @@ module Gitlab
Gitlab::SafeRequestStore.fetch(:current_application_settings) { ensure_application_settings! }
end
def current_application_settings?
Gitlab::SafeRequestStore.exist?(:current_application_settings) || ::ApplicationSetting.current.present?
end
def expire_current_application_settings
::ApplicationSetting.expire
Gitlab::SafeRequestStore.delete(:current_application_settings)
......
......@@ -27,7 +27,8 @@ RSpec.describe Admin::RunnersController do
# There is still an N+1 query for `runner.builds.count`
# We also need to add 1 because it takes 2 queries to preload tags
expect { get :index }.not_to exceed_query_limit(control_count + 6)
# also looking for token nonce requires database queries
expect { get :index }.not_to exceed_query_limit(control_count + 16)
expect(response).to have_gitlab_http_status(:ok)
expect(response.body).to have_content('tag1')
......
# frozen_string_literal: true
FactoryBot.define do
factory :token_with_iv do
hashed_token { ::Digest::SHA256.digest(SecureRandom.hex(50)) }
iv { ::Digest::SHA256.digest(SecureRandom.hex(50)) }
hashed_plaintext_token { ::Digest::SHA256.digest(SecureRandom.hex(50)) }
end
end
......@@ -19,21 +19,83 @@ RSpec.describe Gitlab::CryptoHelper do
expect(encrypted).to match %r{\A[A-Za-z0-9+/=]+\z}
expect(encrypted).not_to include "\n"
end
it 'saves hashed token with iv value in database' do
expect { described_class.aes256_gcm_encrypt('some-value') }.to change { TokenWithIv.count }.by(1)
end
it 'saves hashed token in database' do
encrypted_token = described_class.aes256_gcm_encrypt('some-value')
expect(TokenWithIv.last.hashed_token).to eq(Digest::SHA256.digest(encrypted_token))
end
it 'saves digested plaintext token in database' do
described_class.aes256_gcm_encrypt('some-value')
expect(TokenWithIv.last.hashed_plaintext_token).to eq(Digest::SHA256.digest('some-value'))
end
context 'when we are encrypting the same token for a second time' do
before do
described_class.aes256_gcm_encrypt('some-value')
end
it 'does not save digested plaintext token in database' do
expect { described_class.aes256_gcm_encrypt('some-value') }.not_to change { TokenWithIv.count }
end
end
context 'when read only is true' do
before do
allow(described_class).to receive(:read_only?).and_return(true)
end
it 'does not save tokens in database' do
expect { described_class.aes256_gcm_encrypt('some-value') }.not_to change { TokenWithIv.count }
end
it 'encrypts using static iv' do
expect(Encryptor).to receive(:encrypt).with(described_class::AES256_GCM_OPTIONS.merge(value: 'some-value', iv: described_class::AES256_GCM_IV_STATIC)).and_return('hashed_value')
described_class.aes256_gcm_encrypt('some-value')
end
end
end
describe '.aes256_gcm_decrypt' do
let(:encrypted) { described_class.aes256_gcm_encrypt('some-value') }
context 'when token was encrypted using static nonce' do
let(:encrypted) { described_class.aes256_gcm_encrypt('some-value', nonce: described_class::AES256_GCM_IV_STATIC) }
it 'correctly decrypts encrypted string' do
decrypted = described_class.aes256_gcm_decrypt(encrypted)
it 'correctly decrypts encrypted string' do
decrypted = described_class.aes256_gcm_decrypt(encrypted)
expect(decrypted).to eq 'some-value'
expect(decrypted).to eq 'some-value'
end
it 'decrypts a value when it ends with a new line character' do
decrypted = described_class.aes256_gcm_decrypt(encrypted + "\n")
expect(decrypted).to eq 'some-value'
end
it 'saves hashed token with iv value in database' do
expect { described_class.aes256_gcm_decrypt(encrypted) }.to change { TokenWithIv.count }.by(1)
end
end
it 'decrypts a value when it ends with a new line character' do
decrypted = described_class.aes256_gcm_decrypt(encrypted + "\n")
context 'when token was encrypted using random nonce' do
let!(:encrypted) { described_class.aes256_gcm_encrypt('some-value') }
it 'correctly decrypts encrypted string' do
decrypted = described_class.aes256_gcm_decrypt(encrypted)
expect(decrypted).to eq 'some-value'
end
expect(decrypted).to eq 'some-value'
it 'does not save hashed token with iv value in database' do
expect { described_class.aes256_gcm_decrypt(encrypted) }.not_to change { TokenWithIv.count }
end
end
end
end
......@@ -194,4 +194,32 @@ RSpec.describe Gitlab::CurrentSettings do
end
end
end
describe '#current_application_settings?', :use_clean_rails_memory_store_caching do
before do
allow(Gitlab::CurrentSettings).to receive(:current_application_settings?).and_call_original
end
it 'returns true when settings exist' do
create(:application_setting,
home_page_url: 'http://mydomain.com',
signup_enabled: false)
expect(described_class.current_application_settings?).to eq(true)
end
it 'returns false when settings do not exist' do
expect(described_class.current_application_settings?).to eq(false)
end
context 'with cache', :request_store do
include_context 'with settings in cache'
it 'returns an in-memory ApplicationSetting object' do
expect(ApplicationSetting).not_to receive(:current)
expect(described_class.current_application_settings?).to eq(true)
end
end
end
end
......@@ -332,13 +332,13 @@ RSpec.describe Gitlab do
describe '.maintenance_mode?' do
it 'returns true when maintenance mode is enabled' do
stub_application_setting(maintenance_mode: true)
stub_maintenance_mode_setting(true)
expect(described_class.maintenance_mode?).to eq(true)
end
it 'returns false when maintenance mode is disabled' do
stub_application_setting(maintenance_mode: false)
stub_maintenance_mode_setting(false)
expect(described_class.maintenance_mode?).to eq(false)
end
......
......@@ -8,7 +8,7 @@ RSpec.describe EncryptFeatureFlagsClientsTokens do
let(:feature_flags_clients) { table(:operations_feature_flags_clients) }
let(:projects) { table(:projects) }
let(:plaintext) { "secret-token" }
let(:ciphertext) { Gitlab::CryptoHelper.aes256_gcm_encrypt(plaintext) }
let(:ciphertext) { Gitlab::CryptoHelper.aes256_gcm_encrypt(plaintext, nonce: Gitlab::CryptoHelper::AES256_GCM_IV_STATIC) }
describe '#up' do
it 'keeps plaintext token the same and populates token_encrypted if not present' do
......
......@@ -358,7 +358,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
it 'calls .destroy_sessions' do
expect(ActiveSession).to(
receive(:destroy_sessions)
.with(anything, user, [active_session.public_id, rack_session.public_id, rack_session.private_id]))
.with(anything, user, [encrypted_active_session_id, rack_session.public_id, rack_session.private_id]))
subject
end
......
......@@ -53,8 +53,9 @@ RSpec.describe ApplicationSetting, 'TokenAuthenticatable' do
it 'persists new token as an encrypted string' do
expect(subject).to eq settings.reload.runners_registration_token
nonce = TokenWithIv.find_by_hashed_token(settings.read_attribute('runners_registration_token_encrypted')).iv
expect(settings.read_attribute('runners_registration_token_encrypted'))
.to eq Gitlab::CryptoHelper.aes256_gcm_encrypt(subject)
.to eq Gitlab::CryptoHelper.aes256_gcm_encrypt(subject, nonce: nonce)
expect(settings).to be_persisted
end
......@@ -243,7 +244,8 @@ RSpec.describe Ci::Build, 'TokenAuthenticatable' do
it 'persists new token as an encrypted string' do
build.ensure_token!
encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(build.token)
nonce = TokenWithIv.find_by_hashed_token(build.token_encrypted).iv
encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(build.token, nonce: nonce)
expect(build.read_attribute('token_encrypted')).to eq encrypted
end
......
......@@ -124,7 +124,7 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
it 'writes encrypted token and removes plaintext token and returns it' do
expect(instance).to receive(:[]=)
.with('some_field_encrypted', encrypted)
.with('some_field_encrypted', any_args)
expect(instance).to receive(:[]=)
.with('some_field', nil)
......@@ -137,7 +137,7 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
it 'writes encrypted token and writes plaintext token' do
expect(instance).to receive(:[]=)
.with('some_field_encrypted', encrypted)
.with('some_field_encrypted', any_args)
expect(instance).to receive(:[]=)
.with('some_field', 'my-value')
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe TokenWithIv do
describe 'validations' do
it { is_expected.to validate_presence_of :hashed_token }
it { is_expected.to validate_presence_of :iv }
it { is_expected.to validate_presence_of :hashed_plaintext_token }
end
describe '.find_by_hashed_token' do
it 'only includes matching record' do
matching_record = create(:token_with_iv, hashed_token: ::Digest::SHA256.digest('hashed-token'))
create(:token_with_iv)
expect(described_class.find_by_hashed_token('hashed-token')).to eq(matching_record)
end
end
describe '.find_by_plaintext_token' do
it 'only includes matching record' do
matching_record = create(:token_with_iv, hashed_plaintext_token: ::Digest::SHA256.digest('hashed-token'))
create(:token_with_iv)
expect(described_class.find_by_plaintext_token('hashed-token')).to eq(matching_record)
end
end
end
......@@ -289,6 +289,8 @@ RSpec.configure do |config|
current_user_mode.send(:user)&.admin?
end
end
allow(Gitlab::CurrentSettings).to receive(:current_application_settings?).and_return(false)
end
config.around(:example, :quarantine) do |example|
......
......@@ -121,6 +121,12 @@ module StubConfiguration
allow(::Gitlab.config.packages).to receive_messages(to_settings(messages))
end
def stub_maintenance_mode_setting(value)
allow(Gitlab::CurrentSettings).to receive(:current_application_settings?).and_return(true)
stub_application_setting(maintenance_mode: value)
end
private
# Modifies stubbed messages to also stub possible predicate versions
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment