Commit 3b8c3850 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'pages-cname' into 'master'

Added support for configuring Pages CNAME and TLS Certificates

Preliminary implementation with all expected features that will ship with this feature.

What is supported:

- Multiple domains per-project
- One certificate per-domain
- Validation of certificate
- Validation of certificate chain (solves a problem with missing intermediates)
- Validation of private key against certificate
- Support for Pages Daemon (the shared/pages/group/project/config.json) that will be read by daemon

Plans for enhancements:

- [x] Write behavior and unit tests
- [x] Make the UI nicer

/cc @grzesiek @DouweM @sytses @rspeicher @marin @jacobvosmaer

See merge request !173
parents e405b8f3 787d4f91
......@@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.5.0 (unreleased)
- Show warning when mirror repository default branch could not be updated because it has diverged from upstream.
- More reliable wiki indexer
- GitLab Pages gets support for custom domain and custom certificate
v 8.4.4
- Re-introduce "Send email to users" link in Admin area
......
......@@ -46,6 +46,9 @@ gem 'devise-two-factor', '~> 2.0.0'
gem 'rqrcode-rails3', '~> 0.1.7'
gem 'attr_encrypted', '~> 1.3.4'
# GitLab Pages
gem 'validates_hostname', '~> 1.0.0'
# Browser detection
gem "browser", '~> 1.0.0'
......
......@@ -867,6 +867,9 @@ GEM
uniform_notifier (1.9.0)
uuid (2.3.8)
macaddr (~> 1.0)
validates_hostname (1.0.5)
activerecord (>= 3.0)
activesupport (>= 3.0)
version_sorter (2.0.0)
virtus (1.0.5)
axiom-types (~> 0.1)
......@@ -1066,6 +1069,7 @@ DEPENDENCIES
unf (~> 0.1.4)
unicorn (~> 4.8.2)
unicorn-worker-killer (~> 0.4.2)
validates_hostname (~> 1.0.0)
version_sorter (~> 2.0.0)
virtus (~> 1.0.1)
web-console (~> 2.0)
......
class Projects::PagesController < Projects::ApplicationController
layout 'project_settings'
before_action :authorize_update_pages!
def show
@domains = @project.pages_domains.order(:domain)
end
def destroy
project.remove_pages
project.pages_domains.destroy_all
respond_to do |format|
format.html do
redirect_to(namespace_project_pages_path(@project.namespace, @project),
notice: 'Pages were removed')
end
end
end
end
class Projects::PagesDomainsController < Projects::ApplicationController
layout 'project_settings'
before_action :authorize_update_pages!, except: [:show]
before_action :domain, only: [:show, :destroy]
def show
end
def new
@domain = @project.pages_domains.new
end
def create
@domain = @project.pages_domains.create(pages_domain_params)
if @domain.valid?
redirect_to namespace_project_pages_path(@project.namespace, @project)
else
render 'new'
end
end
def destroy
@domain.destroy
respond_to do |format|
format.html do
redirect_to(namespace_project_pages_path(@project.namespace, @project),
notice: 'Domain was removed')
end
format.js
end
end
private
def pages_domain_params
params.require(:pages_domain).permit(
:certificate,
:key,
:domain
)
end
def domain
@domain ||= @project.pages_domains.find_by(domain: params[:id].to_s)
end
end
......@@ -171,16 +171,6 @@ class ProjectsController < ApplicationController
end
end
def remove_pages
return access_denied! unless can?(current_user, :remove_pages, @project)
@project.remove_pages
respond_to do |format|
format.html { redirect_to project_path(@project) }
end
end
def housekeeping
::Projects::HousekeepingService.new(@project).execute
......
......@@ -245,6 +245,7 @@ class Ability
:change_visibility_level,
:rename_project,
:remove_project,
:update_pages,
:remove_pages,
:archive_project,
:remove_fork_project
......
class PagesDomain < ActiveRecord::Base
belongs_to :project
validates :domain, hostname: true
validates_uniqueness_of :domain, case_sensitive: false
validates :certificate, certificate: true, allow_nil: true, allow_blank: true
validates :key, certificate_key: true, allow_nil: true, allow_blank: true
validate :validate_pages_domain
validate :validate_matching_key, if: ->(domain) { domain.certificate.present? || domain.key.present? }
validate :validate_intermediates, if: ->(domain) { domain.certificate.present? }
attr_encrypted :key, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base
after_create :update
after_save :update
after_destroy :update
def to_param
domain
end
def url
return unless domain
if certificate
"https://#{domain}"
else
"http://#{domain}"
end
end
def has_matching_key?
return false unless x509
return false unless pkey
# We compare the public key stored in certificate with public key from certificate key
x509.check_private_key(pkey)
end
def has_intermediates?
return false unless x509
# self-signed certificates doesn't have the certificate chain
return true if x509.verify(x509.public_key)
store = OpenSSL::X509::Store.new
store.set_default_paths
# This forces to load all intermediate certificates stored in `certificate`
Tempfile.open('certificate_chain') do |f|
f.write(certificate)
f.flush
store.add_file(f.path)
end
store.verify(x509)
rescue OpenSSL::X509::StoreError
false
end
def expired?
return false unless x509
current = Time.new
current < x509.not_before || x509.not_after < current
end
def subject
return unless x509
x509.subject.to_s
end
def certificate_text
@certificate_text ||= x509.try(:to_text)
end
private
def update
::Projects::UpdatePagesConfigurationService.new(project).execute
end
def validate_matching_key
unless has_matching_key?
self.errors.add(:key, "doesn't match the certificate")
end
end
def validate_intermediates
unless has_intermediates?
self.errors.add(:certificate, 'misses intermediates')
end
end
def validate_pages_domain
return unless domain
if domain.downcase.ends_with?(".#{Settings.pages.host}".downcase)
self.errors.add(:domain, "*.#{Settings.pages.host} is restricted")
end
end
def x509
return unless certificate
@x509 ||= OpenSSL::X509::Certificate.new(certificate)
rescue OpenSSL::X509::CertificateError
nil
end
def pkey
return unless key
@pkey ||= OpenSSL::PKey::RSA.new(key)
rescue OpenSSL::PKey::PKeyError, OpenSSL::Cipher::CipherError
nil
end
end
......@@ -162,6 +162,7 @@ class Project < ActiveRecord::Base
has_many :lfs_objects, through: :lfs_objects_projects
has_many :project_group_links, dependent: :destroy
has_many :invited_groups, through: :project_group_links, source: :group
has_many :pages_domains, dependent: :destroy
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
......@@ -1046,7 +1047,8 @@ class Project < ActiveRecord::Base
end
def pages_url
if Dir.exist?(public_pages_path)
return unless Dir.exist?(public_pages_path)
host = "#{namespace.path}.#{Settings.pages.host}"
url = Gitlab.config.pages.url.sub(/^https?:\/\//) do |prefix|
"#{prefix}#{namespace.path}."
......@@ -1057,7 +1059,6 @@ class Project < ActiveRecord::Base
"#{url}/#{path}"
end
end
def pages_path
File.join(Settings.pages.path, path_with_namespace)
......@@ -1071,7 +1072,7 @@ class Project < ActiveRecord::Base
# 1. We rename pages to temporary directory
# 2. We wait 5 minutes, due to NFS caching
# 3. We asynchronously remove pages with force
temp_path = "#{path}.#{SecureRandom.hex}"
temp_path = "#{path}.#{SecureRandom.hex}.deleted"
if Gitlab::PagesTransfer.new.rename_project(path, temp_path, namespace.path)
PagesWorker.perform_in(5.minutes, :remove, namespace.path, temp_path)
......
module Projects
class UpdatePagesConfigurationService < BaseService
attr_reader :project
def initialize(project)
@project = project
end
def execute
update_file(pages_config_file, pages_config.to_json)
reload_daemon
success
rescue => e
error(e.message)
end
private
def pages_config
{
domains: pages_domains_config
}
end
def pages_domains_config
project.pages_domains.map do |domain|
{
domain: domain.domain,
certificate: domain.certificate,
key: domain.key,
}
end
end
def reload_daemon
# GitLab Pages daemon constantly watches for modification time of `pages.path`
# It reloads configuration when `pages.path` is modified
update_file(pages_update_file, SecureRandom.hex(64))
end
def pages_path
@pages_path ||= project.pages_path
end
def pages_config_file
File.join(pages_path, 'config.json')
end
def pages_update_file
File.join(Settings.pages.path, '.update')
end
def update_file(file, data)
unless data
FileUtils.remove(file, force: true)
return
end
temp_file = "#{file}.#{SecureRandom.hex(16)}"
File.open(temp_file, 'w') do |f|
f.write(data)
end
FileUtils.move(temp_file, file, force: true)
ensure
# In case if the updating fails
FileUtils.remove(temp_file, force: true)
end
end
end
# UrlValidator
#
# Custom validator for private keys.
#
# class Project < ActiveRecord::Base
# validates :certificate_key, certificate_key: true
# end
#
class CertificateKeyValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless valid_private_key_pem?(value)
record.errors.add(attribute, "must be a valid PEM private key")
end
end
private
def valid_private_key_pem?(value)
return false unless value
pkey = OpenSSL::PKey::RSA.new(value)
pkey.private?
rescue OpenSSL::PKey::PKeyError
false
end
end
# UrlValidator
#
# Custom validator for private keys.
#
# class Project < ActiveRecord::Base
# validates :certificate_key, certificate: true
# end
#
class CertificateValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless valid_certificate_pem?(value)
record.errors.add(attribute, "must be a valid PEM certificate")
end
end
private
def valid_certificate_pem?(value)
return false unless value
OpenSSL::X509::Certificate.new(value).present?
rescue OpenSSL::X509::CertificateError
false
end
end
......@@ -49,6 +49,11 @@
= icon('clone fw')
%span
Mirror Repository
= nav_link(controller: :pages) do
= link_to namespace_project_pages_path(@project.namespace, @project), title: 'Pages', data: {placement: 'right'} do
= icon('cloud-upload fw')
%span
Pages
= nav_link(controller: :audit_events) do
= link_to namespace_project_audit_events_path(@project.namespace, @project) do
= icon('file-text-o fw')
......
......@@ -186,47 +186,6 @@
.form-actions
= f.submit 'Save changes', class: "btn btn-save"
- if Settings.pages.enabled
.pages-settings
.panel.panel-default
.panel-heading Pages
.errors-holder
.panel-body
- if @project.pages_url
%strong
Congratulations! Your pages are served at:
%p= link_to @project.pages_url, @project.pages_url
- else
%p
Learn how to upload your static site and have it served by
GitLab by following the #{link_to "documentation on GitLab Pages", "http://doc.gitlab.com/ee/pages/README.html", target: :blank}.
%p
In the example below we define a special job named
%code pages
which is using Jekyll to build a static site. The generated
HTML will be stored in the
%code public/
directory which will then be archived and uploaded to GitLab.
The name of the directory should not be different than
%code public/
in order for the pages to work.
%ul
%li
%pre
:plain
pages:
image: jekyll/jekyll
script: jekyll build -d public/
artifacts:
paths:
- public/
- if @project.pages_url && can?(current_user, :remove_pages, @project)
.form-actions
= link_to 'Remove pages', remove_pages_namespace_project_path(@project.namespace, @project),
data: { confirm: "Are you sure that you want to remove pages for this project?" },
method: :post, class: "btn btn-warning"
.danger-settings
.panel.panel-default
.panel-heading Housekeeping
......
- if @project.pages_url
.panel.panel-default
.panel-heading
Access pages
.panel-body
%p
%strong
Congratulations! Your pages are served under:
%p= link_to @project.pages_url, @project.pages_url
- @project.pages_domains.each do |domain|
%p= link_to domain.url, domain.url
- if can?(current_user, :remove_pages, @project) && @project.pages_url
.panel.panel-default.panel.panel-danger
.panel-heading Remove pages
.errors-holder
.panel-body
%p
Removing the pages will prevent from exposing them to outside world.
.form-actions
= link_to 'Remove pages', namespace_project_pages_path(@project.namespace, @project), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove"
.panel.panel-default
.nothing-here-block
GitLab Pages are disabled.
Ask your system's administrator to enable it.
- if @domains.any?
.panel.panel-default
.panel-heading
Domains (#{@domains.count})
%ul.well-list
- @domains.each do |domain|
%li
.pull-right
= link_to 'Details', namespace_project_pages_domain_path(@project.namespace, @project, domain), class: "btn btn-sm btn-grouped"
= link_to 'Remove', namespace_project_pages_domain_path(@project.namespace, @project, domain), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped"
.clearfix
%span= link_to domain.domain, domain.url
%p
- if domain.subject
%span.label.label-gray Certificate: #{domain.subject}
- if domain.expired?
%span.label.label-danger Expired
.panel.panel-default
.panel-heading
Domains
.nothing-here-block
Support for domains and certificates is disabled.
Ask your system's administrator to enable it.
- unless @project.pages_url
.panel.panel-info
.panel-heading
Configure pages
.panel-body
%p
Learn how to upload your static site and have it served by
GitLab by following the #{link_to "documentation on GitLab Pages", "http://doc.gitlab.com/ee/pages/README.html", target: :blank}.
- page_title 'Pages'
%h3.page_title
Pages
- if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https
= link_to new_namespace_project_pages_domain_path(@project.namespace, @project), class: 'btn btn-new pull-right', title: 'New Domain' do
%i.fa.fa-plus
New Domain
%p.light
With GitLab Pages you can host your static websites on GitLab.
Combined with the power of GitLab CI and the help of GitLab Runner
you can deploy static pages for your individual projects, your user or your group.
%hr.clearfix
- if Gitlab.config.pages.enabled
= render 'access'
= render 'use'
- if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https
= render 'list'
- else
= render 'no_domains'
= render 'destroy'
- else
= render 'disabled'
= form_for [@project.namespace, @project, @domain], html: { class: 'form-horizontal fieldset-form' } do |f|
- if @domain.errors.any?
#error_explanation
.alert.alert-danger
- @domain.errors.full_messages.each do |msg|
%p= msg
.form-group
= f.label :domain, class: 'control-label' do
Domain
.col-sm-10
= f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control'
- if Gitlab.config.pages.external_https
.form-group
= f.label :certificate, class: 'control-label' do
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
.form-group
= f.label :key, class: 'control-label' do
Key (PEM)
.col-sm-10
= f.text_area :key, rows: 5, class: 'form-control'
%span.help-inline 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.
.form-actions
= f.submit 'Create New Domain', class: "btn btn-save"
- page_title 'New Pages Domain'
%h3.page_title
New Pages Domain
%hr.clearfix
%div
= render 'form'
- page_title "#{@domain.domain}", 'Pages Domains'
%h3.page-title
Pages Domain
.table-holder
%table.table
%tr
%td
Domain
%td
= link_to @domain.domain, @domain.url
%tr
%td
DNS
%td
%p
To access the domain create a new DNS record:
%pre
#{@domain.domain} CNAME #{@domain.project.namespace.path}.#{Settings.pages.host}.
%tr
%td
Certificate
%td
- if @domain.certificate_text
%pre
= @domain.certificate_text
- else
.light
missing
......@@ -9,7 +9,11 @@ class PagesWorker
def deploy(build_id)
build = Ci::Build.find_by(id: build_id)
Projects::UpdatePagesService.new(build.project, build).execute
result = Projects::UpdatePagesService.new(build.project, build).execute
if result[:status] == :success
result = Projects::UpdatePagesConfigurationService.new(build.project).execute
end
result
end
def remove(namespace_path, project_path)
......
......@@ -160,6 +160,8 @@ production: &base
host: example.com
port: 80 # Set to 443 if you serve the pages with HTTPS
https: false # Set to true if you serve the pages with HTTPS
# external_http: "1.1.1.1:80" # If defined, enables custom domain support in GitLab Pages
# external_https: "1.1.1.1:443" # If defined, enables custom domain and certificate support in GitLab Pages
## Gravatar
## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html
......
......@@ -299,6 +299,8 @@ Settings.pages['https'] = false if Settings.pages['https'].nil?
Settings.pages['port'] ||= Settings.pages.https ? 443 : 80
Settings.pages['protocol'] ||= Settings.pages.https ? "https" : "http"
Settings.pages['url'] ||= Settings.send(:build_pages_url)
Settings.pages['external_http'] ||= false if Settings.pages['external_http'].nil?
Settings.pages['external_https'] ||= false if Settings.pages['external_https'].nil?
#
# Git LFS
......
......@@ -428,7 +428,6 @@ Rails.application.routes.draw do
delete :remove_fork
post :archive
post :unarchive
post :remove_pages
post :housekeeping
post :toggle_star
post :markdown_preview
......@@ -547,6 +546,10 @@ Rails.application.routes.draw do
end
end
resource :pages, only: [:show, :destroy] do
resources :domains, only: [:show, :new, :create, :destroy], controller: 'pages_domains'
end
resources :compare, only: [:index, :create]
resources :network, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ }
......
class CreatePagesDomain < ActiveRecord::Migration
def change
create_table :pages_domains do |t|
t.integer :project_id
t.text :certificate
t.text :encrypted_key
t.string :encrypted_key_iv
t.string :encrypted_key_salt
t.string :domain
end
add_index :pages_domains, :domain, unique: true
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160209130428) do
ActiveRecord::Schema.define(version: 20160210105555) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -734,6 +734,17 @@ ActiveRecord::Schema.define(version: 20160209130428) do
add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree
add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
create_table "pages_domains", force: :cascade do |t|
t.integer "project_id"
t.text "certificate"
t.text "encrypted_key"
t.string "encrypted_key_iv"
t.string "encrypted_key_salt"
t.string "domain"
end
add_index "pages_domains", ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree
create_table "project_group_links", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "group_id", null: false
......
......@@ -70,6 +70,13 @@ Feature: Project Active Tab
And no other sub navs should be active
And the active main tab should be Settings
Scenario: On Project Settings/Pages
Given I visit my project's settings page
And I click the "Pages" tab
Then the active sub nav should be Pages
And no other sub navs should be active
And the active main tab should be Settings
# Sub Tabs: Commits
Scenario: On Project Commits/Commits
......
Feature: Project Pages
Background:
Given I sign in as a user
And I own a project
Scenario: Pages are disabled
Given pages are disabled
When I visit the Project Pages
Then I should see that GitLab Pages are disabled
Scenario: I can see the pages usage if not deployed
Given pages are enabled
When I visit the Project Pages
Then I should see the usage of GitLab Pages
Scenario: I can access the pages if deployed
Given pages are enabled
And pages are deployed
When I visit the Project Pages
Then I should be able to access the Pages
Scenario: I should message that domains support is disabled
Given pages are enabled
And pages are deployed
And support for external domains is disabled
When I visit the Project Pages
Then I should see that support for domains is disabled
Scenario: I should see a new domain button
Given pages are enabled
And pages are exposed on external HTTP address
When I visit the Project Pages
And I should be able to add a New Domain
Scenario: I should be able to add a new domain
Given pages are enabled
And pages are exposed on external HTTP address
When I visit add a new Pages Domain
And I fill the domain
And I click on "Create New Domain"
Then I should see a new domain added
Scenario: I should be denied to add the same domain twice
Given pages are enabled
And pages are exposed on external HTTP address
And pages domain is added
When I visit add a new Pages Domain
And I fill the domain
And I click on "Create New Domain"
Then I should see error message that domain already exists
Scenario: I should message that certificates support is disabled when trying to add a new domain
Given pages are enabled
And pages are exposed on external HTTP address
And pages domain is added
When I visit add a new Pages Domain
Then I should see that support for certificates is disabled
Scenario: I should be able to add a new domain with certificate
Given pages are enabled
And pages are exposed on external HTTPS address
When I visit add a new Pages Domain
And I fill the domain
And I fill the certificate and key
And I click on "Create New Domain"
Then I should see a new domain added
Scenario: I can remove the pages if deployed
Given pages are enabled
And pages are deployed
When I visit the Project Pages
And I click Remove Pages
Then The Pages should get removed
......@@ -33,6 +33,10 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
click_link('Deploy Keys')
end
step 'I click the "Pages" tab' do
click_link('Pages')
end
step 'the active sub nav should be Team' do
ensure_active_sub_nav('Members')
end
......@@ -49,6 +53,10 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
ensure_active_sub_nav('Deploy Keys')
end
step 'the active sub nav should be Pages' do
ensure_active_sub_nav('Pages')
end
# Sub Tabs: Commits
step 'I click the "Compare" tab' do
......
class Spinach::Features::ProjectPages < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedProject
step 'pages are enabled' do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
allow(Gitlab.config.pages).to receive(:host).and_return('example.com')
allow(Gitlab.config.pages).to receive(:port).and_return(80)
allow(Gitlab.config.pages).to receive(:https).and_return(false)
end
step 'pages are disabled' do
allow(Gitlab.config.pages).to receive(:enabled).and_return(false)
end
step 'I visit the Project Pages' do
visit namespace_project_pages_path(@project.namespace, @project)
end
step 'I should see that GitLab Pages are disabled' do
expect(page).to have_content('GitLab Pages are disabled')
end
step 'I should see the usage of GitLab Pages' do
expect(page).to have_content('Configure pages')
end
step 'pages are deployed' do
commit = @project.ensure_ci_commit(@project.commit('HEAD').sha)
build = build(:ci_build,
project: @project,
commit: commit,
ref: 'HEAD',
artifacts_file: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip'),
artifacts_metadata: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip.meta')
)
result = ::Projects::UpdatePagesService.new(@project, build).execute
expect(result[:status]).to eq(:success)
end
step 'I should be able to access the Pages' do
expect(page).to have_content('Access pages')
end
step 'I should see that support for domains is disabled' do
expect(page).to have_content('Support for domains and certificates is disabled')
end
step 'support for external domains is disabled' do
allow(Gitlab.config.pages).to receive(:external_http).and_return(nil)
allow(Gitlab.config.pages).to receive(:external_https).and_return(nil)
end
step 'pages are exposed on external HTTP address' do
allow(Gitlab.config.pages).to receive(:external_http).and_return('1.1.1.1:80')
allow(Gitlab.config.pages).to receive(:external_https).and_return(nil)
end
step 'pages are exposed on external HTTPS address' do
allow(Gitlab.config.pages).to receive(:external_http).and_return('1.1.1.1:80')
allow(Gitlab.config.pages).to receive(:external_https).and_return('1.1.1.1:443')
end
step 'I should be able to add a New Domain' do
expect(page).to have_content('New Domain')
end
step 'I visit add a new Pages Domain' do
visit new_namespace_project_pages_domain_path(@project.namespace, @project)
end
step 'I fill the domain' do
fill_in 'Domain', with: 'my.test.domain.com'
end
step 'I click on "Create New Domain"' do
click_button 'Create New Domain'
end
step 'I should see a new domain added' do
expect(page).to have_content('Domains (1)')
expect(page).to have_content('my.test.domain.com')
end
step 'pages domain is added' do
@project.pages_domains.create!(domain: 'my.test.domain.com')
end
step 'I should see error message that domain already exists' do
expect(page).to have_content('Domain has already been taken')
end
step 'I should see that support for certificates is disabled' do
expect(page).to have_content('Support for custom certificates is disabled')
end
step 'I fill the certificate and key' do
fill_in 'Certificate (PEM)', with: '-----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-----'
fill_in 'Key (PEM)', with: '-----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-----'
end
step 'I click Remove Pages' do
click_link 'Remove pages'
end
step 'The Pages should get removed' do
expect(@project.pages_url).to be_nil
end
end
FactoryGirl.define do
factory :pages_domain, class: 'PagesDomain' do
domain 'my.domain.com'
trait :with_certificate do
certificate '-----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-----'
end
trait :with_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-----'
end
trait :with_missing_chain do
# This certificate is signed with different key
# And misses the CA to build trust chain
certificate '-----BEGIN CERTIFICATE-----
MIIDGTCCAgGgAwIBAgIBAjANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQDEwdUZXN0
IENBMB4XDTE2MDIxMjE0MjMwMFoXDTE3MDIxMTE0MjMwMFowHTEbMBkGA1UEAxMS
dGVzdC1jZXJ0aWZpY2F0ZS0yMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAw8RWetIUT0YymSuKvBpClzDv/jQdX0Ch+2iF7f4Lm3lcmoUuXgyhl/WRe5K9
ONuMHPQlZbeavEbvWb0BsU7geInhsjd/zAu3EP17jfSIXToUdSD20wcSG/yclLdZ
qhb6NCtHTJKFUI8BktoS7kafkdvmeem/UJFzlvcA6VMyGDkS8ZN39a45R1jGmPEl
Yk0g1jW7lSKcBLjU1O/Csv59LyWXqBP6jR1vB8ijlUf1IyK8gOk7NHF13GHl7Z3A
/8zwuEt/pB3yK92o71P+FnSEcJ23zcAalz6H9ajVTzRr/AXttineBNVYnEuPXW+V
Rsboe+bBO/e4pVKXnQ1F3aMT7QIDAQABo28wbTAMBgNVHRMBAf8EAjAAMB0GA1Ud
DgQWBBSFwo3rhc26lD8ZVaBVcUY1NyCOLDALBgNVHQ8EBAMCBeAwEQYJYIZIAYb4
QgEBBAQDAgZAMB4GCWCGSAGG+EIBDQQRFg94Y2EgY2VydGlmaWNhdGUwDQYJKoZI
hvcNAQEFBQADggEBABppUhunuT7qArM9gZ2gLgcOK8qyZWU8AJulvloaCZDvqGVs
Qom0iEMBrrt5+8bBevNiB49Tz7ok8NFgLzrlEnOw6y6QGjiI/g8sRKEiXl+ZNX8h
s8VN6arqT348OU8h2BixaXDmBF/IqZVApGhR8+B4fkCt0VQmdzVuHGbOQXMWJCpl
WlU8raZoPIqf6H/8JA97pM/nk/3CqCoHsouSQv+jGY4pSL22RqsO0ylIM0LDBbmF
m4AEaojTljX1tMJAF9Rbiw/omam5bDPq2JWtosrz/zB69y5FaQjc6FnCk0M4oN/+
VM+d42lQAgoq318A84Xu5vRh1KCAJuztkhNbM+w=
-----END CERTIFICATE-----'
end
trait :with_trusted_chain do
# This contains
# [Intermediate #2 (SHA-2)] 'Comodo RSA Domain Validation Secure Server CA'
# [Intermediate #1 (SHA-2)] 'COMODO RSA Certification Authority'
certificate '-----BEGIN CERTIFICATE-----
MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMjEy
MDAwMDAwWhcNMjkwMjExMjM1OTU5WjCBkDELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
Q09NT0RPIENBIExpbWl0ZWQxNjA0BgNVBAMTLUNPTU9ETyBSU0EgRG9tYWluIFZh
bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAI7CAhnhoFmk6zg1jSz9AdDTScBkxwtiBUUWOqigwAwCfx3M28Sh
bXcDow+G+eMGnD4LgYqbSRutA776S9uMIO3Vzl5ljj4Nr0zCsLdFXlIvNN5IJGS0
Qa4Al/e+Z96e0HqnU4A7fK31llVvl0cKfIWLIpeNs4TgllfQcBhglo/uLQeTnaG6
ytHNe+nEKpooIZFNb5JPJaXyejXdJtxGpdCsWTWM/06RQ1A/WZMebFEh7lgUq/51
UHg+TLAchhP6a5i84DuUHoVS3AOTJBhuyydRReZw3iVDpA3hSqXttn7IzW3uLh0n
c13cRTCAquOyQQuvvUSH2rnlG51/ruWFgqUCAwEAAaOCAWUwggFhMB8GA1UdIwQY
MBaAFLuvfgI9+qbxPISOre44mOzZMjLUMB0GA1UdDgQWBBSQr2o6lFoL2JDqElZz
30O0Oija5zAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgG
BmeBDAECATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNv
bS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcB
AQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9E
T1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21v
ZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAE4rdk+SHGI2ibp3wScF9BzWRJ2p
mj6q1WZmAT7qSeaiNbz69t2Vjpk1mA42GHWx3d1Qcnyu3HeIzg/3kCDKo2cuH1Z/
e+FE6kKVxF0NAVBGFfKBiVlsit2M8RKhjTpCipj4SzR7JzsItG8kO3KdY3RYPBps
P0/HEZrIqPW1N+8QRcZs2eBelSaz662jue5/DJpmNXMyYE7l3YphLG5SEXdoltMY
dVEVABt0iN3hxzgEQyjpFv3ZBdRdRydg1vs4O2xyopT4Qhrf7W8GjEXCBgCq5Ojc
2bXhc3js9iPc0d1sjhqPpepUfJa3w/5Vjo1JXvxku88+vZbrac2/4EjxYoIQ5QxG
V/Iz2tDIY+3GH5QFlkoakdH368+PUq4NCNk+qKBR6cGHdNXJ93SrLlP7u3r7l+L4
HyaPs9Kg4DdbKDsx5Q5XLVq4rXmsXiBmGqW5prU5wfWYQ//u+aen/e7KJD2AFsQX
j4rBYKEMrltDR5FL1ZoXX/nUh8HCjLfn4g8wGTeGrODcQgPmlKidrv0PJFGUzpII
0fxQ8ANAe4hZ7Q7drNJ3gjTcBpUC2JD5Leo31Rpg0Gcg19hCC0Wvgmje3WYkN5Ap
lBlGGSW4gNfL1IYoakRwJiNiqZ+Gb7+6kHDSVneFeO/qJakXzlByjAA6quPbYzSf
+AZxAeKCINT+b72x
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv
MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
gYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYD
VQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkq
hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNw
AHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR6
2RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onr
ayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt
4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIq
m1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/
vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT
8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IE
IlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfO
KJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPO
GHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/
s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g
JMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQD
AgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9
MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy
bmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6
Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQ
zbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfj
Jw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLY
Uspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5
B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9Hvx
PUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vR
pu/xO28QOG8=
-----END CERTIFICATE-----'
end
trait :with_expired_certificate do
certificate '-----BEGIN CERTIFICATE-----
MIIBsDCCARmgAwIBAgIBATANBgkqhkiG9w0BAQUFADAeMRwwGgYDVQQDExNleHBp
cmVkLWNlcnRpZmljYXRlMB4XDTE1MDIxMjE0MzMwMFoXDTE2MDIwMTE0MzMwMFow
HjEcMBoGA1UEAxMTZXhwaXJlZC1jZXJ0aWZpY2F0ZTCBnzANBgkqhkiG9w0BAQEF
AAOBjQAwgYkCgYEApL4J9L0ZxFJ1hI1LPIflAlAGvm6ZEvoT4qKU5Xf2JgU7/2ge
NR1qlNFaSvCc08Knupp5yTgmvyK/Xi09U0N82vvp4Zvr/diSc4A/RA6Mta6egLyS
NT438kdTnY2tR5feoTLwQpX0t4IMlwGQGT5h6Of2fKmDxzuwuyffcIHqLdsCAwEA
ATANBgkqhkiG9w0BAQUFAAOBgQBNj+vWvneyW1KkbVK+b/cVmnYPSfbkHrYK6m8X
Hq9LkWn6WP4EHsesHyslgTQZF8C7kVLTbLn2noLnOE+Mp3vcWlZxl3Yk6aZMhKS+
Iy6oRpHaCF/2obZdIdgf9rlyz0fkqyHJc9GkioSoOhJZxEV2SgAkap8yS0sX2tJ9
ZDXgrA==
-----END CERTIFICATE-----'
end
end
end
require 'spec_helper'
describe PagesDomain, models: true do
describe 'associations' do
it { is_expected.to belong_to(:project) }
end
describe :validate_domain do
subject { build(:pages_domain, domain: domain) }
context 'is unique' do
let(:domain) { 'my.domain.com' }
it { is_expected.to validate_uniqueness_of(:domain) }
end
context 'valid domain' do
let(:domain) { 'my.domain.com' }
it { is_expected.to be_valid }
end
context 'no domain' do
let(:domain) { nil }
it { is_expected.to_not be_valid }
end
context 'invalid domain' do
let(:domain) { '0123123' }
it { is_expected.to_not be_valid }
end
context 'domain from .example.com' do
let(:domain) { 'my.domain.com' }
before { allow(Settings.pages).to receive(:host).and_return('domain.com') }
it { is_expected.to_not be_valid }
end
end
describe 'validate certificate' do
subject { domain }
context 'when only certificate is specified' do
let(:domain) { build(:pages_domain, :with_certificate) }
it { is_expected.to_not be_valid }
end
context 'when only key is specified' do
let(:domain) { build(:pages_domain, :with_key) }
it { is_expected.to_not be_valid }
end
context 'with matching key' do
let(:domain) { build(:pages_domain, :with_certificate, :with_key) }
it { is_expected.to be_valid }
end
context 'for not matching key' do
let(:domain) { build(:pages_domain, :with_missing_chain, :with_key) }
it { is_expected.to_not be_valid }
end
end
describe :url do
subject { domain.url }
context 'without the certificate' do
let(:domain) { build(:pages_domain) }
it { is_expected.to eq('http://my.domain.com') }
end
context 'with a certificate' do
let(:domain) { build(:pages_domain, :with_certificate) }
it { is_expected.to eq('https://my.domain.com') }
end
end
describe :has_matching_key? do
subject { domain.has_matching_key? }
context 'for matching key' do
let(:domain) { build(:pages_domain, :with_certificate, :with_key) }
it { is_expected.to be_truthy }
end
context 'for invalid key' do
let(:domain) { build(:pages_domain, :with_missing_chain, :with_key) }
it { is_expected.to be_falsey }
end
end
describe :has_intermediates? do
subject { domain.has_intermediates? }
context 'for self signed' do
let(:domain) { build(:pages_domain, :with_certificate) }
it { is_expected.to be_truthy }
end
context 'for missing certificate chain' do
let(:domain) { build(:pages_domain, :with_missing_chain) }
it { is_expected.to be_falsey }
end
context 'for trusted certificate chain' do
# We only validate that we can to rebuild the trust chain, for certificates
# We assume that 'AddTrustExternalCARoot' needed to validate the chain is in trusted store.
# It will be if ca-certificates is installed on Debian/Ubuntu/Alpine
let(:domain) { build(:pages_domain, :with_trusted_chain) }
it { is_expected.to be_truthy }
end
end
describe :expired? do
subject { domain.expired? }
context 'for valid' do
let(:domain) { build(:pages_domain, :with_certificate) }
it { is_expected.to be_falsey }
end
context 'for expired' do
let(:domain) { build(:pages_domain, :with_expired_certificate) }
it { is_expected.to be_truthy }
end
end
describe :subject do
let(:domain) { build(:pages_domain, :with_certificate) }
subject { domain.subject }
it { is_expected.to eq('/CN=test-certificate') }
end
describe :certificate_text do
let(:domain) { build(:pages_domain, :with_certificate) }
subject { domain.certificate_text }
# We test only existence of output, since the output is long
it { is_expected.to_not be_empty }
end
end
......@@ -68,6 +68,7 @@ describe Project, models: true do
it { is_expected.to have_many(:runners) }
it { is_expected.to have_many(:variables) }
it { is_expected.to have_many(:triggers) }
it { is_expected.to have_many(:pages_domains) }
end
describe 'modules' 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