Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0fbe2f816e
commit
25ceb3dc1c
22 changed files with 773 additions and 72 deletions
|
@ -34,7 +34,7 @@
|
|||
@media (min-width: map-get($grid-breakpoints, md)) {
|
||||
// The `+11` is to ensure the file header border shows when scrolled -
|
||||
// the bottom of the compare-versions header and the top of the file header
|
||||
$mr-file-header-top: calc(#{$mr-version-controls-height} + #{$header-height} + #{$mr-tabs-height} + 11);
|
||||
$mr-file-header-top: calc(#{$mr-version-controls-height} + #{$header-height} + #{$mr-tabs-height} + 11px);
|
||||
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
|
|
|
@ -228,6 +228,10 @@ production: &base
|
|||
# client_id: "YOUR-CLIENT-ID"
|
||||
# client_secret: "YOUR-CLIENT-SECRET"
|
||||
|
||||
# File that contains the shared secret key for verifying access for mailroom's incoming_email.
|
||||
# Default is '.gitlab_mailroom_secret' relative to Rails.root (i.e. root of the GitLab app).
|
||||
# secret_file: /home/git/gitlab/.gitlab_mailroom_secret
|
||||
|
||||
## Consolidated object store config
|
||||
## This will only take effect if the object_store sections are not defined
|
||||
## within the types (e.g. artifacts, lfs, etc.).
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
:mailboxes:
|
||||
<%
|
||||
require_relative "../lib/gitlab/mail_room" unless defined?(Gitlab::MailRoom)
|
||||
Gitlab::MailRoom.enabled_configs.each do |config|
|
||||
Gitlab::MailRoom.enabled_configs.each do |_key, config|
|
||||
%>
|
||||
-
|
||||
:host: <%= config[:host].to_json %>
|
||||
|
|
|
@ -13,6 +13,8 @@ class EncryptStaticObjectsExternalStorageAuthToken < Gitlab::Database::Migration
|
|||
ApplicationSetting.reset_column_information
|
||||
|
||||
ApplicationSetting.encrypted_token_is_null.plaintext_token_is_not_null.find_each do |application_setting|
|
||||
next if application_setting.static_objects_external_storage_auth_token.empty?
|
||||
|
||||
token_encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(application_setting.static_objects_external_storage_auth_token)
|
||||
application_setting.update!(static_objects_external_storage_auth_token_encrypted: token_encrypted)
|
||||
end
|
||||
|
|
|
@ -375,3 +375,38 @@ It supports the same parameters as the [Merge Requests API](merge_requests.md#li
|
|||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/deployments/42/merge_requests"
|
||||
```
|
||||
|
||||
## Approve or Reject a blocked Deployment
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/343864) in GitLab 14.7 [with a flag](../administration/feature_flags.md) named `deployment_approvals`. Disabled by default. This feature is not ready for production use.
|
||||
|
||||
```plaintext
|
||||
POST /projects/:id/deployments/:deployment_id/approval
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `deployment_id` | integer | yes | The ID of the deployment. |
|
||||
| `status` | string | yes | The status of the approval (either `approved` or `rejected`). |
|
||||
|
||||
```shell
|
||||
curl --data "status=approved" \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/deployments/1/approval"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"user": {
|
||||
"name": "Administrator",
|
||||
"username": "root",
|
||||
"id": 1,
|
||||
"state": "active",
|
||||
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
|
||||
"web_url": "http://localhost:3000/root"
|
||||
},
|
||||
"status": "approved"
|
||||
}
|
||||
```
|
||||
|
|
|
@ -164,10 +164,14 @@ Second command line.
|
|||
When you omit the `>` or `|` block scalar indicators, GitLab concatenates non-empty
|
||||
lines to form the command. Make sure the lines can run when concatenated.
|
||||
|
||||
<!-- vale gitlab.MeaningfulLinkWords = NO -->
|
||||
|
||||
[Shell here documents](https://en.wikipedia.org/wiki/Here_document) work with the
|
||||
`|` and `>` operators as well. The example below transliterates lower case letters
|
||||
to upper case:
|
||||
|
||||
<!-- vale gitlab.MeaningfulLinkWords = YES -->
|
||||
|
||||
```yaml
|
||||
job:
|
||||
script:
|
||||
|
|
|
@ -299,6 +299,7 @@ module API
|
|||
mount ::API::Internal::Lfs
|
||||
mount ::API::Internal::Pages
|
||||
mount ::API::Internal::Kubernetes
|
||||
mount ::API::Internal::MailRoom
|
||||
|
||||
version 'v3', using: :path do
|
||||
# Although the following endpoints are kept behind V3 namespace,
|
||||
|
|
|
@ -165,3 +165,5 @@ module API
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
API::Deployments.prepend_mod
|
||||
|
|
47
lib/api/internal/mail_room.rb
Normal file
47
lib/api/internal/mail_room.rb
Normal file
|
@ -0,0 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
# This internal endpoint receives webhooks sent from the MailRoom component.
|
||||
# This component constantly listens to configured email accounts. When it
|
||||
# finds any incoming email or service desk email, it makes a POST request to
|
||||
# this endpoint. The target mailbox type is indicated in the request path.
|
||||
# The email raw content is attached to the request body.
|
||||
#
|
||||
# For more information, please visit https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/644
|
||||
module Internal
|
||||
class MailRoom < ::API::Base
|
||||
feature_category :service_desk
|
||||
|
||||
before do
|
||||
authenticate_gitlab_mailroom_request!
|
||||
end
|
||||
|
||||
helpers do
|
||||
def authenticate_gitlab_mailroom_request!
|
||||
unauthorized! unless Gitlab::MailRoom::Authenticator.verify_api_request(headers, params[:mailbox_type])
|
||||
end
|
||||
end
|
||||
|
||||
namespace 'internal' do
|
||||
namespace 'mail_room' do
|
||||
params do
|
||||
requires :mailbox_type, type: String,
|
||||
desc: 'The destination mailbox type configuration. Must either be incoming_email or service_desk_email'
|
||||
end
|
||||
post "/*mailbox_type" do
|
||||
worker = Gitlab::MailRoom.worker_for(params[:mailbox_type])
|
||||
begin
|
||||
worker.perform_async(request.body.read)
|
||||
rescue Gitlab::SidekiqMiddleware::SizeLimiter::ExceedLimitError => e
|
||||
status 400
|
||||
break { success: false, message: e.message }
|
||||
end
|
||||
|
||||
status 200
|
||||
{ success: true }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,26 +13,38 @@ module Gitlab
|
|||
module ClassMethods
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
def decode_jwt_for_issuer(issuer, encoded_message)
|
||||
JWT.decode(
|
||||
encoded_message,
|
||||
secret,
|
||||
true,
|
||||
{ iss: issuer, verify_iss: true, algorithm: 'HS256' }
|
||||
)
|
||||
def decode_jwt(encoded_message, jwt_secret = secret, issuer: nil, iat_after: nil)
|
||||
options = { algorithm: 'HS256' }
|
||||
options = options.merge(iss: issuer, verify_iss: true) if issuer.present?
|
||||
options = options.merge(verify_iat: true) if iat_after.present?
|
||||
|
||||
decoded_message = JWT.decode(encoded_message, jwt_secret, true, options)
|
||||
payload = decoded_message[0]
|
||||
if iat_after.present?
|
||||
raise JWT::DecodeError, "JWT iat claim is missing" if payload['iat'].blank?
|
||||
|
||||
iat = payload['iat'].to_i
|
||||
raise JWT::ExpiredSignature, 'Token has expired' if iat < iat_after.to_i
|
||||
end
|
||||
|
||||
decoded_message
|
||||
end
|
||||
|
||||
def secret
|
||||
strong_memoize(:secret) do
|
||||
Base64.strict_decode64(File.read(secret_path).chomp).tap do |bytes|
|
||||
raise "#{secret_path} does not contain #{SECRET_LENGTH} bytes" if bytes.length != SECRET_LENGTH
|
||||
end
|
||||
read_secret(secret_path)
|
||||
end
|
||||
end
|
||||
|
||||
def write_secret
|
||||
def read_secret(path)
|
||||
Base64.strict_decode64(File.read(path).chomp).tap do |bytes|
|
||||
raise "#{path} does not contain #{SECRET_LENGTH} bytes" if bytes.length != SECRET_LENGTH
|
||||
end
|
||||
end
|
||||
|
||||
def write_secret(path = secret_path)
|
||||
bytes = SecureRandom.random_bytes(SECRET_LENGTH)
|
||||
File.open(secret_path, 'w:BINARY', 0600) do |f|
|
||||
File.open(path, 'w:BINARY', 0600) do |f|
|
||||
f.chmod(0600) # If the file already existed, the '0600' passed to 'open' above was a no-op.
|
||||
f.write(Base64.strict_encode64(bytes))
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ module Gitlab
|
|||
|
||||
class << self
|
||||
def verify_api_request(request_headers)
|
||||
decode_jwt_for_issuer(JWT_ISSUER, request_headers[INTERNAL_API_REQUEST_HEADER])
|
||||
decode_jwt(request_headers[INTERNAL_API_REQUEST_HEADER], issuer: JWT_ISSUER)
|
||||
rescue JWT::DecodeError
|
||||
nil
|
||||
end
|
||||
|
|
|
@ -25,7 +25,7 @@ module Gitlab
|
|||
|
||||
# Email specific configuration which is merged with configuration
|
||||
# fetched from YML config file.
|
||||
ADDRESS_SPECIFIC_CONFIG = {
|
||||
MAILBOX_SPECIFIC_CONFIGS = {
|
||||
incoming_email: {
|
||||
queue: 'email_receiver',
|
||||
worker: 'EmailReceiverWorker'
|
||||
|
@ -38,7 +38,15 @@ module Gitlab
|
|||
|
||||
class << self
|
||||
def enabled_configs
|
||||
@enabled_configs ||= configs.select { |config| enabled?(config) }
|
||||
@enabled_configs ||= configs.select { |_key, config| enabled?(config) }
|
||||
end
|
||||
|
||||
def enabled_mailbox_types
|
||||
enabled_configs.keys.map(&:to_s)
|
||||
end
|
||||
|
||||
def worker_for(mailbox_type)
|
||||
MAILBOX_SPECIFIC_CONFIGS.try(:[], mailbox_type.to_sym).try(:[], :worker).try(:safe_constantize)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -48,7 +56,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def configs
|
||||
ADDRESS_SPECIFIC_CONFIG.keys.map { |key| fetch_config(key) }
|
||||
MAILBOX_SPECIFIC_CONFIGS.to_h { |key, _value| [key, fetch_config(key)] }
|
||||
end
|
||||
|
||||
def fetch_config(config_key)
|
||||
|
@ -63,7 +71,7 @@ module Gitlab
|
|||
|
||||
def merged_configs(config_key)
|
||||
yml_config = load_yaml.fetch(config_key, {})
|
||||
specific_config = ADDRESS_SPECIFIC_CONFIG.fetch(config_key, {})
|
||||
specific_config = MAILBOX_SPECIFIC_CONFIGS.fetch(config_key, {})
|
||||
DEFAULT_CONFIG.merge(specific_config, yml_config) do |_key, oldval, newval|
|
||||
newval.nil? ? oldval : newval
|
||||
end
|
||||
|
|
50
lib/gitlab/mail_room/authenticator.rb
Normal file
50
lib/gitlab/mail_room/authenticator.rb
Normal file
|
@ -0,0 +1,50 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module MailRoom
|
||||
class Authenticator
|
||||
include JwtAuthenticatable
|
||||
|
||||
SecretConfigurationError = Class.new(StandardError)
|
||||
INTERNAL_API_REQUEST_HEADER = 'Gitlab-Mailroom-Api-Request'
|
||||
INTERNAL_API_REQUEST_JWT_ISSUER = 'gitlab-mailroom'
|
||||
|
||||
# Only allow token generated within the last 5 minutes
|
||||
EXPIRATION = 5.minutes
|
||||
|
||||
class << self
|
||||
def verify_api_request(request_headers, mailbox_type)
|
||||
mailbox_type = mailbox_type.to_sym
|
||||
return false if enabled_configs[mailbox_type].blank?
|
||||
|
||||
decode_jwt(
|
||||
request_headers[INTERNAL_API_REQUEST_HEADER],
|
||||
secret(mailbox_type),
|
||||
issuer: INTERNAL_API_REQUEST_JWT_ISSUER, iat_after: Time.current - EXPIRATION
|
||||
)
|
||||
rescue JWT::DecodeError => e
|
||||
::Gitlab::AppLogger.warn("Fail to decode MailRoom JWT token: #{e.message}") if Rails.env.development?
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def secret(mailbox_type)
|
||||
strong_memoize("jwt_secret_#{mailbox_type}".to_sym) do
|
||||
secret_path = enabled_configs[mailbox_type][:secret_file]
|
||||
raise SecretConfigurationError, "#{mailbox_type}'s secret_file configuration is missing" if secret_path.blank?
|
||||
|
||||
begin
|
||||
read_secret(secret_path)
|
||||
rescue StandardError => e
|
||||
raise SecretConfigurationError, "Fail to read #{mailbox_type}'s secret: #{e.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def enabled_configs
|
||||
Gitlab::MailRoom.enabled_configs
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -116,7 +116,7 @@ module Gitlab
|
|||
jwt_token = params[param_key]
|
||||
raise "Empty JWT param: #{param_key}" if jwt_token.blank?
|
||||
|
||||
payload = Gitlab::Workhorse.decode_jwt(jwt_token).first
|
||||
payload = Gitlab::Workhorse.decode_jwt_with_issuer(jwt_token).first
|
||||
raise "Invalid JWT payload: not a Hash" unless payload.is_a?(Hash)
|
||||
|
||||
upload_params = payload.fetch(JWT_PARAM_FIXED_KEY, {})
|
||||
|
@ -172,7 +172,7 @@ module Gitlab
|
|||
encoded_message = env.delete(RACK_ENV_KEY)
|
||||
return @app.call(env) if encoded_message.blank?
|
||||
|
||||
message = ::Gitlab::Workhorse.decode_jwt(encoded_message)[0]
|
||||
message = ::Gitlab::Workhorse.decode_jwt_with_issuer(encoded_message)[0]
|
||||
|
||||
::Gitlab::Middleware::Multipart::Handler.new(env, message).with_open_files do
|
||||
@app.call(env)
|
||||
|
|
|
@ -10,7 +10,7 @@ module Gitlab
|
|||
|
||||
class << self
|
||||
def verify_api_request(request_headers)
|
||||
decode_jwt_for_issuer('gitlab-pages', request_headers[INTERNAL_API_REQUEST_HEADER])
|
||||
decode_jwt(request_headers[INTERNAL_API_REQUEST_HEADER], issuer: 'gitlab-pages')
|
||||
rescue JWT::DecodeError
|
||||
false
|
||||
end
|
||||
|
|
|
@ -203,11 +203,11 @@ module Gitlab
|
|||
end
|
||||
|
||||
def verify_api_request!(request_headers)
|
||||
decode_jwt(request_headers[INTERNAL_API_REQUEST_HEADER])
|
||||
decode_jwt_with_issuer(request_headers[INTERNAL_API_REQUEST_HEADER])
|
||||
end
|
||||
|
||||
def decode_jwt(encoded_message)
|
||||
decode_jwt_for_issuer('gitlab-workhorse', encoded_message)
|
||||
def decode_jwt_with_issuer(encoded_message)
|
||||
decode_jwt(encoded_message, issuer: 'gitlab-workhorse')
|
||||
end
|
||||
|
||||
def secret_path
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 '.write_secret' do
|
||||
it 'uses mode 0600' do
|
||||
expect(File.stat(test_class.secret_path).mode & 0777).to eq(0600)
|
||||
end
|
||||
describe '.secret' do
|
||||
it_behaves_like 'reading secret from the secret path' do
|
||||
subject(:secret) { test_class.secret }
|
||||
|
||||
it 'writes base64 data' do
|
||||
bytes = Base64.strict_decode64(File.read(test_class.secret_path))
|
||||
|
||||
expect(bytes).not_to be_empty
|
||||
let(:secret_path) { test_class.secret_path }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.decode_jwt_for_issuer' do
|
||||
let(:payload) { { 'iss' => 'test_issuer' } }
|
||||
describe '.read_secret' do
|
||||
it_behaves_like 'reading secret from the secret path' do
|
||||
subject(:secret) { test_class.read_secret(secret_path) }
|
||||
|
||||
it 'accepts a correct header' do
|
||||
encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
|
||||
let(:secret_path) { test_class.secret_path }
|
||||
end
|
||||
end
|
||||
|
||||
expect { test_class.decode_jwt_for_issuer('test_issuer', encoded_message) }.not_to raise_error
|
||||
describe '.write_secret' do
|
||||
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 'raises an error when the JWT is not signed' do
|
||||
encoded_message = JWT.encode(payload, nil, 'none')
|
||||
context 'with an input' do
|
||||
let(:another_path) do
|
||||
Rails.root.join('tmp', 'tests', '.jwt_another_shared_secret')
|
||||
end
|
||||
|
||||
expect { test_class.decode_jwt_for_issuer('test_issuer', encoded_message) }.to raise_error(JWT::DecodeError)
|
||||
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' 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 '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) }.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 'use an input secret' do
|
||||
let(:another_secret) { 'another secret' }
|
||||
|
||||
expect { test_class.decode_jwt_for_issuer('test_issuer', encoded_message) }.to raise_error(JWT::DecodeError)
|
||||
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
|
||||
|
||||
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 issuer is incorrect' do
|
||||
payload['iss'] = 'somebody else'
|
||||
encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
|
||||
context 'issuer option' do
|
||||
let(:payload) { { 'iss' => 'test_issuer' } }
|
||||
|
||||
expect { test_class.decode_jwt_for_issuer('test_issuer', encoded_message) }.to raise_error(JWT::DecodeError)
|
||||
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(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
|
||||
|
||||
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(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
|
||||
|
|
188
spec/lib/gitlab/mail_room/authenticator_spec.rb
Normal file
188
spec/lib/gitlab/mail_room/authenticator_spec.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
185
spec/requests/api/internal/mail_room_spec.rb
Normal file
185
spec/requests/api/internal/mail_room_spec.rb
Normal file
|
@ -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
|
Loading…
Reference in a new issue