Skip to content
Snippets Groups Projects
Commit 4b3158b6 authored by Linjie Zhang's avatar Linjie Zhang Committed by Drew Blessing
Browse files

Add domain verification operation views

parent 4a43a79e
No related branches found
No related tags found
3 merge requests!122597doc/gitaly: Remove references to removed metrics,!120936Draft: Debugging commit to trigger pipeline (DO NOT MERGE),!116647Add domain verification operation views
Showing
with 319 additions and 29 deletions
......@@ -172,6 +172,10 @@ def keyed_verification_code
"#{VERIFICATION_KEY}=#{verification_code}"
end
def verification_record
"#{verification_domain} TXT #{keyed_verification_code}"
end
def certificate=(certificate)
super(certificate)
......
......@@ -13,4 +13,11 @@ def show_auto_ssl_failed_warning?
::Gitlab::LetsEncrypt.enabled? && auto_ssl_failed
end
def user_defined_certificate?
persisted? &&
certificate.present? &&
certificate_user_provided? &&
errors[:certificate].blank?
end
end
......@@ -13,7 +13,6 @@
%p.form-text.text-muted
= _("To access this domain create a new DNS record")
- if verification_enabled
- verification_record = "#{domain_presenter.verification_domain} TXT #{domain_presenter.keyed_verification_code}"
.form-group.border-section
.row
.col-sm-2
......@@ -24,7 +23,7 @@
= gl_badge_tag text, variant: status
= link_to sprite_icon("redo"), verify_project_pages_domain_path(@project, domain_presenter), method: :post, class: "gl-ml-2 gl-button btn btn-sm btn-default has-tooltip", title: _("Retry verification")
.input-group
= text_field_tag :domain_verification, verification_record, class: "monospace js-select-on-focus form-control", readonly: true
= text_field_tag :domain_verification, domain_presenter.verification_record, class: "monospace js-select-on-focus form-control", readonly: true
.input-group-append
= clipboard_button(target: '#domain_verification', class: 'btn-default d-none d-sm-block')
%p.form-text.text-muted
......
......@@ -41,21 +41,62 @@ The following automated processes use [verified domains](../project/pages/custom
Prerequisites:
- A project with [GitLab Pages](../project/pages/index.md), served under the default Pages domain `*.gitlab.io`.
- A custom domain name `example.com` or subdomain `subdomain.example.com`.
- Access to your domain's server control panel to set up a DNS `TXT` record to verify your domain's ownership.
Setting up a verified domain is similar to [setting up a custom domain on GitLab Pages](../project/pages/custom_domains_ssl_tls_certification/index.md). However, you must:
- Only configure the DNS `TXT` record to verify the domain's ownership.
- Ignore instructions for the `A`, `CNAME`, and `ALIAS` records.
- Link the domain to a project. For more information on group-level domain verification, see [issue 5299](https://gitlab.com/groups/gitlab-org/-/epics/5299).
- Configure the DNS `TXT` record to verify the domain's ownership.
1. [Add a custom domain](../project/pages/custom_domains_ssl_tls_certification/index.md#1-add-a-custom-domain) for the matching email domain.
- The domain must match the email domain exactly. For example, if your email is `username@example.com`, verify the `example.com` domain.
1. [Get a verification code](../project/pages/custom_domains_ssl_tls_certification/index.md#2-get-the-verification-code).
1. [Set up the DNS `TXT`](../project/pages/custom_domains_ssl_tls_certification/index.md#3-set-up-dns-records) for your custom domain.
1. [Verify the domain's ownership](../project/pages/custom_domains_ssl_tls_certification/index.md#4-verify-the-domains-ownership).
1. Optional. [Add more domain aliases](../project/pages/custom_domains_ssl_tls_certification/index.md#add-more-domain-aliases).
Steps:
#### 1. Add a custom domain for the matching email domain
The custom domain must match the email domain exactly. For example, if your email is `username@example.com`, verify the `example.com` domain.
1. On the top bar, select **Main menu > Groups** and find your top group.
1. On the left sidebar, select **Settings > Domain Verification**.
1. In the upper-right corner, select **Add Domain**.
1. In **Domain**, enter the domain name.
1. In **Project**, link to a project.
1. Optional. In **Certificate**, switch the **Manually enter certificate information** toggle to add an SSL/TLS
certificate. You can also add the certificate and key later.
1. Select **Add Domain**.
#### 2. Get a verification code
After you create a new domain, the verification code prompts you. Copy the values from GitLab
and paste them in your domain's control panel as a `TXT` record.
![Get the verification code](../img/get_domain_verification_code_v16_0.png)
#### 3. Verify the domain's ownership
After you have added all the DNS records:
1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Domain Verification**.
1. On the domain table row, Select **Retry verification** (**{retry}**).
![Verify your domain](../img/retry_domain_verification_v16_0.png)
WARNING:
For GitLab instances with domain verification enabled,
if the domain cannot be verified for 7 days, that domain is removed
from the GitLab project.
> **Notes:**
>
> - Domain verification is **required for GitLab.com users**;
for GitLab self-managed instances, your GitLab administrator has the option
to [disabled custom domain verification](../../administration/pages/index.md#custom-domain-verification).
> - [DNS propagation may take some time (up to 24 hours)](https://www.inmotionhosting.com/support/domain-names/dns-nameserver-changes/complete-guide-to-dns-records/),
although it's usually a matter of minutes to complete. Until it does, verification
fails, and attempts to visit your domain result in a 404.
> - Once your domain has been verified, leave the verification record
in place. Your domain is periodically reverified, and may be
disabled if the record is removed.
### View domains in group
......
doc/user/img/get_domain_verification_code_v16_0.png

46.2 KiB

doc/user/img/retry_domain_verification_v16_0.png

22.1 KiB

export function initRemoveButtonBehavior() {
const emptyState = document.querySelector('.js-domain-empty-state');
function removeRowSuccessCallback() {
this.closest('tr').classList.add('gl-display-none!');
const labelsCount = document.querySelectorAll('.js-domain-row:not(.gl-display-none\\!)').length;
if (labelsCount < 1 && emptyState) {
emptyState.classList.remove('gl-display-none');
}
}
document.querySelectorAll('.js-remove-domain').forEach((button) => {
button.addEventListener('ajax:success', removeRowSuccessCallback);
});
}
import { parseBoolean } from '~/lib/utils/common_utils';
import { initProjectSelects } from '~/vue_shared/components/entity_select/init_project_selects';
export const initDomainVerificationForm = () => {
initProjectSelects();
document.querySelectorAll('[name="pages_domain[auto_ssl_enabled]"]').forEach((input) => {
input.addEventListener('change', (event) => {
const isAutoSslEnabled = parseBoolean(event.target.value);
document
.querySelector('.js-shown-unless-auto-ssl')
.classList.toggle('gl-display-none', isAutoSslEnabled);
document
.querySelector('.js-shown-if-auto-ssl')
.classList.toggle('gl-display-none', !isAutoSslEnabled);
});
});
};
import { initRemoveButtonBehavior } from 'ee/domain_verification';
initRemoveButtonBehavior();
import { initDomainVerificationForm } from 'ee/pages/groups/settings/domain_verification/form';
initDomainVerificationForm();
import { initDomainVerificationForm } from 'ee/pages/groups/settings/domain_verification/form';
initDomainVerificationForm();
# frozen_string_literal: true
module Groups
module DomainVerificationHelper
def can_add_group_domain?(group)
Feature.enabled?(:domain_verification_operation, group) &&
(Gitlab.config.pages.external_http || Gitlab.config.pages.external_https)
end
def can_verify_group_domain?(domain)
domain.persisted? && Gitlab::CurrentSettings.pages_domain_verification_enabled?
end
def can_add_group_domain_custom_certificate?
Gitlab.config.pages.external_https
end
def group_domain_auto_ssl_available?
::Gitlab::LetsEncrypt.enabled?
end
end
end
- auto_ssl_available_and_enabled = group_domain_auto_ssl_available? && domain_presenter.auto_ssl_enabled?
- if group_domain_auto_ssl_available?
.form-group.gl-form-group
%label{ for: "pages_domain_auto_ssl_enabled_button" }
= _('Certificate')
%p.gl-text-secondary.gl-mt-1
- docs_link_url = help_page_path("user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md")
- 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 }
- 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
= f.gitlab_ui_radio_component :auto_ssl_enabled,
true,
_("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 },
radio_options: { checked: auto_ssl_available_and_enabled }
= f.gitlab_ui_radio_component :auto_ssl_enabled,
false,
s_("DomainVerification|Manually enter certificate information"),
radio_options: { checked: !auto_ssl_available_and_enabled }
.js-shown-unless-auto-ssl.custom-control{ class: ("gl-display-none" if auto_ssl_available_and_enabled) }
- if domain_presenter.user_defined_certificate?
.form-group.gl-form-group.gl-max-w-80
= render Pajamas::CardComponent.new(body_options: { class: 'gl-display-flex gl-align-items-center gl-justify-content-space-between gl-p-5' }) do |c|
- c.header do
= s_('Certificate')
- c.body do
%span
= domain_presenter.pages_domain.subject || _('missing')
= link_to _('Remove'),
clean_certificate_group_settings_domain_verification_path(@group, domain_presenter),
data: { confirm: _('Are you sure?'), 'confirm-btn-variant': 'danger' },
'aria-label': s_("DomainVerification|Remove certificate"),
class: 'gl-button btn btn-danger btn-sm',
method: :delete
- else
.form-group.gl-form-group.gl-max-w-80
= f.label :user_provided_certificate, _("Certificate (PEM)")
= f.text_area :user_provided_certificate,
rows: 5,
class: "form-control"
%p.help-inline.text-muted= _("Upload a certificate for your domain with all intermediates")
.form-group.gl-form-group.gl-max-w-80
= f.label :user_provided_key, _("Key (PEM)")
= f.text_area :user_provided_key,
rows: 5,
class: "form-control"
%p.help-inline.text-muted= _("Upload a private key for your certificate")
= render 'lets_encrypt_callout', auto_ssl_available_and_enabled: auto_ssl_available_and_enabled
.form-group
%label
= _("TXT")
.input-group.gl-max-w-80
= text_field_tag :domain_verification, domain_presenter.verification_record, class: "monospace js-select-on-focus form-control", readonly: true
.input-group-append
= clipboard_button(target: '#domain_verification', class: 'btn-default d-none d-sm-block')
%p.form-text.text-muted
- link_to_help = link_to(s_('DomainVerification|How to set up DNS records?'), help_page_path('user/project/pages/custom_domains_ssl_tls_certification/index.md', anchor: '3-set-up-dns-records'))
= s_("DomainVerification|To verify ownership of your domain, add the above key to a TXT record within your DNS configuration. %{link_to_help}").html_safe % { link_to_help: link_to_help }
- if domain_presenter.errors.any?
= render Pajamas::AlertComponent.new(variant: :danger, dismissible: false) do |c|
= c.body do
- domain_presenter.errors.full_messages.each do |msg|
= msg
.form-group.gl-form-group.gl-max-w-80
= f.label :domain, _("Domain")
= f.text_field :domain, required: true, placeholder: s_("DomainVerification|Enter your domain"), autocomplete: "off", class: "form-control", disabled: domain_presenter.persisted?
.form-group.gl-form-group.gl-max-w-80
- if domain_presenter.persisted?
= f.label :project_id, _("Project")
= f.text_field :project_id, value: domain_presenter.project.full_path, class: "form-control", disabled: true
- else
.js-vue-project-select{ data: {
label: _('Project'),
input_name: 'pages_domain[project_id]',
input_id: 'pages_domain_project_id',
group_id: @group.id,
order_by: 'last_activity_at',
with_shared: false.to_s,
include_subgroups: true.to_s,
clearable: true.to_s,
block: true.to_s,
selected: domain_presenter.project_id } }
- if can_verify_group_domain?(domain_presenter)
= render 'dns'
- if can_add_group_domain_custom_certificate?
= render 'certificate', f: f
- 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/enterprise_user/index.md", anchor: "verified-domains-for-groups")
- 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
%p= _("%{docs_link_start}Setting up a verified domain%{docs_link_end} requires being linked to a project.").html_safe % { docs_link_url: docs_link_url, docs_link_start: docs_link_start, docs_link_end: docs_link_end }
- if domain_presenter.enabled?
- if domain_presenter.auto_ssl_enabled
- if domain_presenter.show_auto_ssl_failed_warning?
.form-group.gl-form-group.js-shown-if-auto-ssl{ class: ("gl-display-none" unless auto_ssl_available_and_enabled) }
.bs-callout.bs-callout-warning.gl-mt-0
.row.align-items-center.gl-mx-2
= sprite_icon('warning-solid', css_class: 'gl-mr-2 gl-text-orange-600')
= _("Something went wrong while obtaining the Let's Encrypt certificate.")
.row.gl-mx-0.gl-mt-3
= link_to _('Retry'), retry_auto_ssl_group_settings_domain_verification_path(group, domain_presenter), class: "gl-button btn btn-default btn-sm btn-grouped", method: :post
- elsif !domain_presenter.certificate_gitlab_provided?
.form-group.gl-form-group.js-shown-if-auto-ssl{ class: ("gl-display-none" unless auto_ssl_available_and_enabled) }
.bs-callout.bs-callout-info.gl-mt-0
= _("GitLab is obtaining a Let's Encrypt SSL certificate for this domain. This process can take some time. Please try again later.")
- else
.form-group.gl-form-group.js-shown-if-auto-ssl{ class: ("gl-display-none" unless auto_ssl_available_and_enabled) }
.bs-callout.bs-callout-warning.gl-mt-0
= _("A Let's Encrypt SSL certificate can not be obtained until your domain is verified.")
......@@ -2,28 +2,59 @@
%h1.page-title.gl-font-size-h-display
= _('Domain Verification')
- if can_add_group_domain?(@group)
= render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { class: 'float-right'}, href: new_group_settings_domain_verification_path(@group)) do
= s_("DomainVerification|Add Domain")
%p
= s_('DomainVerification|The following domains are configured for projects in this group. Users with email addresses that match a verified domain do not need to confirm their account.')
= link_to s_('DomainVerification|How do I configure a domain?'), help_page_path('user/project/pages/custom_domains_ssl_tls_certification/index', anchor: 'steps')
= link_to s_('DomainVerification|How do I configure a domain?'), help_page_path('user/enterprise_user/index.md', anchor: 'verified-domains-for-groups')
%table.gl-table.gl-w-full
%thead
%tr
%th= _('Domain')
%th= _('Project')
%tbody
- if @domains.empty?
- if Feature.enabled?(:domain_verification_operation, @group)
%table.gl-table.gl-w-full
%thead
%tr
%td{ colspan: 2, class: 'gl-py-6! text-center' }
= s_('DomainVerification|No domains configured. Create a domain in a project in this group hierarchy.')
- @domains.each do |domain|
%th= _('Domain')
%th= _('Source Project')
%th= _('Verification status')
%th= _('Actions')
%tbody
%tr.js-domain-empty-state{ class: @domains.empty? ? '' : 'gl-display-none' }
%td{ colspan: 4, class: 'gl-py-6! text-center' }
= s_("DomainVerification|No domains configured. Create a domain in a project in this group hierarchy.")
- @domains.each do |domain|
%tr.js-domain-row{ id: "domain#{domain.id}" }
%td
= domain.domain
%td
- project = domain.project
= link_to project.full_path, project_path(project)
%td
= domain.verified? ? _('Verified') : _('Unverified')
%td
.btn-group
- if can_verify_group_domain?(domain)
= render Pajamas::ButtonComponent.new(icon: "redo", href: verify_group_settings_domain_verification_path(@group, domain), button_options: { data: { method: :post } })
= render Pajamas::ButtonComponent.new(icon: "pencil", href: group_settings_domain_verification_path(@group, domain))
= render Pajamas::ButtonComponent.new(variant: :danger, icon: "remove", href: group_settings_domain_verification_path(@group, domain), button_options: { data: { confirm: s_("DomainVerification|Are you sure you want to delete this domain?"), confirm_btn_variant: "danger", remote: true, method: :delete, title: s_("DomainVerification|Delete domain") }, 'aria-label': s_("DomainVerification|Delete domain"), class: "js-remove-domain" })
- else
%table.gl-table.gl-w-full
%thead
%tr
%td.gl-lg-w-30p{ id: "domain#{domain.id}" }
%th= _('Domain')
%th= _('Project')
%tbody
- if @domains.empty?
%tr
%td{ colspan: 2, class: 'gl-py-6! text-center' }
= s_('DomainVerification|No domains configured. Create a domain in a project in this group hierarchy.')
- @domains.each do |domain|
%tr
%td.gl-lg-w-30p{ id: "domain#{domain.id}" }
= link_to domain.domain, project_pages_domain_path(domain.project, domain.domain)
- text, status = domain.unverified? ? [_('Unverified'), :danger] : [_('Verified'), :success]
= gl_badge_tag text, variant: status, size: :sm
%td
- project = domain.project
= link_to project.full_path, project_path(project)
= link_to domain.domain, project_pages_domain_path(domain.project, domain.domain)
- text, status = domain.unverified? ? [_('Unverified'), :danger] : [_('Verified'), :success]
= gl_badge_tag text, variant: status, size: :sm
%td
- project = domain.project
= link_to project.full_path, project_path(project)
- add_to_breadcrumbs _("Domain Verification"), group_settings_domain_verification_index_path(@group)
- page_title s_("DomainVerification|Add Domain")
%h1.page-title.gl-font-size-h-display
= s_("DomainVerification|New Domain")
= render 'groups/settings/domain_verification/helper_text'
%div
= gitlab_ui_form_for domain_presenter, url: group_settings_domain_verification_index_path(@group), html: { name: "domainForm", class: 'fieldset-form' } do |f|
= render 'form', { f: f }
= f.submit s_("DomainVerification|Add Domain"), class: 'gl-mr-3', pajamas_button: true
= link_to _('Cancel'), group_settings_domain_verification_index_path(@group), class: 'gl-button btn btn-default btn-cancel'
- add_to_breadcrumbs _("Domain Verification"), group_settings_domain_verification_index_path(@group)
- breadcrumb_title s_("DomainVerification|Edit Domain")
- page_title domain_presenter.domain
%h1.page-title.gl-font-size-h-display
= s_("DomainVerification|Edit Domain")
= render 'groups/settings/domain_verification/helper_text'
%div
= gitlab_ui_form_for domain_presenter, url: group_settings_domain_verification_path(@group, domain_presenter), html: { name: "domain-form", class: 'fieldset-form' } do |f|
= render 'form', { f: f }
= f.submit _('Save Changes'), class: 'gl-mr-3', pajamas_button: true
= link_to _('Cancel'), group_settings_domain_verification_index_path(@group), class: 'gl-button btn btn-default btn-cancel'
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment