2019-10-20 17:06:17 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-07-15 08:19:29 -04:00
|
|
|
require 'spec_helper'
|
2018-05-19 09:03:29 -04:00
|
|
|
require_relative '../../config/initializers/01_secret_token'
|
2016-07-15 08:19:29 -04:00
|
|
|
|
2020-06-24 05:08:32 -04:00
|
|
|
RSpec.describe 'create_tokens' do
|
2017-01-09 12:19:48 -05:00
|
|
|
include StubENV
|
|
|
|
|
2016-07-15 08:19:29 -04:00
|
|
|
let(:secrets) { ActiveSupport::OrderedOptions.new }
|
2020-05-17 20:08:13 -04:00
|
|
|
let(:hex_key) { /\h{128}/.freeze }
|
|
|
|
let(:rsa_key) { /\A-----BEGIN RSA PRIVATE KEY-----\n.+\n-----END RSA PRIVATE KEY-----\n\Z/m.freeze }
|
2016-12-09 12:36:50 -05:00
|
|
|
|
2016-07-15 08:19:29 -04:00
|
|
|
before do
|
|
|
|
allow(Rails).to receive_message_chain(:application, :secrets).and_return(secrets)
|
|
|
|
allow(Rails).to receive_message_chain(:root, :join) { |string| string }
|
2020-11-19 16:09:07 -05:00
|
|
|
|
|
|
|
allow(File).to receive(:write).and_call_original
|
|
|
|
allow(File).to receive(:write).with(Rails.root.join('config/secrets.yml'))
|
|
|
|
allow(File).to receive(:delete).and_call_original
|
|
|
|
allow(File).to receive(:delete).with(Rails.root.join('.secret'))
|
2016-07-19 10:12:40 -04:00
|
|
|
allow(self).to receive(:warn)
|
|
|
|
allow(self).to receive(:exit)
|
2016-07-15 08:19:29 -04:00
|
|
|
end
|
|
|
|
|
2020-06-24 08:09:24 -04:00
|
|
|
describe 'ensure acknowledged secrets in any installations' do
|
|
|
|
let(:acknowledged_secrets) do
|
2020-11-30 16:09:16 -05:00
|
|
|
%w[secret_key_base otp_key_base db_key_base openid_connect_signing_key encrypted_settings_key_base rotated_encrypted_settings_key_base]
|
2020-06-24 08:09:24 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not allow to add a new secret without a proper handling' do
|
|
|
|
create_tokens
|
|
|
|
|
|
|
|
secrets_hash = YAML.load_file(Rails.root.join('config/secrets.yml'))
|
|
|
|
|
|
|
|
secrets_hash.each do |environment, secrets|
|
|
|
|
new_secrets = secrets.keys - acknowledged_secrets
|
|
|
|
|
|
|
|
expect(new_secrets).to be_empty,
|
|
|
|
<<~EOS
|
|
|
|
CAUTION:
|
|
|
|
It looks like you have just added new secret(s) #{new_secrets.inspect} to the secrets.yml.
|
|
|
|
Please read the development guide for GitLab secrets at doc/development/application_secrets.md before you proceed this change.
|
|
|
|
If you're absolutely sure that the change is safe, please add the new secrets to the 'acknowledged_secrets' in order to silence this warning.
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-12-09 12:36:50 -05:00
|
|
|
context 'setting secret keys' do
|
2016-07-15 08:19:29 -04:00
|
|
|
context 'when none of the secrets exist' do
|
|
|
|
before do
|
2017-01-09 12:19:48 -05:00
|
|
|
stub_env('SECRET_KEY_BASE', nil)
|
2016-07-15 08:19:29 -04:00
|
|
|
allow(File).to receive(:exist?).with('.secret').and_return(false)
|
|
|
|
allow(File).to receive(:exist?).with('config/secrets.yml').and_return(false)
|
|
|
|
allow(self).to receive(:warn_missing_secret)
|
|
|
|
end
|
|
|
|
|
2016-12-09 12:36:50 -05:00
|
|
|
it 'generates different hashes for secret_key_base, otp_key_base, and db_key_base' do
|
2016-07-15 08:19:29 -04:00
|
|
|
create_tokens
|
|
|
|
|
2016-07-17 06:01:38 -04:00
|
|
|
keys = secrets.values_at(:secret_key_base, :otp_key_base, :db_key_base)
|
2016-07-15 08:19:29 -04:00
|
|
|
|
|
|
|
expect(keys.uniq).to eq(keys)
|
2020-05-17 20:08:13 -04:00
|
|
|
expect(keys).to all(match(hex_key))
|
2016-12-09 12:36:50 -05:00
|
|
|
end
|
|
|
|
|
2020-06-16 14:09:01 -04:00
|
|
|
it 'generates an RSA key for openid_connect_signing_key' do
|
2016-12-09 12:36:50 -05:00
|
|
|
create_tokens
|
|
|
|
|
2020-06-16 14:09:01 -04:00
|
|
|
keys = secrets.values_at(:openid_connect_signing_key)
|
2016-12-09 12:36:50 -05:00
|
|
|
|
|
|
|
expect(keys.uniq).to eq(keys)
|
2020-05-17 20:08:13 -04:00
|
|
|
expect(keys).to all(match(rsa_key))
|
2016-07-15 08:19:29 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'warns about the secrets to add to secrets.yml' do
|
2016-07-17 06:01:38 -04:00
|
|
|
expect(self).to receive(:warn_missing_secret).with('secret_key_base')
|
2016-07-15 08:19:29 -04:00
|
|
|
expect(self).to receive(:warn_missing_secret).with('otp_key_base')
|
|
|
|
expect(self).to receive(:warn_missing_secret).with('db_key_base')
|
2017-09-19 11:20:49 -04:00
|
|
|
expect(self).to receive(:warn_missing_secret).with('openid_connect_signing_key')
|
2016-07-15 08:19:29 -04:00
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'writes the secrets to secrets.yml' do
|
|
|
|
expect(File).to receive(:write).with('config/secrets.yml', any_args) do |filename, contents, options|
|
2021-04-23 23:09:38 -04:00
|
|
|
new_secrets = YAML.safe_load(contents)[Rails.env]
|
2016-07-15 08:19:29 -04:00
|
|
|
|
2016-07-17 06:01:38 -04:00
|
|
|
expect(new_secrets['secret_key_base']).to eq(secrets.secret_key_base)
|
|
|
|
expect(new_secrets['otp_key_base']).to eq(secrets.otp_key_base)
|
|
|
|
expect(new_secrets['db_key_base']).to eq(secrets.db_key_base)
|
2017-09-19 11:20:49 -04:00
|
|
|
expect(new_secrets['openid_connect_signing_key']).to eq(secrets.openid_connect_signing_key)
|
2020-11-30 16:09:16 -05:00
|
|
|
expect(new_secrets['encrypted_settings_key_base']).to eq(secrets.encrypted_settings_key_base)
|
2016-07-15 08:19:29 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
|
|
|
|
2016-07-17 06:01:38 -04:00
|
|
|
it 'does not write a .secret file' do
|
|
|
|
expect(File).not_to receive(:write).with('.secret')
|
2016-07-15 08:19:29 -04:00
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the other secrets all exist' do
|
|
|
|
before do
|
|
|
|
secrets.db_key_base = 'db_key_base'
|
2017-09-19 11:20:49 -04:00
|
|
|
secrets.openid_connect_signing_key = 'openid_connect_signing_key'
|
2020-11-30 16:09:16 -05:00
|
|
|
secrets.encrypted_settings_key_base = 'encrypted_settings_key_base'
|
2016-07-15 08:19:29 -04:00
|
|
|
|
|
|
|
allow(File).to receive(:exist?).with('.secret').and_return(true)
|
2020-11-19 16:09:07 -05:00
|
|
|
stub_file_read('.secret', content: 'file_key')
|
2016-07-15 08:19:29 -04:00
|
|
|
end
|
|
|
|
|
2016-07-19 10:12:40 -04:00
|
|
|
context 'when secret_key_base exists in the environment and secrets.yml' do
|
|
|
|
before do
|
2017-01-09 12:19:48 -05:00
|
|
|
stub_env('SECRET_KEY_BASE', 'env_key')
|
2016-07-19 10:12:40 -04:00
|
|
|
secrets.secret_key_base = 'secret_key_base'
|
|
|
|
secrets.otp_key_base = 'otp_key_base'
|
2017-09-19 11:20:49 -04:00
|
|
|
secrets.openid_connect_signing_key = 'openid_connect_signing_key'
|
2016-07-19 10:12:40 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not issue a warning' do
|
|
|
|
expect(self).not_to receive(:warn)
|
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'uses the environment variable' do
|
|
|
|
create_tokens
|
|
|
|
|
|
|
|
expect(secrets.secret_key_base).to eq('env_key')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not update secrets.yml' do
|
|
|
|
expect(File).not_to receive(:write)
|
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-07-17 06:01:38 -04:00
|
|
|
context 'when secret_key_base and otp_key_base exist' do
|
|
|
|
before do
|
|
|
|
secrets.secret_key_base = 'secret_key_base'
|
|
|
|
secrets.otp_key_base = 'otp_key_base'
|
2017-09-19 11:20:49 -04:00
|
|
|
secrets.openid_connect_signing_key = 'openid_connect_signing_key'
|
2016-07-17 06:01:38 -04:00
|
|
|
end
|
2016-07-15 08:19:29 -04:00
|
|
|
|
|
|
|
it 'does not write any files' do
|
|
|
|
expect(File).not_to receive(:write)
|
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
|
|
|
|
2018-11-19 09:08:23 -05:00
|
|
|
it 'sets the keys to the values from the environment and secrets.yml' do
|
2016-07-15 08:19:29 -04:00
|
|
|
create_tokens
|
|
|
|
|
2016-07-17 06:01:38 -04:00
|
|
|
expect(secrets.secret_key_base).to eq('secret_key_base')
|
2016-07-15 08:19:29 -04:00
|
|
|
expect(secrets.otp_key_base).to eq('otp_key_base')
|
|
|
|
expect(secrets.db_key_base).to eq('db_key_base')
|
2017-09-19 11:20:49 -04:00
|
|
|
expect(secrets.openid_connect_signing_key).to eq('openid_connect_signing_key')
|
2020-11-30 16:09:16 -05:00
|
|
|
expect(secrets.encrypted_settings_key_base).to eq('encrypted_settings_key_base')
|
2016-07-15 08:19:29 -04:00
|
|
|
end
|
2016-07-17 06:01:38 -04:00
|
|
|
|
|
|
|
it 'deletes the .secret file' do
|
|
|
|
expect(File).to receive(:delete).with('.secret')
|
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
2016-07-15 08:19:29 -04:00
|
|
|
end
|
|
|
|
|
2016-07-17 06:01:38 -04:00
|
|
|
context 'when secret_key_base and otp_key_base do not exist' do
|
2016-07-15 08:19:29 -04:00
|
|
|
before do
|
|
|
|
allow(File).to receive(:exist?).with('config/secrets.yml').and_return(true)
|
|
|
|
allow(YAML).to receive(:load_file).with('config/secrets.yml').and_return('test' => secrets.to_h.stringify_keys)
|
|
|
|
allow(self).to receive(:warn_missing_secret)
|
|
|
|
end
|
|
|
|
|
2016-07-19 10:12:40 -04:00
|
|
|
it 'uses the file secret' do
|
2016-07-15 08:19:29 -04:00
|
|
|
expect(File).to receive(:write) do |filename, contents, options|
|
2021-04-23 23:09:38 -04:00
|
|
|
new_secrets = YAML.safe_load(contents)[Rails.env]
|
2016-07-15 08:19:29 -04:00
|
|
|
|
2016-07-19 10:12:40 -04:00
|
|
|
expect(new_secrets['secret_key_base']).to eq('file_key')
|
|
|
|
expect(new_secrets['otp_key_base']).to eq('file_key')
|
2016-07-17 06:01:38 -04:00
|
|
|
expect(new_secrets['db_key_base']).to eq('db_key_base')
|
2017-09-19 11:20:49 -04:00
|
|
|
expect(new_secrets['openid_connect_signing_key']).to eq('openid_connect_signing_key')
|
2016-07-15 08:19:29 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
create_tokens
|
|
|
|
|
2016-07-19 10:12:40 -04:00
|
|
|
expect(secrets.otp_key_base).to eq('file_key')
|
2016-07-15 08:19:29 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'keeps the other secrets as they were' do
|
|
|
|
create_tokens
|
|
|
|
|
|
|
|
expect(secrets.db_key_base).to eq('db_key_base')
|
|
|
|
end
|
|
|
|
|
2016-07-17 06:01:38 -04:00
|
|
|
it 'warns about the missing secrets' do
|
|
|
|
expect(self).to receive(:warn_missing_secret).with('secret_key_base')
|
2016-07-15 08:19:29 -04:00
|
|
|
expect(self).to receive(:warn_missing_secret).with('otp_key_base')
|
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
2016-07-17 06:01:38 -04:00
|
|
|
|
|
|
|
it 'deletes the .secret file' do
|
|
|
|
expect(File).to receive(:delete).with('.secret')
|
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
2016-07-15 08:19:29 -04:00
|
|
|
end
|
2020-11-30 16:09:16 -05:00
|
|
|
|
|
|
|
context 'when rotated_encrypted_settings_key_base does not exist' do
|
|
|
|
before do
|
|
|
|
secrets.secret_key_base = 'secret_key_base'
|
|
|
|
secrets.otp_key_base = 'otp_key_base'
|
|
|
|
secrets.openid_connect_signing_key = 'openid_connect_signing_key'
|
|
|
|
secrets.encrypted_settings_key_base = 'encrypted_settings_key_base'
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not warn about the missing secrets' do
|
|
|
|
expect(self).not_to receive(:warn_missing_secret).with('rotated_encrypted_settings_key_base')
|
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not update secrets.yml' do
|
|
|
|
expect(File).not_to receive(:write)
|
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
|
|
|
end
|
2016-07-15 08:19:29 -04:00
|
|
|
end
|
2016-07-19 10:12:40 -04:00
|
|
|
|
|
|
|
context 'when db_key_base is blank but exists in secrets.yml' do
|
|
|
|
before do
|
|
|
|
secrets.otp_key_base = 'otp_key_base'
|
|
|
|
secrets.secret_key_base = 'secret_key_base'
|
2020-11-30 16:09:16 -05:00
|
|
|
secrets.encrypted_settings_key_base = 'encrypted_settings_key_base'
|
2016-07-19 10:12:40 -04:00
|
|
|
yaml_secrets = secrets.to_h.stringify_keys.merge('db_key_base' => '<%= an_erb_expression %>')
|
|
|
|
|
|
|
|
allow(File).to receive(:exist?).with('.secret').and_return(false)
|
|
|
|
allow(File).to receive(:exist?).with('config/secrets.yml').and_return(true)
|
|
|
|
allow(YAML).to receive(:load_file).with('config/secrets.yml').and_return('test' => yaml_secrets)
|
|
|
|
allow(self).to receive(:warn_missing_secret)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'warns about updating db_key_base' do
|
|
|
|
expect(self).to receive(:warn_missing_secret).with('db_key_base')
|
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'warns about the blank value existing in secrets.yml and exits' do
|
|
|
|
expect(self).to receive(:warn) do |warning|
|
|
|
|
expect(warning).to include('db_key_base')
|
|
|
|
expect(warning).to include('<%= an_erb_expression %>')
|
|
|
|
end
|
|
|
|
|
|
|
|
create_tokens
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not update secrets.yml' do
|
|
|
|
expect(self).to receive(:exit).with(1).and_call_original
|
|
|
|
expect(File).not_to receive(:write)
|
|
|
|
|
|
|
|
expect { create_tokens }.to raise_error(SystemExit)
|
|
|
|
end
|
|
|
|
end
|
2016-07-15 08:19:29 -04:00
|
|
|
end
|
|
|
|
end
|