Commit c454e131 authored by Doug Stull's avatar Doug Stull Committed by Dmitry Gruzd

Add mailgun application settings for receiving email send failures

parent a9f93de6
...@@ -343,6 +343,8 @@ module ApplicationSettingsHelper ...@@ -343,6 +343,8 @@ module ApplicationSettingsHelper
:commit_email_hostname, :commit_email_hostname,
:protected_ci_variables, :protected_ci_variables,
:local_markdown_version, :local_markdown_version,
:mailgun_signing_key,
:mailgun_events_enabled,
:snowplow_collector_hostname, :snowplow_collector_hostname,
:snowplow_cookie_domain, :snowplow_cookie_domain,
:snowplow_enabled, :snowplow_enabled,
......
...@@ -172,6 +172,11 @@ class ApplicationSetting < ApplicationRecord ...@@ -172,6 +172,11 @@ class ApplicationSetting < ApplicationRecord
addressable_url: { enforce_sanitization: true }, addressable_url: { enforce_sanitization: true },
if: :gitpod_enabled if: :gitpod_enabled
validates :mailgun_signing_key,
presence: true,
length: { maximum: 255 },
if: :mailgun_events_enabled
validates :snowplow_collector_hostname, validates :snowplow_collector_hostname,
presence: true, presence: true,
hostname: true, hostname: true,
...@@ -552,6 +557,7 @@ class ApplicationSetting < ApplicationRecord ...@@ -552,6 +557,7 @@ class ApplicationSetting < ApplicationRecord
attr_encrypted :secret_detection_token_revocation_token, encryption_options_base_32_aes_256_gcm attr_encrypted :secret_detection_token_revocation_token, encryption_options_base_32_aes_256_gcm
attr_encrypted :cloud_license_auth_token, encryption_options_base_32_aes_256_gcm attr_encrypted :cloud_license_auth_token, encryption_options_base_32_aes_256_gcm
attr_encrypted :external_pipeline_validation_service_token, encryption_options_base_32_aes_256_gcm attr_encrypted :external_pipeline_validation_service_token, encryption_options_base_32_aes_256_gcm
attr_encrypted :mailgun_signing_key, encryption_options_base_32_aes_256_gcm.merge(encode: false)
validates :disable_feed_token, validates :disable_feed_token,
inclusion: { in: [true, false], message: _('must be a boolean value') } inclusion: { in: [true, false], message: _('must be a boolean value') }
......
...@@ -104,6 +104,8 @@ module ApplicationSettingImplementation ...@@ -104,6 +104,8 @@ module ApplicationSettingImplementation
issues_create_limit: 300, issues_create_limit: 300,
local_markdown_version: 0, local_markdown_version: 0,
login_recaptcha_protection_enabled: false, login_recaptcha_protection_enabled: false,
mailgun_signing_key: nil,
mailgun_events_enabled: false,
max_artifacts_size: Settings.artifacts['max_size'], max_artifacts_size: Settings.artifacts['max_size'],
max_attachment_size: Settings.gitlab['max_attachment_size'], max_attachment_size: Settings.gitlab['max_attachment_size'],
max_import_size: 0, max_import_size: 0,
......
- return unless Feature.enabled?(:mailgun_events_receiver)
- expanded = integration_expanded?('mailgun_')
%section.settings.as-mailgun.no-animate#js-mailgun-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Mailgun')
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
= _('Configure the %{link} integration.').html_safe % { link: link_to(_('Mailgun events'), 'https://documentation.mailgun.com/en/latest/user_manual.html#webhooks', target: '_blank') }
.settings-content
= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-mailgun-settings'), html: { class: 'fieldset-form', id: 'mailgun-settings' } do |f|
= form_errors(@application_setting) if expanded
%fieldset
.form-group
.form-check
= f.check_box :mailgun_events_enabled, class: 'form-check-input'
= f.label :mailgun_events_enabled, _('Enable Mailgun event receiver'), class: 'form-check-label'
.form-group
= f.label :mailgun_signing_key, _('Mailgun HTTP webhook signing key'), class: 'label-light'
= f.text_field :mailgun_signing_key, class: 'form-control gl-form-input'
= f.submit _('Save changes'), class: 'gl-button btn btn-confirm'
...@@ -107,6 +107,7 @@ ...@@ -107,6 +107,7 @@
= render_if_exists 'admin/application_settings/maintenance_mode_settings_form' = render_if_exists 'admin/application_settings/maintenance_mode_settings_form'
= render 'admin/application_settings/gitpod' = render 'admin/application_settings/gitpod'
= render 'admin/application_settings/kroki' = render 'admin/application_settings/kroki'
= render 'admin/application_settings/mailgun'
= render 'admin/application_settings/plantuml' = render 'admin/application_settings/plantuml'
= render 'admin/application_settings/sourcegraph' = render 'admin/application_settings/sourcegraph'
= render_if_exists 'admin/application_settings/slack' = render_if_exists 'admin/application_settings/slack'
......
...@@ -151,6 +151,7 @@ module Gitlab ...@@ -151,6 +151,7 @@ module Gitlab
elasticsearch_password elasticsearch_password
search search
jwt jwt
mailgun_signing_key
otp_attempt otp_attempt
sentry_dsn sentry_dsn
trace trace
......
---
name: mailgun_events_receiver
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64249
rollout_issue_url:
milestone: '14.1'
type: development
group: group::expansion
default_enabled: false
# frozen_string_literal: true
class AddMailgunSettingsToApplicationSetting < ActiveRecord::Migration[6.1]
def change
add_column :application_settings, :encrypted_mailgun_signing_key, :binary
add_column :application_settings, :encrypted_mailgun_signing_key_iv, :binary
add_column :application_settings, :mailgun_events_enabled, :boolean, default: false, null: false
end
end
8d73f4b4b716176afe5a9b0ee3a4ef28bbbc2fe944a18fb66afa8cf8f763e8ac
\ No newline at end of file
...@@ -9522,6 +9522,9 @@ CREATE TABLE application_settings ( ...@@ -9522,6 +9522,9 @@ CREATE TABLE application_settings (
diff_max_lines integer DEFAULT 50000 NOT NULL, diff_max_lines integer DEFAULT 50000 NOT NULL,
diff_max_files integer DEFAULT 1000 NOT NULL, diff_max_files integer DEFAULT 1000 NOT NULL,
valid_runner_registrars character varying[] DEFAULT '{project,group}'::character varying[], valid_runner_registrars character varying[] DEFAULT '{project,group}'::character varying[],
encrypted_mailgun_signing_key bytea,
encrypted_mailgun_signing_key_iv bytea,
mailgun_events_enabled boolean DEFAULT false NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)), CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)), CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)),
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)), CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
...@@ -328,6 +328,8 @@ listed in the descriptions of the relevant settings. ...@@ -328,6 +328,8 @@ listed in the descriptions of the relevant settings.
| `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.| | `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.|
| `keep_latest_artifact` | boolean | no | Prevent the deletion of the artifacts from the most recent successful jobs, regardless of the expiry time. Enabled by default. | | `keep_latest_artifact` | boolean | no | Prevent the deletion of the artifacts from the most recent successful jobs, regardless of the expiry time. Enabled by default. |
| `local_markdown_version` | integer | no | Increase this value when any cached Markdown should be invalidated. | | `local_markdown_version` | integer | no | Increase this value when any cached Markdown should be invalidated. |
| `mailgun_signing_key` | string | no | The Mailgun HTTP webhook signing key for receiving events from webhook |
| `mailgun_events_enabled` | boolean | no | Enable Mailgun event receiver. |
| `maintenance_mode_message` | string | no | **(PREMIUM)** Message displayed when instance is in maintenance mode. | | `maintenance_mode_message` | string | no | **(PREMIUM)** Message displayed when instance is in maintenance mode. |
| `maintenance_mode` | boolean | no | **(PREMIUM)** When instance is in maintenance mode, non-administrative users can sign in with read-only access and make read-only API requests. | | `maintenance_mode` | boolean | no | **(PREMIUM)** When instance is in maintenance mode, non-administrative users can sign in with read-only access and make read-only API requests. |
| `max_artifacts_size` | integer | no | Maximum artifacts size in MB. | | `max_artifacts_size` | integer | no | Maximum artifacts size in MB. |
......
...@@ -160,6 +160,10 @@ module API ...@@ -160,6 +160,10 @@ module API
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.' optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
optional :local_markdown_version, type: Integer, desc: 'Local markdown version, increase this value when any cached markdown should be invalidated' optional :local_markdown_version, type: Integer, desc: 'Local markdown version, increase this value when any cached markdown should be invalidated'
optional :allow_local_requests_from_hooks_and_services, type: Boolean, desc: 'Deprecated: Use :allow_local_requests_from_web_hooks_and_services instead. Allow requests to the local network from hooks and services.' # support legacy names, can be removed in v5 optional :allow_local_requests_from_hooks_and_services, type: Boolean, desc: 'Deprecated: Use :allow_local_requests_from_web_hooks_and_services instead. Allow requests to the local network from hooks and services.' # support legacy names, can be removed in v5
optional :mailgun_events_enabled, type: Grape::API::Boolean, desc: 'Enable Mailgun event receiver'
given mailgun_events_enabled: ->(val) { val } do
requires :mailgun_signing_key, type: String, desc: 'The Mailgun HTTP webhook signing key for receiving events from webhook'
end
optional :snowplow_enabled, type: Grape::API::Boolean, desc: 'Enable Snowplow tracking' optional :snowplow_enabled, type: Grape::API::Boolean, desc: 'Enable Snowplow tracking'
given snowplow_enabled: ->(val) { val } do given snowplow_enabled: ->(val) { val } do
requires :snowplow_collector_hostname, type: String, desc: 'The Snowplow collector hostname' requires :snowplow_collector_hostname, type: String, desc: 'The Snowplow collector hostname'
......
...@@ -8205,6 +8205,9 @@ msgstr "" ...@@ -8205,6 +8205,9 @@ msgstr ""
msgid "Configure storage path settings." msgid "Configure storage path settings."
msgstr "" msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
msgid "Configure the way a user creates a new account." msgid "Configure the way a user creates a new account."
msgstr "" msgstr ""
...@@ -11935,6 +11938,9 @@ msgstr "" ...@@ -11935,6 +11938,9 @@ msgstr ""
msgid "Enable Kroki" msgid "Enable Kroki"
msgstr "" msgstr ""
msgid "Enable Mailgun event receiver"
msgstr ""
msgid "Enable PlantUML" msgid "Enable PlantUML"
msgstr "" msgstr ""
...@@ -19715,6 +19721,15 @@ msgstr "" ...@@ -19715,6 +19721,15 @@ msgstr ""
msgid "Made this issue confidential." msgid "Made this issue confidential."
msgstr "" msgstr ""
msgid "Mailgun"
msgstr ""
msgid "Mailgun HTTP webhook signing key"
msgstr ""
msgid "Mailgun events"
msgstr ""
msgid "Maintenance mode" msgid "Maintenance mode"
msgstr "" msgstr ""
......
...@@ -269,7 +269,10 @@ RSpec.describe 'Admin updates settings' do ...@@ -269,7 +269,10 @@ RSpec.describe 'Admin updates settings' do
end end
context 'Integrations page' do context 'Integrations page' do
let(:mailgun_events_receiver_enabled) { true }
before do before do
stub_feature_flags(mailgun_events_receiver: mailgun_events_receiver_enabled)
visit general_admin_application_settings_path visit general_admin_application_settings_path
end end
...@@ -282,6 +285,28 @@ RSpec.describe 'Admin updates settings' do ...@@ -282,6 +285,28 @@ RSpec.describe 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully" expect(page).to have_content "Application settings saved successfully"
expect(current_settings.hide_third_party_offers).to be true expect(current_settings.hide_third_party_offers).to be true
end end
context 'when mailgun_events_receiver feature flag is enabled' do
it 'enabling Mailgun events', :aggregate_failures do
page.within('.as-mailgun') do
check 'Enable Mailgun event receiver'
fill_in 'Mailgun HTTP webhook signing key', with: 'MAILGUN_SIGNING_KEY'
click_button 'Save changes'
end
expect(page).to have_content 'Application settings saved successfully'
expect(current_settings.mailgun_events_enabled).to be true
expect(current_settings.mailgun_signing_key).to eq 'MAILGUN_SIGNING_KEY'
end
end
context 'when mailgun_events_receiver feature flag is disabled' do
let(:mailgun_events_receiver_enabled) { false }
it 'does not have mailgun' do
expect(page).not_to have_selector('.as-mailgun')
end
end
end end
context 'when Service Templates are enabled' do context 'when Service Templates are enabled' do
......
...@@ -258,6 +258,19 @@ RSpec.describe ApplicationSetting do ...@@ -258,6 +258,19 @@ RSpec.describe ApplicationSetting do
it { is_expected.to allow_value(nil).for(:snowplow_collector_hostname) } it { is_expected.to allow_value(nil).for(:snowplow_collector_hostname) }
end end
context 'when mailgun_events_enabled is enabled' do
before do
setting.mailgun_events_enabled = true
end
it { is_expected.to validate_presence_of(:mailgun_signing_key) }
it { is_expected.to validate_length_of(:mailgun_signing_key).is_at_most(255) }
end
context 'when mailgun_events_enabled is not enabled' do
it { is_expected.not_to validate_presence_of(:mailgun_signing_key) }
end
context "when user accepted let's encrypt terms of service" do context "when user accepted let's encrypt terms of service" do
before do before do
expect do expect do
......
...@@ -127,6 +127,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do ...@@ -127,6 +127,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
spam_check_endpoint_enabled: true, spam_check_endpoint_enabled: true,
spam_check_endpoint_url: 'grpc://example.com/spam_check', spam_check_endpoint_url: 'grpc://example.com/spam_check',
spam_check_api_key: 'SPAM_CHECK_API_KEY', spam_check_api_key: 'SPAM_CHECK_API_KEY',
mailgun_events_enabled: true,
mailgun_signing_key: 'MAILGUN_SIGNING_KEY',
disabled_oauth_sign_in_sources: 'unknown', disabled_oauth_sign_in_sources: 'unknown',
import_sources: 'github,bitbucket', import_sources: 'github,bitbucket',
wiki_page_max_content_bytes: 12345, wiki_page_max_content_bytes: 12345,
...@@ -175,6 +177,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do ...@@ -175,6 +177,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
expect(json_response['spam_check_endpoint_enabled']).to be_truthy expect(json_response['spam_check_endpoint_enabled']).to be_truthy
expect(json_response['spam_check_endpoint_url']).to eq('grpc://example.com/spam_check') expect(json_response['spam_check_endpoint_url']).to eq('grpc://example.com/spam_check')
expect(json_response['spam_check_api_key']).to eq('SPAM_CHECK_API_KEY') expect(json_response['spam_check_api_key']).to eq('SPAM_CHECK_API_KEY')
expect(json_response['mailgun_events_enabled']).to be(true)
expect(json_response['mailgun_signing_key']).to eq('MAILGUN_SIGNING_KEY')
expect(json_response['disabled_oauth_sign_in_sources']).to eq([]) expect(json_response['disabled_oauth_sign_in_sources']).to eq([])
expect(json_response['import_sources']).to match_array(%w(github bitbucket)) expect(json_response['import_sources']).to match_array(%w(github bitbucket))
expect(json_response['wiki_page_max_content_bytes']).to eq(12345) expect(json_response['wiki_page_max_content_bytes']).to eq(12345)
...@@ -493,6 +497,15 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do ...@@ -493,6 +497,15 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
end end
end end
context "missing mailgun_signing_key value when mailgun_events_enabled is true" do
it "returns a blank parameter error message" do
put api("/application/settings", admin), params: { mailgun_events_enabled: true }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('mailgun_signing_key is missing')
end
end
context "personal access token prefix settings" do context "personal access token prefix settings" do
context "handles validation errors" do context "handles validation errors" do
it "fails to update the settings with too long prefix" do it "fails to update the settings with too long prefix" 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