Commit 836653f8 authored by Sean Arnold's avatar Sean Arnold Committed by Igor Drozdov

Add HTTP Basic support for Alert authentication

parent 5e8f3a30
......@@ -3,6 +3,8 @@
module Projects
module Alerting
class NotificationsController < Projects::ApplicationController
include ActionController::HttpAuthentication::Basic
respond_to :json
skip_before_action :verify_authenticity_token
......@@ -27,9 +29,19 @@ module Projects
end
def extract_alert_manager_token(request)
extract_bearer_token(request) || extract_basic_auth_token(request)
end
def extract_bearer_token(request)
Doorkeeper::OAuth::Token.from_bearer_authorization(request)
end
def extract_basic_auth_token(request)
_username, token = user_name_and_password(request)
token
end
def notify_service
notify_service_class.new(project, notification_payload)
end
......
......@@ -125,17 +125,7 @@ NOTE:
Ensure your requests are smaller than the
[payload application limits](../../administration/instance_limits.md#generic-alert-json-payloads).
Example request:
```shell
curl --request POST \
--data '{"title": "Incident title"}' \
--header "Authorization: Bearer <authorization_key>" \
--header "Content-Type: application/json" \
<url>
```
The `<authorization_key>` and `<url>` values can be found when configuring an alert integration.
### Example request body
Example payload:
......@@ -157,6 +147,55 @@ Example payload:
}
```
## Authorization
The following authorization methods are accepted:
- Bearer authorization header
- Basic authentication
The `<authorization_key>` and `<url>` values can be found when configuring an alert integration.
### Bearer authorization header
The authorization key can be used as the Bearer token:
```shell
curl --request POST \
--data '{"title": "Incident title"}' \
--header "Authorization: Bearer <authorization_key>" \
--header "Content-Type: application/json" \
<url>
```
### Basic authentication
The authorization key can be used as the `password`. The `username` is left blank:
- username: <blank>
- pasword: authorization_key
```shell
curl --request POST \
--data '{"title": "Incident title"}' \
--header "Authorization: Basic <base_64_encoded_credentials>" \
--header "Content-Type: application/json" \
<url>
```
Basic authentication can also be used with credentials directly in the URL:
```shell
curl --request POST \
--data '{"title": "Incident title"}' \
--header "Content-Type: application/json" \
<username:password@url>
```
WARNING:
Using your authorization key in the URL is insecure, as it's visible in server logs. We recommend
using one of the above header options if your tooling supports it.
## Triggering test alerts
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab in 13.2.
......
......@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Projects::Alerting::NotificationsController do
include HttpBasicAuthHelpers
let_it_be(:project) { create(:project) }
let_it_be(:environment) { create(:environment, project: project) }
......@@ -53,86 +55,96 @@ RSpec.describe Projects::Alerting::NotificationsController do
end
end
context 'bearer token' do
context 'when set' do
context 'when extractable' do
before do
request.headers['HTTP_AUTHORIZATION'] = 'Bearer some token'
end
it 'extracts bearer token' do
expect(notify_service).to receive(:execute).with('some token', nil)
make_request
end
context 'with a corresponding integration' do
context 'with integration parameters specified' do
let_it_be_with_reload(:integration) { create(:alert_management_http_integration, project: project) }
shared_examples 'a working token' do
it 'extracts token' do
expect(notify_service).to receive(:execute).with('some token', nil)
let(:params) { project_params(endpoint_identifier: integration.endpoint_identifier, name: integration.name) }
context 'the integration is active' do
it 'extracts and finds the integration' do
expect(notify_service).to receive(:execute).with('some token', integration)
make_request
end
make_request
end
end
context 'with a corresponding integration' do
context 'with integration parameters specified' do
let_it_be_with_reload(:integration) { create(:alert_management_http_integration, project: project) }
context 'when the integration is inactive' do
before do
integration.update!(active: false)
end
let(:params) { project_params(endpoint_identifier: integration.endpoint_identifier, name: integration.name) }
it 'does not find an integration' do
expect(notify_service).to receive(:execute).with('some token', nil)
context 'the integration is active' do
it 'extracts and finds the integration' do
expect(notify_service).to receive(:execute).with('some token', integration)
make_request
end
end
make_request
end
end
context 'without integration parameters specified' do
let_it_be(:integration) { create(:alert_management_http_integration, :legacy, project: project) }
context 'when the integration is inactive' do
before do
integration.update!(active: false)
end
it 'extracts and finds the legacy integration' do
expect(notify_service).to receive(:execute).with('some token', integration)
it 'does not find an integration' do
expect(notify_service).to receive(:execute).with('some token', nil)
make_request
end
make_request
end
end
end
context 'when inextractable' do
it 'passes nil for a non-bearer token' do
request.headers['HTTP_AUTHORIZATION'] = 'some token'
context 'without integration parameters specified' do
let_it_be(:integration) { create(:alert_management_http_integration, :legacy, project: project) }
expect(notify_service).to receive(:execute).with(nil, nil)
it 'extracts and finds the legacy integration' do
expect(notify_service).to receive(:execute).with('some token', integration)
make_request
end
end
end
end
context 'when missing' do
it 'passes nil' do
expect(notify_service).to receive(:execute).with(nil, nil)
make_request
context 'with bearer token' do
context 'when set' do
before do
request.headers.merge(build_token_auth_header('some token'))
end
it_behaves_like 'a working token'
end
end
context 'with basic auth token' do
before do
request.headers.merge basic_auth_header(nil, 'some token')
end
it_behaves_like 'a working token'
end
context 'when inextractable token' do
it 'passes nil for a non-bearer token' do
request.headers['HTTP_AUTHORIZATION'] = 'some token'
expect(notify_service).to receive(:execute).with(nil, nil)
make_request
end
end
context 'when missing token' do
it 'passes nil' do
expect(notify_service).to receive(:execute).with(nil, nil)
make_request
end
end
end
context 'generic alert payload' do
context 'with generic alert payload' do
it_behaves_like 'process alert payload', Projects::Alerting::NotifyService do
let(:payload) { { title: 'Alert title' } }
end
end
context 'Prometheus alert payload' do
context 'with Prometheus alert payload' do
include PrometheusHelpers
it_behaves_like 'process alert payload', Projects::Prometheus::Alerts::NotifyService do
......
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