Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-01-06 09:10:35 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-01-06 09:10:35 +0300
commit25ceb3dc1c387950d777b71aabde00849d4c7bf9 (patch)
tree755188dc8d772ad10fb52f6eaa75a499ee15325a /spec
parent0fbe2f816ecef98003377154b479d350f13597d7 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/config/mail_room_spec.rb3
-rw-r--r--spec/lib/gitlab/jwt_authenticatable_spec.rb163
-rw-r--r--spec/lib/gitlab/mail_room/authenticator_spec.rb188
-rw-r--r--spec/lib/gitlab/mail_room/mail_room_spec.rb63
-rw-r--r--spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb22
-rw-r--r--spec/requests/api/internal/mail_room_spec.rb185
6 files changed, 580 insertions, 44 deletions
diff --git a/spec/config/mail_room_spec.rb b/spec/config/mail_room_spec.rb
index 55f8fdd78ba..ec306837361 100644
--- a/spec/config/mail_room_spec.rb
+++ b/spec/config/mail_room_spec.rb
@@ -18,8 +18,9 @@ RSpec.describe 'mail_room.yml' do
result = Gitlab::Popen.popen_with_detail(%W(ruby -rerb -e #{cmd}), absolute_path('config'), vars)
output = result.stdout
+ errors = result.stderr
status = result.status
- raise "Error interpreting #{mailroom_config_path}: #{output}" unless status == 0
+ raise "Error interpreting #{mailroom_config_path}: #{output}\n#{errors}" unless status == 0
YAML.safe_load(output, permitted_classes: [Symbol])
end
diff --git a/spec/lib/gitlab/jwt_authenticatable_spec.rb b/spec/lib/gitlab/jwt_authenticatable_spec.rb
index 36bb46cb250..92d5feceb75 100644
--- a/spec/lib/gitlab/jwt_authenticatable_spec.rb
+++ b/spec/lib/gitlab/jwt_authenticatable_spec.rb
@@ -14,17 +14,12 @@ RSpec.describe Gitlab::JwtAuthenticatable do
end
before do
- begin
- File.delete(test_class.secret_path)
- rescue Errno::ENOENT
- end
+ FileUtils.rm_f(test_class.secret_path)
test_class.write_secret
end
- describe '.secret' do
- subject(:secret) { test_class.secret }
-
+ shared_examples 'reading secret from the secret path' do
it 'returns 32 bytes' do
expect(secret).to be_a(String)
expect(secret.length).to eq(32)
@@ -32,62 +27,170 @@ RSpec.describe Gitlab::JwtAuthenticatable do
end
it 'accepts a trailing newline' do
- File.open(test_class.secret_path, 'a') { |f| f.write "\n" }
+ File.open(secret_path, 'a') { |f| f.write "\n" }
expect(secret.length).to eq(32)
end
it 'raises an exception if the secret file cannot be read' do
- File.delete(test_class.secret_path)
+ File.delete(secret_path)
expect { secret }.to raise_exception(Errno::ENOENT)
end
it 'raises an exception if the secret file contains the wrong number of bytes' do
- File.truncate(test_class.secret_path, 0)
+ File.truncate(secret_path, 0)
expect { secret }.to raise_exception(RuntimeError)
end
end
+ describe '.secret' do
+ it_behaves_like 'reading secret from the secret path' do
+ subject(:secret) { test_class.secret }
+
+ let(:secret_path) { test_class.secret_path }
+ end
+ end
+
+ describe '.read_secret' do
+ it_behaves_like 'reading secret from the secret path' do
+ subject(:secret) { test_class.read_secret(secret_path) }
+
+ let(:secret_path) { test_class.secret_path }
+ end
+ end
+
describe '.write_secret' do
- it 'uses mode 0600' do
- expect(File.stat(test_class.secret_path).mode & 0777).to eq(0600)
+ context 'without an input' do
+ it 'uses mode 0600' do
+ expect(File.stat(test_class.secret_path).mode & 0777).to eq(0600)
+ end
+
+ it 'writes base64 data' do
+ bytes = Base64.strict_decode64(File.read(test_class.secret_path))
+
+ expect(bytes).not_to be_empty
+ end
end
- it 'writes base64 data' do
- bytes = Base64.strict_decode64(File.read(test_class.secret_path))
+ context 'with an input' do
+ let(:another_path) do
+ Rails.root.join('tmp', 'tests', '.jwt_another_shared_secret')
+ end
- expect(bytes).not_to be_empty
+ after do
+ File.delete(another_path)
+ rescue Errno::ENOENT
+ end
+
+ it 'uses mode 0600' do
+ test_class.write_secret(another_path)
+ expect(File.stat(another_path).mode & 0777).to eq(0600)
+ end
+
+ it 'writes base64 data' do
+ test_class.write_secret(another_path)
+ bytes = Base64.strict_decode64(File.read(another_path))
+
+ expect(bytes).not_to be_empty
+ end
end
end
- describe '.decode_jwt_for_issuer' do
- let(:payload) { { 'iss' => 'test_issuer' } }
+ describe '.decode_jwt' do |decode|
+ let(:payload) { {} }
+
+ context 'use included class secret' do
+ it 'accepts a correct header' do
+ encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
+
+ expect { test_class.decode_jwt(encoded_message) }.not_to raise_error
+ end
+
+ it 'raises an error when the JWT is not signed' do
+ encoded_message = JWT.encode(payload, nil, 'none')
+
+ expect { test_class.decode_jwt(encoded_message) }.to raise_error(JWT::DecodeError)
+ end
- it 'accepts a correct header' do
- encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
+ it 'raises an error when the header is signed with the wrong secret' do
+ encoded_message = JWT.encode(payload, 'wrongsecret', 'HS256')
- expect { test_class.decode_jwt_for_issuer('test_issuer', encoded_message) }.not_to raise_error
+ expect { test_class.decode_jwt(encoded_message) }.to raise_error(JWT::DecodeError)
+ end
end
- it 'raises an error when the JWT is not signed' do
- encoded_message = JWT.encode(payload, nil, 'none')
+ context 'use an input secret' do
+ let(:another_secret) { 'another secret' }
+
+ it 'accepts a correct header' do
+ encoded_message = JWT.encode(payload, another_secret, 'HS256')
+
+ expect { test_class.decode_jwt(encoded_message, another_secret) }.not_to raise_error
+ end
- expect { test_class.decode_jwt_for_issuer('test_issuer', encoded_message) }.to raise_error(JWT::DecodeError)
+ it 'raises an error when the JWT is not signed' do
+ encoded_message = JWT.encode(payload, nil, 'none')
+
+ expect { test_class.decode_jwt(encoded_message, another_secret) }.to raise_error(JWT::DecodeError)
+ end
+
+ it 'raises an error when the header is signed with the wrong secret' do
+ encoded_message = JWT.encode(payload, 'wrongsecret', 'HS256')
+
+ expect { test_class.decode_jwt(encoded_message, another_secret) }.to raise_error(JWT::DecodeError)
+ end
end
- it 'raises an error when the header is signed with the wrong secret' do
- encoded_message = JWT.encode(payload, 'wrongsecret', 'HS256')
+ context 'issuer option' do
+ let(:payload) { { 'iss' => 'test_issuer' } }
+
+ it 'returns decoded payload if issuer is correct' do
+ encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
+ payload = test_class.decode_jwt(encoded_message, issuer: 'test_issuer')
- expect { test_class.decode_jwt_for_issuer('test_issuer', encoded_message) }.to raise_error(JWT::DecodeError)
+ expect(payload[0]).to match a_hash_including('iss' => 'test_issuer')
+ end
+
+ it 'raises an error when the issuer is incorrect' do
+ payload['iss'] = 'somebody else'
+ encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
+
+ expect { test_class.decode_jwt(encoded_message, issuer: 'test_issuer') }.to raise_error(JWT::DecodeError)
+ end
end
- it 'raises an error when the issuer is incorrect' do
- payload['iss'] = 'somebody else'
- encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
+ context 'iat_after option' do
+ it 'returns decoded payload if iat is valid' do
+ freeze_time do
+ encoded_message = JWT.encode(payload.merge(iat: (Time.current - 10.seconds).to_i), test_class.secret, 'HS256')
+ payload = test_class.decode_jwt(encoded_message, iat_after: Time.current - 20.seconds)
+
+ expect(payload[0]).to match a_hash_including('iat' => be_a(Integer))
+ end
+ end
+
+ it 'raises an error if iat is invalid' do
+ encoded_message = JWT.encode(payload.merge(iat: 'wrong'), test_class.secret, 'HS256')
- expect { test_class.decode_jwt_for_issuer('test_issuer', encoded_message) }.to raise_error(JWT::DecodeError)
+ expect { test_class.decode_jwt(encoded_message, iat_after: true) }.to raise_error(JWT::DecodeError)
+ end
+
+ it 'raises an error if iat is absent' do
+ encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
+
+ expect { test_class.decode_jwt(encoded_message, iat_after: true) }.to raise_error(JWT::DecodeError)
+ end
+
+ it 'raises an error if iat is too far in the past' do
+ freeze_time do
+ encoded_message = JWT.encode(payload.merge(iat: (Time.current - 30.seconds).to_i), test_class.secret, 'HS256')
+ expect do
+ test_class.decode_jwt(encoded_message, iat_after: Time.current - 20.seconds)
+ end.to raise_error(JWT::ExpiredSignature, 'Token has expired')
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/mail_room/authenticator_spec.rb b/spec/lib/gitlab/mail_room/authenticator_spec.rb
new file mode 100644
index 00000000000..44120902661
--- /dev/null
+++ b/spec/lib/gitlab/mail_room/authenticator_spec.rb
@@ -0,0 +1,188 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::MailRoom::Authenticator do
+ let(:yml_config) do
+ {
+ enabled: true,
+ address: 'address@example.com'
+ }
+ end
+
+ let(:incoming_email_secret_path) { '/path/to/incoming_email_secret' }
+ let(:incoming_email_config) { yml_config.merge(secret_file: incoming_email_secret_path) }
+
+ let(:service_desk_email_secret_path) { '/path/to/service_desk_email_secret' }
+ let(:service_desk_email_config) { yml_config.merge(secret_file: service_desk_email_secret_path) }
+
+ let(:configs) do
+ {
+ incoming_email: incoming_email_config,
+ service_desk_email: service_desk_email_config
+ }
+ end
+
+ before do
+ allow(Gitlab::MailRoom).to receive(:enabled_configs).and_return(configs)
+
+ described_class.clear_memoization(:jwt_secret_incoming_email)
+ described_class.clear_memoization(:jwt_secret_service_desk_email)
+ end
+
+ after do
+ described_class.clear_memoization(:jwt_secret_incoming_email)
+ described_class.clear_memoization(:jwt_secret_service_desk_email)
+ end
+
+ around do |example|
+ freeze_time do
+ example.run
+ end
+ end
+
+ describe '#verify_api_request' do
+ let(:incoming_email_secret) { SecureRandom.hex(16) }
+ let(:service_desk_email_secret) { SecureRandom.hex(16) }
+ let(:payload) { { iss: described_class::INTERNAL_API_REQUEST_JWT_ISSUER, iat: (Time.current - 5.minutes + 1.second).to_i } }
+
+ before do
+ allow(described_class).to receive(:secret).with(:incoming_email).and_return(incoming_email_secret)
+ allow(described_class).to receive(:secret).with(:service_desk_email).and_return(service_desk_email_secret)
+ end
+
+ context 'verify a valid token' do
+ it 'returns the decoded payload' do
+ encoded_token = JWT.encode(payload, incoming_email_secret, 'HS256')
+ headers = { described_class::INTERNAL_API_REQUEST_HEADER => encoded_token }
+
+ expect(described_class.verify_api_request(headers, 'incoming_email')[0]).to match a_hash_including(
+ "iss" => "gitlab-mailroom",
+ "iat" => be_a(Integer)
+ )
+
+ encoded_token = JWT.encode(payload, service_desk_email_secret, 'HS256')
+ headers = { described_class::INTERNAL_API_REQUEST_HEADER => encoded_token }
+
+ expect(described_class.verify_api_request(headers, 'service_desk_email')[0]).to match a_hash_including(
+ "iss" => "gitlab-mailroom",
+ "iat" => be_a(Integer)
+ )
+ end
+ end
+
+ context 'verify an invalid token' do
+ it 'returns false' do
+ encoded_token = JWT.encode(payload, 'wrong secret', 'HS256')
+ headers = { described_class::INTERNAL_API_REQUEST_HEADER => encoded_token }
+
+ expect(described_class.verify_api_request(headers, 'incoming_email')).to eq(false)
+ end
+ end
+
+ context 'verify a valid token but wrong mailbox type' do
+ it 'returns false' do
+ encoded_token = JWT.encode(payload, incoming_email_secret, 'HS256')
+ headers = { described_class::INTERNAL_API_REQUEST_HEADER => encoded_token }
+
+ expect(described_class.verify_api_request(headers, 'service_desk_email')).to eq(false)
+ end
+ end
+
+ context 'verify a valid token but wrong issuer' do
+ let(:payload) { { iss: 'invalid_issuer' } }
+
+ it 'returns false' do
+ encoded_token = JWT.encode(payload, incoming_email_secret, 'HS256')
+ headers = { described_class::INTERNAL_API_REQUEST_HEADER => encoded_token }
+
+ expect(described_class.verify_api_request(headers, 'incoming_email')).to eq(false)
+ end
+ end
+
+ context 'verify a valid token but expired' do
+ let(:payload) { { iss: described_class::INTERNAL_API_REQUEST_JWT_ISSUER, iat: (Time.current - 5.minutes - 1.second).to_i } }
+
+ it 'returns false' do
+ encoded_token = JWT.encode(payload, incoming_email_secret, 'HS256')
+ headers = { described_class::INTERNAL_API_REQUEST_HEADER => encoded_token }
+
+ expect(described_class.verify_api_request(headers, 'incoming_email')).to eq(false)
+ end
+ end
+
+ context 'verify a valid token but wrong header field' do
+ it 'returns false' do
+ encoded_token = JWT.encode(payload, incoming_email_secret, 'HS256')
+ headers = { 'a-wrong-header' => encoded_token }
+
+ expect(described_class.verify_api_request(headers, 'incoming_email')).to eq(false)
+ end
+ end
+
+ context 'verify headers for a disabled mailbox type' do
+ let(:configs) { { service_desk_email: service_desk_email_config } }
+
+ it 'returns false' do
+ encoded_token = JWT.encode(payload, incoming_email_secret, 'HS256')
+ headers = { described_class::INTERNAL_API_REQUEST_HEADER => encoded_token }
+
+ expect(described_class.verify_api_request(headers, 'incoming_email')).to eq(false)
+ end
+ end
+
+ context 'verify headers for a non-existing mailbox type' do
+ it 'returns false' do
+ headers = { described_class::INTERNAL_API_REQUEST_HEADER => 'something' }
+
+ expect(described_class.verify_api_request(headers, 'invalid_mailbox_type')).to eq(false)
+ end
+ end
+ end
+
+ describe '#secret' do
+ let(:incoming_email_secret) { SecureRandom.hex(16) }
+ let(:service_desk_email_secret) { SecureRandom.hex(16) }
+
+ context 'the secret is valid' do
+ before do
+ allow(described_class).to receive(:read_secret).with(incoming_email_secret_path).and_return(incoming_email_secret).once
+ allow(described_class).to receive(:read_secret).with(service_desk_email_secret_path).and_return(service_desk_email_secret).once
+ end
+
+ it 'returns the memorized secret from a file' do
+ expect(described_class.secret(:incoming_email)).to eql(incoming_email_secret)
+ # The second call does not trigger secret read again
+ expect(described_class.secret(:incoming_email)).to eql(incoming_email_secret)
+ expect(described_class).to have_received(:read_secret).with(incoming_email_secret_path).once
+
+ expect(described_class.secret(:service_desk_email)).to eql(service_desk_email_secret)
+ # The second call does not trigger secret read again
+ expect(described_class.secret(:service_desk_email)).to eql(service_desk_email_secret)
+ expect(described_class).to have_received(:read_secret).with(service_desk_email_secret_path).once
+ end
+ end
+
+ context 'the secret file is not configured' do
+ let(:incoming_email_config) { yml_config }
+
+ it 'raises a SecretConfigurationError exception' do
+ expect do
+ described_class.secret(:incoming_email)
+ end.to raise_error(described_class::SecretConfigurationError, "incoming_email's secret_file configuration is missing")
+ end
+ end
+
+ context 'the secret file not found' do
+ before do
+ allow(described_class).to receive(:read_secret).with(incoming_email_secret_path).and_raise(Errno::ENOENT)
+ end
+
+ it 'raises a SecretConfigurationError exception' do
+ expect do
+ described_class.secret(:incoming_email)
+ end.to raise_error(described_class::SecretConfigurationError, "Fail to read incoming_email's secret: No such file or directory")
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/mail_room/mail_room_spec.rb b/spec/lib/gitlab/mail_room/mail_room_spec.rb
index 0bd1a27c65e..a4fcf71a012 100644
--- a/spec/lib/gitlab/mail_room/mail_room_spec.rb
+++ b/spec/lib/gitlab/mail_room/mail_room_spec.rb
@@ -30,6 +30,7 @@ RSpec.describe Gitlab::MailRoom do
end
before do
+ allow(described_class).to receive(:load_yaml).and_return(configs)
described_class.instance_variable_set(:@enabled_configs, nil)
end
@@ -38,10 +39,6 @@ RSpec.describe Gitlab::MailRoom do
end
describe '#enabled_configs' do
- before do
- allow(described_class).to receive(:load_yaml).and_return(configs)
- end
-
context 'when both email and address is set' do
it 'returns email configs' do
expect(described_class.enabled_configs.size).to eq(2)
@@ -79,7 +76,7 @@ RSpec.describe Gitlab::MailRoom do
let(:custom_config) { { enabled: true, address: 'address@example.com' } }
it 'overwrites missing values with the default' do
- expect(described_class.enabled_configs.first[:port]).to eq(Gitlab::MailRoom::DEFAULT_CONFIG[:port])
+ expect(described_class.enabled_configs.each_value.first[:port]).to eq(Gitlab::MailRoom::DEFAULT_CONFIG[:port])
end
end
@@ -88,7 +85,7 @@ RSpec.describe Gitlab::MailRoom do
it 'returns only encoming_email' do
expect(described_class.enabled_configs.size).to eq(1)
- expect(described_class.enabled_configs.first[:worker]).to eq('EmailReceiverWorker')
+ expect(described_class.enabled_configs.each_value.first[:worker]).to eq('EmailReceiverWorker')
end
end
@@ -100,11 +97,12 @@ RSpec.describe Gitlab::MailRoom do
end
it 'sets redis config' do
- config = described_class.enabled_configs.first
-
- expect(config[:redis_url]).to eq('localhost')
- expect(config[:redis_db]).to eq(99)
- expect(config[:sentinels]).to eq('yes, them')
+ config = described_class.enabled_configs.each_value.first
+ expect(config).to include(
+ redis_url: 'localhost',
+ redis_db: 99,
+ sentinels: 'yes, them'
+ )
end
end
@@ -113,7 +111,7 @@ RSpec.describe Gitlab::MailRoom do
let(:custom_config) { { log_path: 'tiny_log.log' } }
it 'expands the log path to an absolute value' do
- new_path = Pathname.new(described_class.enabled_configs.first[:log_path])
+ new_path = Pathname.new(described_class.enabled_configs.each_value.first[:log_path])
expect(new_path.absolute?).to be_truthy
end
end
@@ -122,9 +120,48 @@ RSpec.describe Gitlab::MailRoom do
let(:custom_config) { { log_path: '/dev/null' } }
it 'leaves the path as-is' do
- expect(described_class.enabled_configs.first[:log_path]).to eq '/dev/null'
+ expect(described_class.enabled_configs.each_value.first[:log_path]).to eq '/dev/null'
end
end
end
end
+
+ describe '#enabled_mailbox_types' do
+ context 'when all mailbox types are enabled' do
+ it 'returns the mailbox types' do
+ expect(described_class.enabled_mailbox_types).to match(%w[incoming_email service_desk_email])
+ end
+ end
+
+ context 'when an mailbox_types is disabled' do
+ let(:incoming_email_config) { yml_config.merge(enabled: false) }
+
+ it 'returns the mailbox types' do
+ expect(described_class.enabled_mailbox_types).to match(%w[service_desk_email])
+ end
+ end
+
+ context 'when email is disabled' do
+ let(:custom_config) { { enabled: false } }
+
+ it 'returns an empty array' do
+ expect(described_class.enabled_mailbox_types).to match_array([])
+ end
+ end
+ end
+
+ describe '#worker_for' do
+ context 'matched mailbox types' do
+ it 'returns the constantized worker class' do
+ expect(described_class.worker_for('incoming_email')).to eql(EmailReceiverWorker)
+ expect(described_class.worker_for('service_desk_email')).to eql(ServiceDeskEmailReceiverWorker)
+ end
+ end
+
+ context 'non-existing mailbox_type' do
+ it 'returns nil' do
+ expect(described_class.worker_for('another_mailbox_type')).to be(nil)
+ end
+ end
+ end
end
diff --git a/spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb b/spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb
index bc8b7c56676..bf4094eaa49 100644
--- a/spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb
+++ b/spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb
@@ -53,4 +53,26 @@ RSpec.describe EncryptStaticObjectsExternalStorageAuthToken, :migration do
end
end
end
+
+ context 'when static_objects_external_storage_auth_token is empty string' do
+ it 'does not break' do
+ settings = application_settings.create!
+ settings.update_column(:static_objects_external_storage_auth_token, '')
+
+ reversible_migration do |migration|
+ migration.before -> {
+ settings = application_settings.first
+
+ expect(settings.static_objects_external_storage_auth_token).to eq('')
+ expect(settings.static_objects_external_storage_auth_token_encrypted).to be_nil
+ }
+ migration.after -> {
+ settings = application_settings.first
+
+ expect(settings.static_objects_external_storage_auth_token).to eq('')
+ expect(settings.static_objects_external_storage_auth_token_encrypted).to be_nil
+ }
+ end
+ end
+ end
end
diff --git a/spec/requests/api/internal/mail_room_spec.rb b/spec/requests/api/internal/mail_room_spec.rb
new file mode 100644
index 00000000000..51abe92a125
--- /dev/null
+++ b/spec/requests/api/internal/mail_room_spec.rb
@@ -0,0 +1,185 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::Internal::MailRoom do
+ let(:base_configs) do
+ {
+ enabled: true,
+ address: 'address@example.com',
+ port: 143,
+ ssl: false,
+ start_tls: false,
+ mailbox: 'inbox',
+ idle_timeout: 60,
+ log_path: Rails.root.join('log', 'mail_room_json.log').to_s,
+ expunge_deleted: false
+ }
+ end
+
+ let(:enabled_configs) do
+ {
+ incoming_email: base_configs.merge(
+ secure_file: Rails.root.join('tmp', 'tests', '.incoming_email_secret').to_s
+ ),
+ service_desk_email: base_configs.merge(
+ secure_file: Rails.root.join('tmp', 'tests', '.service_desk_email').to_s
+ )
+ }
+ end
+
+ let(:auth_payload) { { 'iss' => Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_JWT_ISSUER, 'iat' => (Time.now - 10.seconds).to_i } }
+
+ let(:incoming_email_secret) { 'incoming_email_secret' }
+ let(:service_desk_email_secret) { 'service_desk_email_secret' }
+
+ let(:email_content) { fixture_file("emails/commands_in_reply.eml") }
+
+ before do
+ allow(Gitlab::MailRoom::Authenticator).to receive(:secret).with(:incoming_email).and_return(incoming_email_secret)
+ allow(Gitlab::MailRoom::Authenticator).to receive(:secret).with(:service_desk_email).and_return(service_desk_email_secret)
+ allow(Gitlab::MailRoom).to receive(:enabled_configs).and_return(enabled_configs)
+ end
+
+ around do |example|
+ freeze_time do
+ example.run
+ end
+ end
+
+ describe "POST /internal/mail_room/*mailbox_type" do
+ context 'handle incoming_email successfully' do
+ let(:auth_headers) do
+ jwt_token = JWT.encode(auth_payload, incoming_email_secret, 'HS256')
+ { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ end
+
+ it 'schedules a EmailReceiverWorker job with raw email content' do
+ Sidekiq::Testing.fake! do
+ expect do
+ post api("/internal/mail_room/incoming_email"), headers: auth_headers, params: email_content
+ end.to change { EmailReceiverWorker.jobs.size }.by(1)
+ end
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ job = EmailReceiverWorker.jobs.last
+ expect(job).to match a_hash_including('args' => [email_content])
+ end
+ end
+
+ context 'handle service_desk_email successfully' do
+ let(:auth_headers) do
+ jwt_token = JWT.encode(auth_payload, service_desk_email_secret, 'HS256')
+ { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ end
+
+ it 'schedules a ServiceDeskEmailReceiverWorker job with raw email content' do
+ Sidekiq::Testing.fake! do
+ expect do
+ post api("/internal/mail_room/service_desk_email"), headers: auth_headers, params: email_content
+ end.to change { ServiceDeskEmailReceiverWorker.jobs.size }.by(1)
+ end
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ job = ServiceDeskEmailReceiverWorker.jobs.last
+ expect(job).to match a_hash_including('args' => [email_content])
+ end
+ end
+
+ context 'email content exceeds limit' do
+ let(:auth_headers) do
+ jwt_token = JWT.encode(auth_payload, incoming_email_secret, 'HS256')
+ { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ end
+
+ before do
+ allow(EmailReceiverWorker).to receive(:perform_async).and_raise(
+ Gitlab::SidekiqMiddleware::SizeLimiter::ExceedLimitError.new(EmailReceiverWorker, email_content.bytesize, email_content.bytesize - 1)
+ )
+ end
+
+ it 'responds with 400 bad request' do
+ Sidekiq::Testing.fake! do
+ expect do
+ post api("/internal/mail_room/incoming_email"), headers: auth_headers, params: email_content
+ end.not_to change { EmailReceiverWorker.jobs.size }
+ end
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(Gitlab::Json.parse(response.body)).to match a_hash_including(
+ { "success" => false, "message" => "EmailReceiverWorker job exceeds payload size limit" }
+ )
+ end
+ end
+
+ context 'not authenticated' do
+ it 'responds with 401 Unauthorized' do
+ post api("/internal/mail_room/incoming_email")
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ context 'wrong token authentication' do
+ let(:auth_headers) do
+ jwt_token = JWT.encode(auth_payload, 'wrongsecret', 'HS256')
+ { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ end
+
+ it 'responds with 401 Unauthorized' do
+ post api("/internal/mail_room/incoming_email"), headers: auth_headers
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ context 'wrong mailbox type authentication' do
+ let(:auth_headers) do
+ jwt_token = JWT.encode(auth_payload, service_desk_email_secret, 'HS256')
+ { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ end
+
+ it 'responds with 401 Unauthorized' do
+ post api("/internal/mail_room/incoming_email"), headers: auth_headers
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ context 'not supported mailbox type' do
+ let(:auth_headers) do
+ jwt_token = JWT.encode(auth_payload, incoming_email_secret, 'HS256')
+ { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ end
+
+ it 'responds with 401 Unauthorized' do
+ post api("/internal/mail_room/invalid_mailbox_type"), headers: auth_headers
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ context 'not enabled mailbox type' do
+ let(:enabled_configs) do
+ {
+ incoming_email: base_configs.merge(
+ secure_file: Rails.root.join('tmp', 'tests', '.incoming_email_secret').to_s
+ )
+ }
+ end
+
+ let(:auth_headers) do
+ jwt_token = JWT.encode(auth_payload, service_desk_email_secret, 'HS256')
+ { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ end
+
+ it 'responds with 401 Unauthorized' do
+ post api("/internal/mail_room/service_desk_email"), headers: auth_headers
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+ end
+end