Commit 6610f2fd authored by Nathan Friend's avatar Nathan Friend

Add auto SSL toggle to Pages domain settings page

This commit adds an auto SSL toggle switch to the settings page of
GitLab Pages domains.  This toggle enable or disabled auto SSL
management via Let's Encrypt.  Toggling the button dynamically
updates the form using client-side JavaScript.  All changes are behind
feature flags.
parent 98e1f7d5
import initForm from '~/pages/projects/pages_domains/form';
document.addEventListener('DOMContentLoaded', initForm);
import setupToggleButtons from '~/toggle_buttons';
export default () => {
const toggleContainer = document.querySelector('.js-auto-ssl-toggle-container');
if (toggleContainer) {
const onToggleButtonClicked = isAutoSslEnabled => {
Array.from(document.querySelectorAll('.js-shown-if-auto-ssl')).forEach(el => {
if (isAutoSslEnabled) {
el.classList.remove('d-none');
} else {
el.classList.add('d-none');
}
});
Array.from(document.querySelectorAll('.js-shown-unless-auto-ssl')).forEach(el => {
if (isAutoSslEnabled) {
el.classList.add('d-none');
} else {
el.classList.remove('d-none');
}
});
Array.from(document.querySelectorAll('.js-enabled-if-auto-ssl')).forEach(el => {
if (isAutoSslEnabled) {
el.removeAttribute('disabled');
} else {
el.setAttribute('disabled', 'disabled');
}
});
Array.from(document.querySelectorAll('.js-enabled-unless-auto-ssl')).forEach(el => {
if (isAutoSslEnabled) {
el.setAttribute('disabled', 'disabled');
} else {
el.removeAttribute('disabled');
}
});
};
setupToggleButtons(toggleContainer, onToggleButtonClicked);
}
};
import initForm from '~/pages/projects/pages_domains/form';
document.addEventListener('DOMContentLoaded', initForm);
......@@ -65,11 +65,11 @@ class Projects::PagesDomainsController < Projects::ApplicationController
private
def create_params
params.require(:pages_domain).permit(:key, :certificate, :domain)
params.require(:pages_domain).permit(:key, :certificate, :domain, :auto_ssl_enabled)
end
def update_params
params.require(:pages_domain).permit(:key, :certificate)
params.require(:pages_domain).permit(:key, :certificate, :auto_ssl_enabled)
end
# rubocop: disable CodeReuse/ActiveRecord
......
- if @domain.errors.any?
#error_explanation
.alert.alert-danger
- @domain.errors.full_messages.each do |msg|
%p= msg
= msg
.form-group.row
.col-sm-2.col-form-label
= f.label :domain, _("Domain")
.col-sm-10
= f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control', disabled: @domain.persisted?
= f.text_field :domain, required: true, autocomplete: "off", class: "form-control", disabled: @domain.persisted?
- if Gitlab.config.pages.external_https
- auto_ssl_available = Feature.enabled?(:pages_auto_ssl)
- auto_ssl_enabled = @domain.auto_ssl_enabled?
- auto_ssl_available_and_enabled = auto_ssl_available && auto_ssl_enabled
- if auto_ssl_available
.form-group.row
.col-sm-2.col-form-label
%label{ for: "pages_domain_auto_ssl_enabled_button" }
- lets_encrypt_link_url = "https://letsencrypt.org/"
- lets_encrypt_link_start = "<a href=\"%{lets_encrypt_link_url}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-nowrap\">".html_safe % { lets_encrypt_link_url: lets_encrypt_link_url }
- lets_encrypt_link_end = "</a>".html_safe
= _("Automatic certificate management using %{lets_encrypt_link_start}Let's Encrypt%{lets_encrypt_link_end}").html_safe % { lets_encrypt_link_start: lets_encrypt_link_start, lets_encrypt_link_end: lets_encrypt_link_end }
.col-sm-10.js-auto-ssl-toggle-container
%button{ type: "button", id: "pages_domain_auto_ssl_enabled_button",
class: "js-project-feature-toggle project-feature-toggle mt-2 #{"is-checked" if auto_ssl_available_and_enabled}",
"aria-label": _("Automatic certificate management using Let's Encrypt") }
= f.hidden_field :auto_ssl_enabled?, class: "js-project-feature-toggle-input"
%span.toggle-icon
= sprite_icon("status_success_borderless", size: 16, css_class: "toggle-icon-svg toggle-status-checked")
= sprite_icon("status_failed_borderless", size: 16, css_class: "toggle-icon-svg toggle-status-unchecked")
%p.text-secondary.mt-3
- docs_link_url = help_page_path("user/project/pages/lets_encrypt_for_gitlab_pages.md", anchor: "lets-encrypt-for-gitlab-pages")
- docs_link_start = "<a href=\"%{docs_link_url}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-nowrap\">".html_safe % { docs_link_url: docs_link_url }
- docs_link_end = "</a>".html_safe
= _("Let's Encrypt is a free, automated, and open certificate authority (CA) that gives digital certificates in order to enable HTTPS (SSL/TLS) for websites. Learn more about Let's Encrypt configuration by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}.").html_safe % { docs_link_url: docs_link_url, docs_link_start: docs_link_start, docs_link_end: docs_link_end }
.js-shown-if-auto-ssl{ class: ("d-none" unless auto_ssl_available_and_enabled) }
.form-group.row
.col-sm-2.col-form-label
= f.label :certificate, _("Certificate (PEM)")
.col-sm-10
= f.text_area :certificate, rows: 5, class: 'form-control'
%span.help-inline= _("Upload a certificate for your domain with all intermediates")
- if auto_ssl_available_and_enabled && !@domain.certificate.empty?
= f.text_area :certificate,
rows: 5,
class: "form-control",
disabled: true
%span.help-inline.text-muted= _("This certificate is automatically managed by Let's Encrypt")
- else
%p.text-secondary.form-control-plaintext= _("The certificate will be shown here once it has been obtained from Let's Encrypt. This process may take up to an hour to complete.")
.js-shown-unless-auto-ssl{ class: ("d-none" if auto_ssl_available_and_enabled) }
.form-group.row
.col-sm-2.col-form-label
= f.label :certificate, _("Certificate (PEM)")
.col-sm-10
= f.text_area :certificate,
rows: 5,
class: "form-control js-enabled-unless-auto-ssl",
value: (@domain.certificate unless auto_ssl_available_and_enabled),
disabled: auto_ssl_available_and_enabled
%span.help-inline.text-muted= _("Upload a certificate for your domain with all intermediates")
.form-group.row
.col-sm-2.col-form-label
= f.label :key, _("Key (PEM)")
.col-sm-10
= f.text_area :key, rows: 5, class: 'form-control'
%span.help-inline= _("Upload a private key for your certificate")
= f.text_area :key,
rows: 5,
class: "form-control js-enabled-unless-auto-ssl",
value: (@domain.key unless auto_ssl_available_and_enabled),
disabled: auto_ssl_available_and_enabled
%span.help-inline.text-muted= _("Upload a private key for your certificate")
- else
.nothing-here-block
= _("Support for custom certificates is disabled. Ask your system's administrator to enable it.")
- docs_link_url = help_page_path("user/project/pages/getting_started_part_three.md", anchor: "adding-certificates-to-your-project")
- docs_link_start = "<a href=\"%{docs_link_url}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-nowrap\">".html_safe % { docs_link_url: docs_link_url }
- docs_link_end = "</a>".html_safe
-# Hiding behind a feature flag to avoid any changes to this feature's implemention
-# when the :pages_auto_ssl feature flag is disabled. This check should be removed
-# once the :pages_auto_ssl feature flag is removed.
- if Feature.enabled?(:pages_auto_ssl)
%p= _("Learn more about adding certificates to your project by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}.").html_safe % { docs_link_url: docs_link_url, docs_link_start: docs_link_start, docs_link_end: docs_link_end }
......@@ -3,6 +3,7 @@
- page_title @domain.domain
%h3.page-title
= @domain.domain
= render 'projects/pages_domains/helper_text'
%hr.clearfix
%div
= form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'fieldset-form' } do |f|
......
......@@ -2,6 +2,7 @@
- page_title _('New Pages Domain')
%h3.page-title
= _("New Pages Domain")
= render 'projects/pages_domains/helper_text'
%hr.clearfix
%div
= form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'fieldset-form' } do |f|
......
---
title: Add auto SSL toggle option to Pages domain settings page
merge_request: 26438
author:
type: added
......@@ -1329,6 +1329,12 @@ msgstr ""
msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
msgstr ""
msgid "Automatic certificate management using %{lets_encrypt_link_start}Let's Encrypt%{lets_encrypt_link_end}"
msgstr ""
msgid "Automatic certificate management using Let's Encrypt"
msgstr ""
msgid "Automatically marked as default internal user"
msgstr ""
......@@ -5803,6 +5809,9 @@ msgstr ""
msgid "Learn more about Kubernetes"
msgstr ""
msgid "Learn more about adding certificates to your project by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}."
msgstr ""
msgid "Learn more about signing commits"
msgstr ""
......@@ -5830,6 +5839,9 @@ msgstr ""
msgid "Let's Encrypt does not accept emails on example.com"
msgstr ""
msgid "Let's Encrypt is a free, automated, and open certificate authority (CA) that gives digital certificates in order to enable HTTPS (SSL/TLS) for websites. Learn more about Let's Encrypt configuration by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}."
msgstr ""
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
......@@ -9864,6 +9876,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The certificate will be shown here once it has been obtained from Let's Encrypt. This process may take up to an hour to complete."
msgstr ""
msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
msgstr ""
......@@ -10185,6 +10200,9 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
msgid "This certificate is automatically managed by Let's Encrypt"
msgstr ""
msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
msgstr ""
......
# frozen_string_literal: true
require 'spec_helper'
describe "Pages with Let's Encrypt", :https_pages_enabled do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:role) { :maintainer }
let(:certificate_pem) do
<<~PEM
-----BEGIN CERTIFICATE-----
MIICGzCCAYSgAwIBAgIBATANBgkqhkiG9w0BAQUFADAbMRkwFwYDVQQDExB0ZXN0
LWNlcnRpZmljYXRlMB4XDTE2MDIxMjE0MzIwMFoXDTIwMDQxMjE0MzIwMFowGzEZ
MBcGA1UEAxMQdGVzdC1jZXJ0aWZpY2F0ZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
gYkCgYEApL4J9L0ZxFJ1hI1LPIflAlAGvm6ZEvoT4qKU5Xf2JgU7/2geNR1qlNFa
SvCc08Knupp5yTgmvyK/Xi09U0N82vvp4Zvr/diSc4A/RA6Mta6egLySNT438kdT
nY2tR5feoTLwQpX0t4IMlwGQGT5h6Of2fKmDxzuwuyffcIHqLdsCAwEAAaNvMG0w
DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxl9WSxBprB0z0ibJs3rXEk0+95AwCwYD
VR0PBAQDAgXgMBEGCWCGSAGG+EIBAQQEAwIGQDAeBglghkgBhvhCAQ0EERYPeGNh
IGNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4GBAGC4T8SlFHK0yPSa+idGLQFQ
joZp2JHYvNlTPkRJ/J4TcXxBTJmArcQgTIuNoBtC+0A/SwdK4MfTCUY4vNWNdese
5A4K65Nb7Oh1AdQieTBHNXXCdyFsva9/ScfQGEl7p55a52jOPs0StPd7g64uvjlg
YHi2yesCrOvVXt+lgPTd
-----END CERTIFICATE-----
PEM
end
let(:certificate_key) do
<<~KEY
-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKS+CfS9GcRSdYSN
SzyH5QJQBr5umRL6E+KilOV39iYFO/9oHjUdapTRWkrwnNPCp7qaeck4Jr8iv14t
PVNDfNr76eGb6/3YknOAP0QOjLWunoC8kjU+N/JHU52NrUeX3qEy8EKV9LeCDJcB
kBk+Yejn9nypg8c7sLsn33CB6i3bAgMBAAECgYA2D26w80T7WZvazYr86BNMePpd
j2mIAqx32KZHzt/lhh40J/SRtX9+Kl0Y7nBoRR5Ja9u/HkAIxNxLiUjwg9r6cpg/
uITEF5nMt7lAk391BuI+7VOZZGbJDsq2ulPd6lO+C8Kq/PI/e4kXcIjeH6KwQsuR
5vrXfBZ3sQfflaiN4QJBANBt8JY2LIGQF8o89qwUpRL5vbnKQ4IzZ5+TOl4RLR7O
AQpJ81tGuINghO7aunctb6rrcKJrxmEH1whzComybrMCQQDKV49nOBudRBAIgG4K
EnLzsRKISUHMZSJiYTYnablof8cKw1JaQduw7zgrUlLwnroSaAGX88+Jw1f5n2Lh
Vlg5AkBDdUGnrDLtYBCDEQYZHblrkc7ZAeCllDOWjxUV+uMqlCv8A4Ey6omvY57C
m6I8DkWVAQx8VPtozhvHjUw80rZHAkB55HWHAM3h13axKG0htCt7klhPsZHpx6MH
EPjGlXIT+aW2XiPmK3ZlCDcWIenE+lmtbOpI159Wpk8BGXs/s/xBAkEAlAY3ymgx
63BDJEwvOb2IaP8lDDxNsXx9XJNVvQbv5n15vNsLHbjslHfAhAbxnLQ1fLhUPqSi
nNp/xedE1YxutQ==
-----END PRIVATE KEY-----
KEY
end
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
project.add_role(user, role)
sign_in(user)
project.namespace.update(owner: user)
allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
end
context 'when the page_auto_ssl feature flag is enabled' do
before do
stub_feature_flags(pages_auto_ssl: true)
end
context 'when the auto SSL management is initially disabled' do
let(:domain) do
create(:pages_domain, auto_ssl_enabled: false, project: project)
end
it 'enables auto SSL and dynamically updates the form accordingly', :js do
visit edit_project_pages_domain_path(project, domain)
expect(domain.auto_ssl_enabled).to eq false
expect(find("#pages_domain_auto_ssl_enabled", visible: false).value).to eq 'false'
expect(page).to have_field 'Certificate (PEM)', type: 'textarea'
expect(page).to have_field 'Key (PEM)', type: 'textarea'
find('.js-auto-ssl-toggle-container .project-feature-toggle').click
expect(find("#pages_domain_auto_ssl_enabled", visible: false).value).to eq 'true'
expect(page).not_to have_field 'Certificate (PEM)', type: 'textarea'
expect(page).not_to have_field 'Key (PEM)', type: 'textarea'
expect(page).to have_content "The certificate will be shown here once it has been obtained from Let's Encrypt. This process may take up to an hour to complete."
click_on 'Save Changes'
expect(domain.reload.auto_ssl_enabled).to eq true
end
end
context 'when the auto SSL management is initially enabled' do
let(:domain) do
create(:pages_domain, auto_ssl_enabled: true, project: project)
end
it 'disables auto SSL and dynamically updates the form accordingly', :js do
visit edit_project_pages_domain_path(project, domain)
expect(find("#pages_domain_auto_ssl_enabled", visible: false).value).to eq 'true'
expect(page).to have_field 'Certificate (PEM)', type: 'textarea', disabled: true
expect(page).not_to have_field 'Key (PEM)', type: 'textarea'
find('.js-auto-ssl-toggle-container .project-feature-toggle').click
expect(find("#pages_domain_auto_ssl_enabled", visible: false).value).to eq 'false'
expect(page).to have_field 'Certificate (PEM)', type: 'textarea'
expect(page).to have_field 'Key (PEM)', type: 'textarea'
fill_in 'Certificate (PEM)', with: certificate_pem
fill_in 'Key (PEM)', with: certificate_key
click_on 'Save Changes'
expect(domain.reload.auto_ssl_enabled).to eq false
end
end
end
context 'when the page_auto_ssl feature flag is disabled' do
let(:domain) do
create(:pages_domain, auto_ssl_enabled: false, project: project)
end
before do
stub_feature_flags(pages_auto_ssl: false)
visit edit_project_pages_domain_path(project, domain)
end
it "does not render the Let's Encrypt field", :js do
expect(page).not_to have_selector '.js-auto-ssl-toggle-container'
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Pages' do
shared_examples 'pages domain editing' do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:role) { :maintainer }
......@@ -318,3 +319,21 @@ describe 'Pages' do
end
end
end
describe 'Pages' do
context 'when pages_auto_ssl feature flag is disabled' do
before do
stub_feature_flags(pages_auto_ssl: false)
end
include_examples 'pages domain editing'
end
context 'when pages_auto_ssl feature flag is enabled' do
before do
stub_feature_flags(pages_auto_ssl: true)
end
include_examples 'pages domain editing'
end
end
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