From 82f2a1aa0dfaad36b1723c8d3d1087c20a6f9618 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 9 Feb 2016 18:06:55 +0100 Subject: [PATCH 01/28] Initial work on GitLab Pages update --- Gemfile | 3 + Gemfile.lock | 4 + app/controllers/projects/pages_controller.rb | 94 +++++++++++++++++++ app/controllers/projects_controller.rb | 10 -- app/helpers/projects_helper.rb | 8 ++ app/models/ability.rb | 1 + app/models/project.rb | 51 ++++++++-- .../update_pages_configuration_service.rb | 53 +++++++++++ app/validators/certificate_key_validator.rb | 24 +++++ app/validators/certificate_validator.rb | 30 ++++++ .../layouts/nav/_project_settings.html.haml | 5 + app/views/projects/pages/_access.html.haml | 34 +++++++ app/views/projects/pages/_destroy.haml | 10 ++ app/views/projects/pages/_disabled.html.haml | 4 + app/views/projects/pages/_form.html.haml | 35 +++++++ .../pages/_remove_certificate.html.haml | 16 ++++ .../pages/_upload_certificate.html.haml | 32 +++++++ app/views/projects/pages/_use.html.haml | 18 ++++ app/views/projects/pages/show.html.haml | 18 ++++ app/workers/pages_worker.rb | 6 +- config/initializers/1_settings.rb | 1 + config/routes.rb | 4 +- ...808_add_pages_custom_domain_to_projects.rb | 10 ++ 23 files changed, 451 insertions(+), 20 deletions(-) create mode 100644 app/controllers/projects/pages_controller.rb create mode 100644 app/services/projects/update_pages_configuration_service.rb create mode 100644 app/validators/certificate_key_validator.rb create mode 100644 app/validators/certificate_validator.rb create mode 100644 app/views/projects/pages/_access.html.haml create mode 100644 app/views/projects/pages/_destroy.haml create mode 100644 app/views/projects/pages/_disabled.html.haml create mode 100644 app/views/projects/pages/_form.html.haml create mode 100644 app/views/projects/pages/_remove_certificate.html.haml create mode 100644 app/views/projects/pages/_upload_certificate.html.haml create mode 100644 app/views/projects/pages/_use.html.haml create mode 100644 app/views/projects/pages/show.html.haml create mode 100644 db/migrate/20160209125808_add_pages_custom_domain_to_projects.rb diff --git a/Gemfile b/Gemfile index e795da787ad8..9575e6d0cc2d 100644 --- a/Gemfile +++ b/Gemfile @@ -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' diff --git a/Gemfile.lock b/Gemfile.lock index d35964085a81..4c940c9367d7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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) diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb new file mode 100644 index 000000000000..ef0ed5051423 --- /dev/null +++ b/app/controllers/projects/pages_controller.rb @@ -0,0 +1,94 @@ +class Projects::PagesController < Projects::ApplicationController + layout 'project_settings' + + before_action :authorize_update_pages!, except: [:show] + before_action :authorize_remove_pages!, only: :destroy + + helper_method :valid_certificate?, :valid_certificate_key? + helper_method :valid_key_for_certificiate?, :valid_certificate_intermediates? + helper_method :certificate, :certificate_key + + def show + end + + def update + if @project.update_attributes(pages_params) + redirect_to namespace_project_pages_path(@project.namespace, @project) + else + render 'show' + end + end + + def certificate + @project.remove_pages_certificate + end + + def destroy + @project.remove_pages + + respond_to do |format| + format.html { redirect_to project_path(@project) } + end + end + + private + + def pages_params + params.require(:project).permit( + :pages_custom_certificate, + :pages_custom_certificate_key, + :pages_custom_domain, + :pages_redirect_http, + ) + end + + def valid_certificate? + certificate.present? + end + + def valid_certificate_key? + certificate_key.present? + end + + def valid_key_for_certificiate? + return false unless certificate + return false unless certificate_key + + certificate.verify(certificate_key) + rescue OpenSSL::X509::CertificateError + false + end + + def valid_certificate_intermediates? + return false unless certificate + + store = OpenSSL::X509::Store.new + store.set_default_paths + + # This forces to load all intermediate certificates stored in `pages_custom_certificate` + Tempfile.open('project_certificate') do |f| + f.write(@project.pages_custom_certificate) + f.flush + store.add_file(f.path) + end + + store.verify(certificate) + rescue OpenSSL::X509::StoreError + false + end + + def certificate + return unless @project.pages_custom_certificate + + @certificate ||= OpenSSL::X509::Certificate.new(@project.pages_custom_certificate) + rescue OpenSSL::X509::CertificateError + nil + end + + def certificate_key + return unless @project.pages_custom_certificate_key + @certificate_key ||= OpenSSL::PKey::RSA.new(@project.pages_custom_certificate_key) + rescue OpenSSL::PKey::PKeyError + nil + end +end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 6393397000a0..95d851a69372 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -171,16 +171,6 @@ def unarchive 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 diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 93d530ef7694..8dd31a895ec4 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -90,6 +90,14 @@ def remove_fork_project_message(project) "You are going to remove the fork relationship to source project #{@project.forked_from_project.name_with_namespace}. Are you ABSOLUTELY sure?" end + def remove_pages_message(project) + "You are going to remove the pages for #{project.name_with_namespace}.\n Are you ABSOLUTELY sure?" + end + + def remove_pages_certificate_message(project) + "You are going to remove a certificates for #{project.name_with_namespace}.\n Are you ABSOLUTELY sure?" + end + def project_nav_tabs @nav_tabs ||= get_project_nav_tabs(@project, current_user) end diff --git a/app/models/ability.rb b/app/models/ability.rb index 29fcaf583fc9..38482c1de2cd 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -245,6 +245,7 @@ def project_admin_rules :change_visibility_level, :rename_project, :remove_project, + :update_pages, :remove_pages, :archive_project, :remove_fork_project diff --git a/app/models/project.rb b/app/models/project.rb index da08add18e6e..08faaa58414a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -95,6 +95,8 @@ def update_forks_visibility_level attr_accessor :new_default_branch attr_accessor :old_path_with_namespace + attr_encrypted :pages_custom_certificate_key, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base + # Relations belongs_to :creator, foreign_key: 'creator_id', class_name: 'User' belongs_to :group, -> { where(type: Group) }, foreign_key: 'namespace_id' @@ -209,6 +211,11 @@ def update_forks_visibility_level validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validates :approvals_before_merge, numericality: true, allow_blank: true + validates :pages_custom_domain, hostname: true, allow_blank: true, allow_nil: true + validates_uniqueness_of :pages_custom_domain, allow_nil: true, allow_blank: true + validates :pages_custom_certificate, certificate: { intermediate: true } + validates :pages_custom_certificate_key, certificate_key: true + add_authentication_token_field :runners_token before_save :ensure_runners_token @@ -1046,16 +1053,27 @@ def runners_token end def pages_url - if Dir.exist?(public_pages_path) - host = "#{namespace.path}.#{Settings.pages.host}" - url = Gitlab.config.pages.url.sub(/^https?:\/\//) do |prefix| - "#{prefix}#{namespace.path}." - end + 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}." + end + + # If the project path is the same as host, leave the short version + return url if host == path + + "#{url}/#{path}" + end - # If the project path is the same as host, leave the short version - return url if host == path + def pages_custom_url + return unless pages_custom_domain + return unless Dir.exist?(public_pages_path) - "#{url}/#{path}" + if Gitlab.config.pages.https + return "https://#{pages_custom_domain}" + else + return "http://#{pages_custom_domain}" end end @@ -1067,6 +1085,15 @@ def public_pages_path File.join(pages_path, 'public') end + def remove_pages_certificate + update( + pages_custom_certificate: nil, + pages_custom_certificate_key: nil + ) + + UpdatePagesConfigurationService.new(self).execute + end + def remove_pages # 1. We rename pages to temporary directory # 2. We wait 5 minutes, due to NFS caching @@ -1076,6 +1103,14 @@ def remove_pages if Gitlab::PagesTransfer.new.rename_project(path, temp_path, namespace.path) PagesWorker.perform_in(5.minutes, :remove, namespace.path, temp_path) end + + update( + pages_custom_certificate: nil, + pages_custom_certificate_key: nil, + pages_custom_domain: nil + ) + + UpdatePagesConfigurationService.new(self).execute end def merge_method diff --git a/app/services/projects/update_pages_configuration_service.rb b/app/services/projects/update_pages_configuration_service.rb new file mode 100644 index 000000000000..be4c2fbef8c3 --- /dev/null +++ b/app/services/projects/update_pages_configuration_service.rb @@ -0,0 +1,53 @@ +module Projects + class UpdatePagesConfigurationService < BaseService + attr_reader :project + + def initialize(project) + @project = project + end + + def execute + update_file(pages_cname_file, project.pages_custom_domain) + update_file(pages_certificate_file, project.pages_custom_certificate) + update_file(pages_certificate_file_key, project.pages_custom_certificate_key) + reload_daemon + success + rescue => e + error(e.message) + end + + private + + def reload_daemon + # GitLab Pages daemon constantly watches for modification time of `pages.path` + # It reloads configuration when `pages.path` is modified + File.touch(Settings.pages.path) + end + + def pages_path + @pages_path ||= project.pages_path + end + + def pages_cname_file + File.join(pages_path, 'CNAME') + end + + def pages_certificate_file + File.join(pages_path, 'domain.crt') + end + + def pages_certificate_key_file + File.join(pages_path, 'domain.key') + end + + def update_file(file, data) + if data + File.open(file, 'w') do |file| + file.write(data) + end + else + File.rm_r(file) + end + end + end +end diff --git a/app/validators/certificate_key_validator.rb b/app/validators/certificate_key_validator.rb new file mode 100644 index 000000000000..3b5bd30db1ae --- /dev/null +++ b/app/validators/certificate_key_validator.rb @@ -0,0 +1,24 @@ +# 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) + pkey = OpenSSL::PKey::RSA.new(value) + pkey.private? + rescue OpenSSL::PKey::PKeyError + false + end +end diff --git a/app/validators/certificate_validator.rb b/app/validators/certificate_validator.rb new file mode 100644 index 000000000000..2cba5a435b7d --- /dev/null +++ b/app/validators/certificate_validator.rb @@ -0,0 +1,30 @@ +# UrlValidator +# +# Custom validator for private keys. +# +# class Project < ActiveRecord::Base +# validates :certificate_key, certificate_key: true +# end +# +class CertificateValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + certificate = parse_certificate(value) + unless certificate + record.errors.add(attribute, "must be a valid PEM certificate") + end + + if options[:intermediates] + unless certificate + record.errors.add(attribute, "certificate verification failed: missing intermediate certificates") + end + end + end + + private + + def parse_certificate(value) + OpenSSL::X509::Certificate.new(value) + rescue OpenSSL::X509::CertificateError + nil + end +end diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index aea70b451e77..80a621edfbfe 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -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('clone 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') diff --git a/app/views/projects/pages/_access.html.haml b/app/views/projects/pages/_access.html.haml new file mode 100644 index 000000000000..d64f99fd22b9 --- /dev/null +++ b/app/views/projects/pages/_access.html.haml @@ -0,0 +1,34 @@ +- if @project.pages_url + .panel.panel-default + .panel-heading + Access pages + .panel-body + %p + %strong + Congratulations! Your pages are served at: + %p= link_to @project.pages_url, @project.pages_url + + - if Settings.pages.custom_domain && @project.pages_custom_url + %p= link_to @project.pages_custom_url, @project.pages_custom_url + + - if @project.pages_custom_certificate + - unless valid_certificate? + #error_explanation + .alert.alert-warning + Your certificate is invalid. + + - unless valid_certificate_key? + #error_explanation + .alert.alert-warning + Your private key is invalid. + + - unless valid_key_for_certificiate? + #error_explanation + .alert.alert-warning + Your private key can't be used with your certificate. + + - unless valid_certificate_intermediates? + #error_explanation + .alert.alert-warning + Your certificate doesn't have intermediates. + Your page may not work properly. diff --git a/app/views/projects/pages/_destroy.haml b/app/views/projects/pages/_destroy.haml new file mode 100644 index 000000000000..61b995a59345 --- /dev/null +++ b/app/views/projects/pages/_destroy.haml @@ -0,0 +1,10 @@ +- if can?(current_user, :remove_pages, @project) && @project.pages_url + .panel.panel-default.panel.panel-danger + .panel-heading Remove pages + .errors-holder + .panel-body + = form_tag(namespace_project_pages_path(@project.namespace, @project), method: :delete, class: 'form-horizontal') do + %p + Removing the pages will prevent from exposing them to outside world. + .form-actions + = button_to 'Remove pages', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_pages_message(@project) } diff --git a/app/views/projects/pages/_disabled.html.haml b/app/views/projects/pages/_disabled.html.haml new file mode 100644 index 000000000000..cf9ef5b4d6f6 --- /dev/null +++ b/app/views/projects/pages/_disabled.html.haml @@ -0,0 +1,4 @@ +.panel.panel-default + .nothing-here-block + GitLab Pages is disabled. + Ask your system's administrator to enable it. diff --git a/app/views/projects/pages/_form.html.haml b/app/views/projects/pages/_form.html.haml new file mode 100644 index 000000000000..a7b03d552dbf --- /dev/null +++ b/app/views/projects/pages/_form.html.haml @@ -0,0 +1,35 @@ +- if can?(current_user, :update_pages, @project) + .panel.panel-default + .panel-heading + Settings + .panel-body + = form_for [@project], url: namespace_project_pages_path(@project.namespace, @project), html: { class: 'form-horizontal fieldset-form' } do |f| + - if @project.errors.any? + #error_explanation + .alert.alert-danger + - @project.errors.full_messages.each do |msg| + %p= msg + + .form-group + = f.label :pages_domain, class: 'control-label' do + Custom domain + .col-sm-10 + - if Settings.pages.custom_domain + = f.text_field :pages_custom_domain, required: false, autocomplete: 'off', class: 'form-control' + %span.help-inline Allows you to serve the pages under your domain + - else + .nothing-here-block + Support for custom domains and certificates is disabled. + Ask your system's administrator to enable it. + + - if Settings.pages.https + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :pages_redirect_http do + = f.check_box :pages_redirect_http + %span.descr Force HTTPS + .help-block Redirect the HTTP to HTTPS forcing to always use the secure connection + + .form-actions + = f.submit 'Save changes', class: "btn btn-save" diff --git a/app/views/projects/pages/_remove_certificate.html.haml b/app/views/projects/pages/_remove_certificate.html.haml new file mode 100644 index 000000000000..e8c0d03adfa8 --- /dev/null +++ b/app/views/projects/pages/_remove_certificate.html.haml @@ -0,0 +1,16 @@ +- if can?(current_user, :update_pages, @project) && @project.pages_custom_certificate + .panel.panel-default.panel.panel-danger + .panel-heading + Remove certificate + .errors-holder + .panel-body + = form_tag(certificates_namespace_project_pages_path(@project.namespace, @project), method: :delete, class: 'form-horizontal') do + %p + Removing the certificate will stop serving the page under HTTPS. + - if certificate + %p + %pre + = certificate.to_text + + .form-actions + = button_to 'Remove certificate', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_pages_certificate_message(@project) } diff --git a/app/views/projects/pages/_upload_certificate.html.haml b/app/views/projects/pages/_upload_certificate.html.haml new file mode 100644 index 000000000000..30873fcf3953 --- /dev/null +++ b/app/views/projects/pages/_upload_certificate.html.haml @@ -0,0 +1,32 @@ +- if can?(current_user, :update_pages, @project) && Settings.pages.https && Settings.pages.custom_domain + .panel.panel-default + .panel-heading + Certificate + .panel-body + %p + Allows you to upload your certificate which will be used to serve pages under your domain. + %br + + = form_for [@project], url: namespace_project_pages_path(@project.namespace, @project), html: { class: 'form-horizontal fieldset-form' } do |f| + - if @project.errors.any? + #error_explanation + .alert.alert-danger + - @project.errors.full_messages.each do |msg| + %p= msg + + .form-group + = f.label :pages_custom_certificate, class: 'control-label' do + Certificate (PEM) + .col-sm-10 + = f.text_area :pages_custom_certificate, required: true, rows: 5, class: 'form-control', value: '' + %span.help-inline Upload a certificate for your domain with all intermediates + + .form-group + = f.label :pages_custom_certificate_key, class: 'control-label' do + Key (PEM) + .col-sm-10 + = f.text_area :pages_custom_certificate_key, required: true, rows: 5, class: 'form-control', value: '' + %span.help-inline Upload a certificate for your domain with all intermediates + + .form-actions + = f.submit 'Update certificate', class: "btn btn-save" diff --git a/app/views/projects/pages/_use.html.haml b/app/views/projects/pages/_use.html.haml new file mode 100644 index 000000000000..5542bbe670ba --- /dev/null +++ b/app/views/projects/pages/_use.html.haml @@ -0,0 +1,18 @@ +- 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}. + %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. diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml new file mode 100644 index 000000000000..5f689800da84 --- /dev/null +++ b/app/views/projects/pages/show.html.haml @@ -0,0 +1,18 @@ +- page_title "Pages" +%h3.page_title Pages +%p.light + With GitLab Pages you can host for free 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 + +- if Settings.pages.enabled + = render 'access' + = render 'use' + - if @project.pages_url + = render 'form' + = render 'upload_certificate' + = render 'remove_certificate' + = render 'destroy' +- else + = render 'disabled' diff --git a/app/workers/pages_worker.rb b/app/workers/pages_worker.rb index 8c99e8dbe763..4eeb9666bb0d 100644 --- a/app/workers/pages_worker.rb +++ b/app/workers/pages_worker.rb @@ -9,7 +9,11 @@ def perform(action, *arg) 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) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index d33f1e736d35..eeddcce2a6c1 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -299,6 +299,7 @@ def host(url) 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['custom_domain'] ||= false if Settings.pages['custom_domain'].nil? # # Git LFS diff --git a/config/routes.rb b/config/routes.rb index 3ba62cf71556..b98c6c99fff5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -428,7 +428,6 @@ delete :remove_fork post :archive post :unarchive - post :remove_pages post :housekeeping post :toggle_star post :markdown_preview @@ -547,6 +546,9 @@ end end + resource :pages, only: [:show, :update, :destroy] do + delete :certificates + end resources :compare, only: [:index, :create] resources :network, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } diff --git a/db/migrate/20160209125808_add_pages_custom_domain_to_projects.rb b/db/migrate/20160209125808_add_pages_custom_domain_to_projects.rb new file mode 100644 index 000000000000..6472199fc4a9 --- /dev/null +++ b/db/migrate/20160209125808_add_pages_custom_domain_to_projects.rb @@ -0,0 +1,10 @@ +class AddPagesCustomDomainToProjects < ActiveRecord::Migration + def change + add_column :projects, :pages_custom_certificate, :text + add_column :projects, :pages_custom_certificate_key, :text + add_column :projects, :pages_custom_certificate_key_iv, :string + add_column :projects, :pages_custom_certificate_key_salt, :string + add_column :projects, :pages_custom_domain, :string, unique: true + add_column :projects, :pages_redirect_http, :boolean, default: false, null: false + end +end -- GitLab From 089660b31f3a371540732834149cd964ebc9d7bd Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 9 Feb 2016 19:04:39 +0100 Subject: [PATCH 02/28] Implement proper verification of certificate's public_key against the private_key --- app/controllers/projects/pages_controller.rb | 5 ++- app/models/project.rb | 8 ++-- app/validators/certificate_key_validator.rb | 1 + app/validators/certificate_validator.rb | 14 ++----- app/views/projects/edit.html.haml | 41 ------------------- app/views/projects/pages/_use.html.haml | 10 ----- ...808_add_pages_custom_domain_to_projects.rb | 6 +-- 7 files changed, 15 insertions(+), 70 deletions(-) diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb index ef0ed5051423..359544472e94 100644 --- a/app/controllers/projects/pages_controller.rb +++ b/app/controllers/projects/pages_controller.rb @@ -54,8 +54,9 @@ def valid_key_for_certificiate? return false unless certificate return false unless certificate_key - certificate.verify(certificate_key) - rescue OpenSSL::X509::CertificateError + # We compare the public key stored in certificate with public key from certificate key + certificate.public_key.to_pem == certificate_key.public_key.to_pem + rescue OpenSSL::X509::CertificateError, OpenSSL::PKey::PKeyError false end diff --git a/app/models/project.rb b/app/models/project.rb index 08faaa58414a..420b484bee1d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -95,8 +95,6 @@ def update_forks_visibility_level attr_accessor :new_default_branch attr_accessor :old_path_with_namespace - attr_encrypted :pages_custom_certificate_key, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base - # Relations belongs_to :creator, foreign_key: 'creator_id', class_name: 'User' belongs_to :group, -> { where(type: Group) }, foreign_key: 'namespace_id' @@ -213,14 +211,16 @@ def update_forks_visibility_level validates :pages_custom_domain, hostname: true, allow_blank: true, allow_nil: true validates_uniqueness_of :pages_custom_domain, allow_nil: true, allow_blank: true - validates :pages_custom_certificate, certificate: { intermediate: true } - validates :pages_custom_certificate_key, certificate_key: true + validates :pages_custom_certificate, certificate: true, allow_nil: true, allow_blank: true + validates :pages_custom_certificate_key, certificate_key: true, allow_nil: true, allow_blank: true add_authentication_token_field :runners_token before_save :ensure_runners_token mount_uploader :avatar, AvatarUploader + attr_encrypted :pages_custom_certificate_key, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base + # Scopes scope :sorted_by_activity, -> { reorder(last_activity_at: :desc) } scope :sorted_by_stars, -> { reorder('projects.star_count DESC') } diff --git a/app/validators/certificate_key_validator.rb b/app/validators/certificate_key_validator.rb index 3b5bd30db1ae..7039bd5a6213 100644 --- a/app/validators/certificate_key_validator.rb +++ b/app/validators/certificate_key_validator.rb @@ -16,6 +16,7 @@ def validate_each(record, attribute, value) private def valid_private_key_pem?(value) + return unless value pkey = OpenSSL::PKey::RSA.new(value) pkey.private? rescue OpenSSL::PKey::PKeyError diff --git a/app/validators/certificate_validator.rb b/app/validators/certificate_validator.rb index 2cba5a435b7d..2a04c76d4b9a 100644 --- a/app/validators/certificate_validator.rb +++ b/app/validators/certificate_validator.rb @@ -3,26 +3,20 @@ # Custom validator for private keys. # # class Project < ActiveRecord::Base -# validates :certificate_key, certificate_key: true +# validates :certificate_key, certificate: true # end # class CertificateValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) - certificate = parse_certificate(value) - unless certificate + unless valid_certificate_pem?(value) record.errors.add(attribute, "must be a valid PEM certificate") end - - if options[:intermediates] - unless certificate - record.errors.add(attribute, "certificate verification failed: missing intermediate certificates") - end - end end private - def parse_certificate(value) + def valid_certificate_pem?(value) + return unless value OpenSSL::X509::Certificate.new(value) rescue OpenSSL::X509::CertificateError nil diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index adcc591a8604..6a3f75eb99ac 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -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 diff --git a/app/views/projects/pages/_use.html.haml b/app/views/projects/pages/_use.html.haml index 5542bbe670ba..ee38f45d44de 100644 --- a/app/views/projects/pages/_use.html.haml +++ b/app/views/projects/pages/_use.html.haml @@ -6,13 +6,3 @@ %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. diff --git a/db/migrate/20160209125808_add_pages_custom_domain_to_projects.rb b/db/migrate/20160209125808_add_pages_custom_domain_to_projects.rb index 6472199fc4a9..13b42d18a7aa 100644 --- a/db/migrate/20160209125808_add_pages_custom_domain_to_projects.rb +++ b/db/migrate/20160209125808_add_pages_custom_domain_to_projects.rb @@ -1,9 +1,9 @@ class AddPagesCustomDomainToProjects < ActiveRecord::Migration def change add_column :projects, :pages_custom_certificate, :text - add_column :projects, :pages_custom_certificate_key, :text - add_column :projects, :pages_custom_certificate_key_iv, :string - add_column :projects, :pages_custom_certificate_key_salt, :string + add_column :projects, :encrypted_pages_custom_certificate_key, :text + add_column :projects, :encrypted_pages_custom_certificate_key_iv, :string + add_column :projects, :encrypted_pages_custom_certificate_key_salt, :string add_column :projects, :pages_custom_domain, :string, unique: true add_column :projects, :pages_redirect_http, :boolean, default: false, null: false end -- GitLab From 6b69bf76d47a440e089359442e172b528f33b850 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 10 Feb 2016 11:37:27 +0100 Subject: [PATCH 03/28] WIP --- app/controllers/projects/pages_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb index 359544472e94..055f182ae00c 100644 --- a/app/controllers/projects/pages_controller.rb +++ b/app/controllers/projects/pages_controller.rb @@ -89,7 +89,7 @@ def certificate def certificate_key return unless @project.pages_custom_certificate_key @certificate_key ||= OpenSSL::PKey::RSA.new(@project.pages_custom_certificate_key) - rescue OpenSSL::PKey::PKeyError + rescue OpenSSL::PKey::PKeyError, OpenSSL::Cipher::CipherError nil end end -- GitLab From ab60acb8295c6392f47f4be38273977423684a63 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 10 Feb 2016 12:07:46 +0100 Subject: [PATCH 04/28] Added PagesDomain --- app/models/pages_domain.rb | 29 ++++++++++++++ app/models/project.rb | 38 +------------------ ...808_add_pages_custom_domain_to_projects.rb | 10 ----- .../20160210105555_create_pages_domain.rb | 14 +++++++ 4 files changed, 45 insertions(+), 46 deletions(-) create mode 100644 app/models/pages_domain.rb delete mode 100644 db/migrate/20160209125808_add_pages_custom_domain_to_projects.rb create mode 100644 db/migrate/20160210105555_create_pages_domain.rb diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb new file mode 100644 index 000000000000..eebdf7501de4 --- /dev/null +++ b/app/models/pages_domain.rb @@ -0,0 +1,29 @@ +class PagesDomain < ActiveRecord::Base + belongs_to :project + + validates :domain, hostname: true + validates_uniqueness_of :domain, allow_nil: true, allow_blank: true + validates :certificate, certificate: true, allow_nil: true, allow_blank: true + validates :key, certificate_key: true, allow_nil: true, allow_blank: true + + attr_encrypted :pages_custom_certificate_key, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base + + after_create :update + after_save :update + after_destroy :update + + def url + return unless domain + return unless Dir.exist?(project.public_pages_path) + + if certificate + return "https://#{domain}" + else + return "http://#{domain}" + end + end + + def update + UpdatePagesConfigurationService.new(project).execute + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 420b484bee1d..8fa6b2e86fca 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -162,6 +162,7 @@ def update_forks_visibility_level 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" @@ -209,18 +210,11 @@ def update_forks_visibility_level validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validates :approvals_before_merge, numericality: true, allow_blank: true - validates :pages_custom_domain, hostname: true, allow_blank: true, allow_nil: true - validates_uniqueness_of :pages_custom_domain, allow_nil: true, allow_blank: true - validates :pages_custom_certificate, certificate: true, allow_nil: true, allow_blank: true - validates :pages_custom_certificate_key, certificate_key: true, allow_nil: true, allow_blank: true - add_authentication_token_field :runners_token before_save :ensure_runners_token mount_uploader :avatar, AvatarUploader - attr_encrypted :pages_custom_certificate_key, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base - # Scopes scope :sorted_by_activity, -> { reorder(last_activity_at: :desc) } scope :sorted_by_stars, -> { reorder('projects.star_count DESC') } @@ -1066,17 +1060,6 @@ def pages_url "#{url}/#{path}" end - def pages_custom_url - return unless pages_custom_domain - return unless Dir.exist?(public_pages_path) - - if Gitlab.config.pages.https - return "https://#{pages_custom_domain}" - else - return "http://#{pages_custom_domain}" - end - end - def pages_path File.join(Settings.pages.path, path_with_namespace) end @@ -1085,32 +1068,15 @@ def public_pages_path File.join(pages_path, 'public') end - def remove_pages_certificate - update( - pages_custom_certificate: nil, - pages_custom_certificate_key: nil - ) - - UpdatePagesConfigurationService.new(self).execute - end - def remove_pages # 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) end - - update( - pages_custom_certificate: nil, - pages_custom_certificate_key: nil, - pages_custom_domain: nil - ) - - UpdatePagesConfigurationService.new(self).execute end def merge_method diff --git a/db/migrate/20160209125808_add_pages_custom_domain_to_projects.rb b/db/migrate/20160209125808_add_pages_custom_domain_to_projects.rb deleted file mode 100644 index 13b42d18a7aa..000000000000 --- a/db/migrate/20160209125808_add_pages_custom_domain_to_projects.rb +++ /dev/null @@ -1,10 +0,0 @@ -class AddPagesCustomDomainToProjects < ActiveRecord::Migration - def change - add_column :projects, :pages_custom_certificate, :text - add_column :projects, :encrypted_pages_custom_certificate_key, :text - add_column :projects, :encrypted_pages_custom_certificate_key_iv, :string - add_column :projects, :encrypted_pages_custom_certificate_key_salt, :string - add_column :projects, :pages_custom_domain, :string, unique: true - add_column :projects, :pages_redirect_http, :boolean, default: false, null: false - end -end diff --git a/db/migrate/20160210105555_create_pages_domain.rb b/db/migrate/20160210105555_create_pages_domain.rb new file mode 100644 index 000000000000..9af206143bd9 --- /dev/null +++ b/db/migrate/20160210105555_create_pages_domain.rb @@ -0,0 +1,14 @@ +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 -- GitLab From 87a43577277583b9648fe4632a130bdba6aa47b3 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 10 Feb 2016 15:06:31 +0100 Subject: [PATCH 05/28] Implement extra domains and save pages configuration --- app/controllers/projects/pages_controller.rb | 94 +++++++------------ app/helpers/projects_helper.rb | 4 - app/models/pages_domain.rb | 84 ++++++++++++++++- .../update_pages_configuration_service.rb | 32 ++++--- app/views/projects/pages/_access.html.haml | 29 +----- app/views/projects/pages/_destroy.haml | 2 +- app/views/projects/pages/_form.html.haml | 64 ++++++------- app/views/projects/pages/_list.html.haml | 16 ++++ .../projects/pages/_no_domains.html.haml | 6 ++ .../pages/_remove_certificate.html.haml | 16 ---- .../pages/_upload_certificate.html.haml | 32 ------- app/views/projects/pages/index.html.haml | 25 +++++ app/views/projects/pages/new.html.haml | 6 ++ app/views/projects/pages/show.html.haml | 38 ++++---- config/gitlab.yml.example | 2 + config/initializers/1_settings.rb | 3 +- config/routes.rb | 4 +- 17 files changed, 249 insertions(+), 208 deletions(-) create mode 100644 app/views/projects/pages/_list.html.haml create mode 100644 app/views/projects/pages/_no_domains.html.haml delete mode 100644 app/views/projects/pages/_remove_certificate.html.haml delete mode 100644 app/views/projects/pages/_upload_certificate.html.haml create mode 100644 app/views/projects/pages/index.html.haml create mode 100644 app/views/projects/pages/new.html.haml diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb index 055f182ae00c..82814afe196e 100644 --- a/app/controllers/projects/pages_controller.rb +++ b/app/controllers/projects/pages_controller.rb @@ -2,25 +2,45 @@ class Projects::PagesController < Projects::ApplicationController layout 'project_settings' before_action :authorize_update_pages!, except: [:show] - before_action :authorize_remove_pages!, only: :destroy + before_action :authorize_remove_pages!, only: [:remove_pages] + before_action :label, only: [:destroy] + before_action :domain, only: [:show] helper_method :valid_certificate?, :valid_certificate_key? helper_method :valid_key_for_certificiate?, :valid_certificate_intermediates? helper_method :certificate, :certificate_key + def index + @domains = @project.pages_domains.order(:domain) + end + def show end - def update - if @project.update_attributes(pages_params) + 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 'show' + render 'new' end end - def certificate - @project.remove_pages_certificate + 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 def destroy @@ -33,63 +53,15 @@ def destroy private - def pages_params - params.require(:project).permit( - :pages_custom_certificate, - :pages_custom_certificate_key, - :pages_custom_domain, - :pages_redirect_http, + def pages_domain_params + params.require(:pages_domain).permit( + :certificate, + :key, + :domain ) end - def valid_certificate? - certificate.present? - end - - def valid_certificate_key? - certificate_key.present? - end - - def valid_key_for_certificiate? - return false unless certificate - return false unless certificate_key - - # We compare the public key stored in certificate with public key from certificate key - certificate.public_key.to_pem == certificate_key.public_key.to_pem - rescue OpenSSL::X509::CertificateError, OpenSSL::PKey::PKeyError - false - end - - def valid_certificate_intermediates? - return false unless certificate - - store = OpenSSL::X509::Store.new - store.set_default_paths - - # This forces to load all intermediate certificates stored in `pages_custom_certificate` - Tempfile.open('project_certificate') do |f| - f.write(@project.pages_custom_certificate) - f.flush - store.add_file(f.path) - end - - store.verify(certificate) - rescue OpenSSL::X509::StoreError - false - end - - def certificate - return unless @project.pages_custom_certificate - - @certificate ||= OpenSSL::X509::Certificate.new(@project.pages_custom_certificate) - rescue OpenSSL::X509::CertificateError - nil - end - - def certificate_key - return unless @project.pages_custom_certificate_key - @certificate_key ||= OpenSSL::PKey::RSA.new(@project.pages_custom_certificate_key) - rescue OpenSSL::PKey::PKeyError, OpenSSL::Cipher::CipherError - nil + def domain + @domain ||= @project.pages_domains.find_by(domain: params[:id].to_s) end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 8dd31a895ec4..f080a71eec09 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -94,10 +94,6 @@ def remove_pages_message(project) "You are going to remove the pages for #{project.name_with_namespace}.\n Are you ABSOLUTELY sure?" end - def remove_pages_certificate_message(project) - "You are going to remove a certificates for #{project.name_with_namespace}.\n Are you ABSOLUTELY sure?" - end - def project_nav_tabs @nav_tabs ||= get_project_nav_tabs(@project, current_user) end diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index eebdf7501de4..810af4e832af 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -2,19 +2,25 @@ class PagesDomain < ActiveRecord::Base belongs_to :project validates :domain, hostname: true - validates_uniqueness_of :domain, allow_nil: true, allow_blank: 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 - attr_encrypted :pages_custom_certificate_key, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base + 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 - return unless Dir.exist?(project.public_pages_path) if certificate return "https://#{domain}" @@ -23,7 +29,77 @@ def url end end + def has_matching_key? + return unless x509 + return 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 + + 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 + return current < x509.not_before || x509.not_after < current + end + + def subject + return unless x509 + return x509.subject.to_s + end + + def fingerprint + return unless x509 + @fingeprint ||= OpenSSL::Digest::SHA256.new(x509.to_der).to_s + end + + private + + 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 + def update - UpdatePagesConfigurationService.new(project).execute + ::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 end diff --git a/app/services/projects/update_pages_configuration_service.rb b/app/services/projects/update_pages_configuration_service.rb index be4c2fbef8c3..5afb0582ca63 100644 --- a/app/services/projects/update_pages_configuration_service.rb +++ b/app/services/projects/update_pages_configuration_service.rb @@ -7,9 +7,7 @@ def initialize(project) end def execute - update_file(pages_cname_file, project.pages_custom_domain) - update_file(pages_certificate_file, project.pages_custom_certificate) - update_file(pages_certificate_file_key, project.pages_custom_certificate_key) + update_file(pages_config_file, pages_config) reload_daemon success rescue => e @@ -18,6 +16,22 @@ def execute 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 @@ -28,16 +42,8 @@ def pages_path @pages_path ||= project.pages_path end - def pages_cname_file - File.join(pages_path, 'CNAME') - end - - def pages_certificate_file - File.join(pages_path, 'domain.crt') - end - - def pages_certificate_key_file - File.join(pages_path, 'domain.key') + def pages_config_file + File.join(pages_path, 'config.jso') end def update_file(file, data) diff --git a/app/views/projects/pages/_access.html.haml b/app/views/projects/pages/_access.html.haml index d64f99fd22b9..9740877b2141 100644 --- a/app/views/projects/pages/_access.html.haml +++ b/app/views/projects/pages/_access.html.haml @@ -5,30 +5,9 @@ .panel-body %p %strong - Congratulations! Your pages are served at: - %p= link_to @project.pages_url, @project.pages_url - - - if Settings.pages.custom_domain && @project.pages_custom_url - %p= link_to @project.pages_custom_url, @project.pages_custom_url - - - if @project.pages_custom_certificate - - unless valid_certificate? - #error_explanation - .alert.alert-warning - Your certificate is invalid. + Congratulations! Your pages are served under: - - unless valid_certificate_key? - #error_explanation - .alert.alert-warning - Your private key is invalid. - - - unless valid_key_for_certificiate? - #error_explanation - .alert.alert-warning - Your private key can't be used with your certificate. + %p= link_to @project.pages_url, @project.pages_url - - unless valid_certificate_intermediates? - #error_explanation - .alert.alert-warning - Your certificate doesn't have intermediates. - Your page may not work properly. + - @project.pages_domains.each do |domain| + %p= link_to domain.url, domain.url diff --git a/app/views/projects/pages/_destroy.haml b/app/views/projects/pages/_destroy.haml index 61b995a59345..dd493a6d312e 100644 --- a/app/views/projects/pages/_destroy.haml +++ b/app/views/projects/pages/_destroy.haml @@ -3,7 +3,7 @@ .panel-heading Remove pages .errors-holder .panel-body - = form_tag(namespace_project_pages_path(@project.namespace, @project), method: :delete, class: 'form-horizontal') do + = form_tag(remove_pages_namespace_project_pages_path(@project.namespace, @project), method: :delete, class: 'form-horizontal') do %p Removing the pages will prevent from exposing them to outside world. .form-actions diff --git a/app/views/projects/pages/_form.html.haml b/app/views/projects/pages/_form.html.haml index a7b03d552dbf..c69b76c6697e 100644 --- a/app/views/projects/pages/_form.html.haml +++ b/app/views/projects/pages/_form.html.haml @@ -1,35 +1,35 @@ -- if can?(current_user, :update_pages, @project) - .panel.panel-default - .panel-heading - Settings - .panel-body - = form_for [@project], url: namespace_project_pages_path(@project.namespace, @project), html: { class: 'form-horizontal fieldset-form' } do |f| - - if @project.errors.any? - #error_explanation - .alert.alert-danger - - @project.errors.full_messages.each do |msg| - %p= msg += form_for [@domain], url: namespace_project_pages_path(@project.namespace, @project), 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 :pages_domain, class: 'control-label' do - Custom domain - .col-sm-10 - - if Settings.pages.custom_domain - = f.text_field :pages_custom_domain, required: false, autocomplete: 'off', class: 'form-control' - %span.help-inline Allows you to serve the pages under your domain - - else - .nothing-here-block - Support for custom domains and certificates is disabled. - Ask your system's administrator to enable it. + .form-group + = f.label :domain, class: 'control-label' do + Domain + .col-sm-10 + = f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control' + %span.help-inline * required - - if Settings.pages.https - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :pages_redirect_http do - = f.check_box :pages_redirect_http - %span.descr Force HTTPS - .help-block Redirect the HTTP to HTTPS forcing to always use the secure connection + - if Settings.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', value: '' + %span.help-inline Upload a certificate for your domain with all intermediates - .form-actions - = f.submit 'Save changes', class: "btn btn-save" + .form-group + = f.label :key, class: 'control-label' do + Key (PEM) + .col-sm-10 + = f.text_area :key, rows: 5, class: 'form-control', value: '' + %span.help-inline Upload a certificate for your domain with all intermediates + - 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" diff --git a/app/views/projects/pages/_list.html.haml b/app/views/projects/pages/_list.html.haml new file mode 100644 index 000000000000..7dfeb0e6e12b --- /dev/null +++ b/app/views/projects/pages/_list.html.haml @@ -0,0 +1,16 @@ +.panel.panel-default + .panel-heading + Domains (#{@domains.count}) + %ul.well-list + - @domains.each do |domain| + %li + .pull-right + = link_to 'Details', namespace_project_page_path(@project.namespace, @project, domain), class: "btn btn-sm btn-grouped" + = link_to 'Remove', namespace_project_page_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 diff --git a/app/views/projects/pages/_no_domains.html.haml b/app/views/projects/pages/_no_domains.html.haml new file mode 100644 index 000000000000..5a18740346a5 --- /dev/null +++ b/app/views/projects/pages/_no_domains.html.haml @@ -0,0 +1,6 @@ +.panel.panel-default + .panel-heading + Domains + .nothing-here-block + Support for domains and certificates is disabled. + Ask your system's administrator to enable it. diff --git a/app/views/projects/pages/_remove_certificate.html.haml b/app/views/projects/pages/_remove_certificate.html.haml deleted file mode 100644 index e8c0d03adfa8..000000000000 --- a/app/views/projects/pages/_remove_certificate.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -- if can?(current_user, :update_pages, @project) && @project.pages_custom_certificate - .panel.panel-default.panel.panel-danger - .panel-heading - Remove certificate - .errors-holder - .panel-body - = form_tag(certificates_namespace_project_pages_path(@project.namespace, @project), method: :delete, class: 'form-horizontal') do - %p - Removing the certificate will stop serving the page under HTTPS. - - if certificate - %p - %pre - = certificate.to_text - - .form-actions - = button_to 'Remove certificate', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_pages_certificate_message(@project) } diff --git a/app/views/projects/pages/_upload_certificate.html.haml b/app/views/projects/pages/_upload_certificate.html.haml deleted file mode 100644 index 30873fcf3953..000000000000 --- a/app/views/projects/pages/_upload_certificate.html.haml +++ /dev/null @@ -1,32 +0,0 @@ -- if can?(current_user, :update_pages, @project) && Settings.pages.https && Settings.pages.custom_domain - .panel.panel-default - .panel-heading - Certificate - .panel-body - %p - Allows you to upload your certificate which will be used to serve pages under your domain. - %br - - = form_for [@project], url: namespace_project_pages_path(@project.namespace, @project), html: { class: 'form-horizontal fieldset-form' } do |f| - - if @project.errors.any? - #error_explanation - .alert.alert-danger - - @project.errors.full_messages.each do |msg| - %p= msg - - .form-group - = f.label :pages_custom_certificate, class: 'control-label' do - Certificate (PEM) - .col-sm-10 - = f.text_area :pages_custom_certificate, required: true, rows: 5, class: 'form-control', value: '' - %span.help-inline Upload a certificate for your domain with all intermediates - - .form-group - = f.label :pages_custom_certificate_key, class: 'control-label' do - Key (PEM) - .col-sm-10 - = f.text_area :pages_custom_certificate_key, required: true, rows: 5, class: 'form-control', value: '' - %span.help-inline Upload a certificate for your domain with all intermediates - - .form-actions - = f.submit 'Update certificate', class: "btn btn-save" diff --git a/app/views/projects/pages/index.html.haml b/app/views/projects/pages/index.html.haml new file mode 100644 index 000000000000..fea34c113ba4 --- /dev/null +++ b/app/views/projects/pages/index.html.haml @@ -0,0 +1,25 @@ +- page_title "Pages" +%h3.page_title + Pages + + = link_to new_namespace_project_page_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 for free 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 Settings.pages.enabled + = render 'access' + = render 'use' + - if Settings.pages.external_http || Settings.pages.external_https + = render 'list' + - else + = render 'no_domains' + = render 'destroy' +- else + = render 'disabled' diff --git a/app/views/projects/pages/new.html.haml b/app/views/projects/pages/new.html.haml new file mode 100644 index 000000000000..2609df62aac2 --- /dev/null +++ b/app/views/projects/pages/new.html.haml @@ -0,0 +1,6 @@ +- page_title 'Pages' +%h3.page_title + New Pages Domain +%hr.clearfix +%div + = render 'form' diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml index 5f689800da84..98c4e8909681 100644 --- a/app/views/projects/pages/show.html.haml +++ b/app/views/projects/pages/show.html.haml @@ -1,18 +1,22 @@ -- page_title "Pages" -%h3.page_title Pages -%p.light - With GitLab Pages you can host for free 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 +- page_title "#{@domain.domain}", "Pages Domain" -- if Settings.pages.enabled - = render 'access' - = render 'use' - - if @project.pages_url - = render 'form' - = render 'upload_certificate' - = render 'remove_certificate' - = render 'destroy' -- else - = render 'disabled' +%h3.page-title + #{@domain.domain} + +.table-holder + %table.table + %tr + %td + Domain + %td + = link_to @domain.domain, @domain.url + %tr + %td + Certificate + %td + - if @domain.certificate + %pre + = @domain.certificate.to_text + - else + .light + missing diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index c51972fda2ac..152fe1c3fa16 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -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 notifies the GitLab pages do support Custom Domains + # external_https: "1.1.1.1:443" # if defined notifies the GitLab pages do support Custom Domains with Certificates ## Gravatar ## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index eeddcce2a6c1..54eadce5f5f7 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -299,7 +299,8 @@ def host(url) 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['custom_domain'] ||= false if Settings.pages['custom_domain'].nil? +Settings.pages['external_http'] ||= false if Settings.pages['external_http'].nil? +Settings.pages['external_https'] ||= false if Settings.pages['external_https'].nil? # # Git LFS diff --git a/config/routes.rb b/config/routes.rb index b98c6c99fff5..e12eb92d3494 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -546,8 +546,8 @@ end end - resource :pages, only: [:show, :update, :destroy] do - delete :certificates + resources :pages, except: [:edit, :update] do + delete :remove_pages end resources :compare, only: [:index, :create] resources :network, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } -- GitLab From eb4efde1afe16adb18ae72ade374a4f74f2c0826 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 10 Feb 2016 16:21:39 +0100 Subject: [PATCH 06/28] Fix the remove_pages --- app/controllers/projects/pages_controller.rb | 5 +++++ .../projects/update_pages_configuration_service.rb | 4 ++-- app/views/projects/pages/index.html.haml | 7 ++++--- config/routes.rb | 4 +++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb index 82814afe196e..0c7f2bd5784e 100644 --- a/app/controllers/projects/pages_controller.rb +++ b/app/controllers/projects/pages_controller.rb @@ -51,6 +51,11 @@ def destroy end end + def remove_pages + project.remove_pages + project.pages_domains.destroy_all + end + private def pages_domain_params diff --git a/app/services/projects/update_pages_configuration_service.rb b/app/services/projects/update_pages_configuration_service.rb index 5afb0582ca63..53e9d9e2757e 100644 --- a/app/services/projects/update_pages_configuration_service.rb +++ b/app/services/projects/update_pages_configuration_service.rb @@ -43,7 +43,7 @@ def pages_path end def pages_config_file - File.join(pages_path, 'config.jso') + File.join(pages_path, 'config.json') end def update_file(file, data) @@ -52,7 +52,7 @@ def update_file(file, data) file.write(data) end else - File.rm_r(file) + File.rm(file, force: true) end end end diff --git a/app/views/projects/pages/index.html.haml b/app/views/projects/pages/index.html.haml index fea34c113ba4..284f362d535d 100644 --- a/app/views/projects/pages/index.html.haml +++ b/app/views/projects/pages/index.html.haml @@ -2,9 +2,10 @@ %h3.page_title Pages - = link_to new_namespace_project_page_path(@project.namespace, @project), class: "btn btn-new pull-right", title: "New Domain" do - %i.fa.fa-plus - New Domain + - if Settings.pages.external_http || Settings.pages.external_https + = link_to new_namespace_project_page_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 for free your static websites on GitLab. diff --git a/config/routes.rb b/config/routes.rb index e12eb92d3494..1b0f15ef880b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -547,7 +547,9 @@ end resources :pages, except: [:edit, :update] do - delete :remove_pages + collection do + delete :remove_pages + end end resources :compare, only: [:index, :create] resources :network, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } -- GitLab From ebe3572bfc78f0bc190e87a766c073d4606c2ec1 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 10 Feb 2016 16:45:59 +0100 Subject: [PATCH 07/28] Fix views --- app/models/pages_domain.rb | 4 +-- app/views/projects/pages/_list.html.haml | 33 ++++++++++++------------ app/views/projects/pages/show.html.haml | 6 ++--- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index 810af4e832af..985329bb8561 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -71,8 +71,6 @@ def fingerprint @fingeprint ||= OpenSSL::Digest::SHA256.new(x509.to_der).to_s end - private - def x509 return unless certificate @x509 ||= OpenSSL::X509::Certificate.new(certificate) @@ -87,6 +85,8 @@ def pkey nil end + private + def update ::Projects::UpdatePagesConfigurationService.new(project).execute end diff --git a/app/views/projects/pages/_list.html.haml b/app/views/projects/pages/_list.html.haml index 7dfeb0e6e12b..e88a001d636b 100644 --- a/app/views/projects/pages/_list.html.haml +++ b/app/views/projects/pages/_list.html.haml @@ -1,16 +1,17 @@ -.panel.panel-default - .panel-heading - Domains (#{@domains.count}) - %ul.well-list - - @domains.each do |domain| - %li - .pull-right - = link_to 'Details', namespace_project_page_path(@project.namespace, @project, domain), class: "btn btn-sm btn-grouped" - = link_to 'Remove', namespace_project_page_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 +- 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_page_path(@project.namespace, @project, domain), class: "btn btn-sm btn-grouped" + = link_to 'Remove', namespace_project_page_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 diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml index 98c4e8909681..52493b1959b4 100644 --- a/app/views/projects/pages/show.html.haml +++ b/app/views/projects/pages/show.html.haml @@ -1,7 +1,7 @@ - page_title "#{@domain.domain}", "Pages Domain" %h3.page-title - #{@domain.domain} + Pages Domain .table-holder %table.table @@ -14,9 +14,9 @@ %td Certificate %td - - if @domain.certificate + - if @domain.x509 %pre - = @domain.certificate.to_text + = @domain.x509.to_text - else .light missing -- GitLab From 807a5494fa543392ea7a996b479e577d41ff6344 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 12 Feb 2016 16:05:17 +0100 Subject: [PATCH 08/28] Pages domain model specs --- app/models/pages_domain.rb | 50 +++--- .../update_pages_configuration_service.rb | 22 ++- app/services/projects/update_pages_service.rb | 3 +- app/views/projects/pages/show.html.haml | 4 +- db/schema.rb | 13 +- spec/factories/pages_domains.rb | 79 +++++++++ spec/models/pages_domain_spec.rb | 152 ++++++++++++++++++ spec/models/project_spec.rb | 1 + ...r_spec.rb => update_pages_service_spec.rb} | 0 9 files changed, 294 insertions(+), 30 deletions(-) create mode 100644 spec/factories/pages_domains.rb create mode 100644 spec/models/pages_domain_spec.rb rename spec/services/projects/{update_pages_worker_spec.rb => update_pages_service_spec.rb} (100%) diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index 985329bb8561..b594957493a4 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -6,7 +6,8 @@ class PagesDomain < ActiveRecord::Base validates :certificate, certificate: true, allow_nil: true, allow_blank: true validates :key, certificate_key: true, allow_nil: true, allow_blank: true - validate :validate_matching_key, if: ->(domain) { domain.certificate.present? && domain.key.present? } + 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 @@ -30,8 +31,8 @@ def url end def has_matching_key? - return unless x509 - return unless pkey + 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) @@ -40,6 +41,9 @@ def has_matching_key? 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 @@ -66,23 +70,8 @@ def subject return x509.subject.to_s end - def fingerprint - return unless x509 - @fingeprint ||= OpenSSL::Digest::SHA256.new(x509.to_der).to_s - 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 + def certificate_text + @certificate_text ||= x509.try(:to_text) end private @@ -102,4 +91,25 @@ def validate_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 diff --git a/app/services/projects/update_pages_configuration_service.rb b/app/services/projects/update_pages_configuration_service.rb index 53e9d9e2757e..b5324587d0eb 100644 --- a/app/services/projects/update_pages_configuration_service.rb +++ b/app/services/projects/update_pages_configuration_service.rb @@ -35,7 +35,7 @@ def pages_domains_config def reload_daemon # GitLab Pages daemon constantly watches for modification time of `pages.path` # It reloads configuration when `pages.path` is modified - File.touch(Settings.pages.path) + update_file(pages_update_file, SecureRandom.hex(64)) end def pages_path @@ -46,14 +46,24 @@ 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) - if data - File.open(file, 'w') do |file| - file.write(data) - end - else + unless data File.rm(file, force: true) + return + end + + temp_file = "#{file}.#{SecureRandom.hex(16)}" + File.open(temp_file, 'w') do |file| + file.write(data) end + File.mv(temp_file, file, force: true) + ensure + # In case if the updating fails + File.rm(temp_file, force: true) end end end diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb index ceabd29fd52a..a9979bf1e96c 100644 --- a/app/services/projects/update_pages_service.rb +++ b/app/services/projects/update_pages_service.rb @@ -1,5 +1,6 @@ module Projects - class UpdatePagesService < BaseService + class + UpdatePagesService < BaseService BLOCK_SIZE = 32.kilobytes MAX_SIZE = 1.terabyte SITE_PATH = 'public/' diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml index 52493b1959b4..8b7010b75b29 100644 --- a/app/views/projects/pages/show.html.haml +++ b/app/views/projects/pages/show.html.haml @@ -14,9 +14,9 @@ %td Certificate %td - - if @domain.x509 + - if @domain.certificate_text %pre - = @domain.x509.to_text + = @domain.certificate_text - else .light missing diff --git a/db/schema.rb b/db/schema.rb index cb85f32fc776..0887f801ba13 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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 @@ 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 diff --git a/spec/factories/pages_domains.rb b/spec/factories/pages_domains.rb new file mode 100644 index 000000000000..4608867087cf --- /dev/null +++ b/spec/factories/pages_domains.rb @@ -0,0 +1,79 @@ +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_certificate_chain do + # This certificate is signed with different key + 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_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 diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb new file mode 100644 index 000000000000..929b2a26549e --- /dev/null +++ b/spec/models/pages_domain_spec.rb @@ -0,0 +1,152 @@ +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_certificate_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_certificate_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 certificate chain without the root' do + let(:domain) { build(:pages_domain, :with_certificate_chain) } + + it { is_expected.to be_falsey } + 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 diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 8b8e0deec60e..92aff1ff333a 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -68,6 +68,7 @@ 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 diff --git a/spec/services/projects/update_pages_worker_spec.rb b/spec/services/projects/update_pages_service_spec.rb similarity index 100% rename from spec/services/projects/update_pages_worker_spec.rb rename to spec/services/projects/update_pages_service_spec.rb -- GitLab From 83d3ffb1628f406da878a0d9cf01f244043df834 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 14 Feb 2016 19:58:45 +0100 Subject: [PATCH 09/28] Fix rubocop complains --- app/controllers/projects/pages_controller.rb | 21 ++++++++++---------- app/models/pages_domain.rb | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb index 0c7f2bd5784e..2268d2d8aa25 100644 --- a/app/controllers/projects/pages_controller.rb +++ b/app/controllers/projects/pages_controller.rb @@ -43,26 +43,25 @@ def destroy end end - def destroy - @project.remove_pages - - respond_to do |format| - format.html { redirect_to project_path(@project) } - end - end - def remove_pages 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 private def pages_domain_params params.require(:pages_domain).permit( - :certificate, - :key, - :domain + :certificate, + :key, + :domain ) end diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index b594957493a4..83fdc1c630d2 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -62,12 +62,12 @@ def has_intermediates? def expired? return false unless x509 current = Time.new - return current < x509.not_before || x509.not_after < current + current < x509.not_before || x509.not_after < current end def subject return unless x509 - return x509.subject.to_s + x509.subject.to_s end def certificate_text -- GitLab From 0f51b37fdc52949a1878c2d66d9e51930e53bdb1 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 14 Feb 2016 20:28:02 +0100 Subject: [PATCH 10/28] Add tests for Active Tab --- app/views/projects/pages/_disabled.html.haml | 2 +- features/project/active_tab.feature | 7 +++++++ features/steps/project/active_tab.rb | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/views/projects/pages/_disabled.html.haml b/app/views/projects/pages/_disabled.html.haml index cf9ef5b4d6f6..ad51fbc6cab5 100644 --- a/app/views/projects/pages/_disabled.html.haml +++ b/app/views/projects/pages/_disabled.html.haml @@ -1,4 +1,4 @@ .panel.panel-default .nothing-here-block - GitLab Pages is disabled. + GitLab Pages are disabled. Ask your system's administrator to enable it. diff --git a/features/project/active_tab.feature b/features/project/active_tab.feature index 2fd097d100bc..0db212da54ee 100644 --- a/features/project/active_tab.feature +++ b/features/project/active_tab.feature @@ -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 diff --git a/features/steps/project/active_tab.rb b/features/steps/project/active_tab.rb index 9e96fa5ba494..c19f22f09f8c 100644 --- a/features/steps/project/active_tab.rb +++ b/features/steps/project/active_tab.rb @@ -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 -- GitLab From 5b874877eee19370b875a2085c178c600d753fb7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 14 Feb 2016 21:06:24 +0100 Subject: [PATCH 11/28] Added spinach tests --- .../projects/pages_domains_controller.rb | 0 app/helpers/projects_helper.rb | 4 - app/views/projects/pages/_destroy.haml | 9 +- app/views/projects/pages/_form.html.haml | 2 +- app/views/projects/pages/index.html.haml | 6 +- features/project/pages.feature | 73 +++++++++ features/steps/project/pages.rb | 139 ++++++++++++++++++ 7 files changed, 220 insertions(+), 13 deletions(-) create mode 100644 app/controllers/projects/pages_domains_controller.rb create mode 100644 features/project/pages.feature create mode 100644 features/steps/project/pages.rb diff --git a/app/controllers/projects/pages_domains_controller.rb b/app/controllers/projects/pages_domains_controller.rb new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index f080a71eec09..93d530ef7694 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -90,10 +90,6 @@ def remove_fork_project_message(project) "You are going to remove the fork relationship to source project #{@project.forked_from_project.name_with_namespace}. Are you ABSOLUTELY sure?" end - def remove_pages_message(project) - "You are going to remove the pages for #{project.name_with_namespace}.\n Are you ABSOLUTELY sure?" - end - def project_nav_tabs @nav_tabs ||= get_project_nav_tabs(@project, current_user) end diff --git a/app/views/projects/pages/_destroy.haml b/app/views/projects/pages/_destroy.haml index dd493a6d312e..c560aca5725e 100644 --- a/app/views/projects/pages/_destroy.haml +++ b/app/views/projects/pages/_destroy.haml @@ -3,8 +3,7 @@ .panel-heading Remove pages .errors-holder .panel-body - = form_tag(remove_pages_namespace_project_pages_path(@project.namespace, @project), method: :delete, class: 'form-horizontal') do - %p - Removing the pages will prevent from exposing them to outside world. - .form-actions - = button_to 'Remove pages', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_pages_message(@project) } + %p + Removing the pages will prevent from exposing them to outside world. + .form-actions + = link_to 'Remove', remove_pages_namespace_project_pages_path(@project.namespace, @project), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove" diff --git a/app/views/projects/pages/_form.html.haml b/app/views/projects/pages/_form.html.haml index c69b76c6697e..fd4114623309 100644 --- a/app/views/projects/pages/_form.html.haml +++ b/app/views/projects/pages/_form.html.haml @@ -12,7 +12,7 @@ = f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control' %span.help-inline * required - - if Settings.pages.external_https + - if Gitlab.config.pages.external_https .form-group = f.label :certificate, class: 'control-label' do Certificate (PEM) diff --git a/app/views/projects/pages/index.html.haml b/app/views/projects/pages/index.html.haml index 284f362d535d..1a5dbb79830d 100644 --- a/app/views/projects/pages/index.html.haml +++ b/app/views/projects/pages/index.html.haml @@ -2,7 +2,7 @@ %h3.page_title Pages - - if Settings.pages.external_http || Settings.pages.external_https + - if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https = link_to new_namespace_project_page_path(@project.namespace, @project), class: "btn btn-new pull-right", title: "New Domain" do %i.fa.fa-plus New Domain @@ -14,10 +14,10 @@ %hr.clearfix -- if Settings.pages.enabled +- if Gitlab.config.pages.enabled = render 'access' = render 'use' - - if Settings.pages.external_http || Settings.pages.external_https + - if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https = render 'list' - else = render 'no_domains' diff --git a/features/project/pages.feature b/features/project/pages.feature new file mode 100644 index 000000000000..392f2d29c3c5 --- /dev/null +++ b/features/project/pages.feature @@ -0,0 +1,73 @@ +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 diff --git a/features/steps/project/pages.rb b/features/steps/project/pages.rb new file mode 100644 index 000000000000..d484ae90bdc0 --- /dev/null +++ b/features/steps/project/pages.rb @@ -0,0 +1,139 @@ +class Spinach::Features::ProjectPages < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedProject + + step 'pages are enabled' do + Gitlab.config.pages.stub(:enabled).and_return(true) + Gitlab.config.pages.stub(:host).and_return('example.com') + Gitlab.config.pages.stub(:port).and_return(80) + Gitlab.config.pages.stub(:https).and_return(false) + end + + step 'pages are disabled' do + Gitlab.config.pages.stub(: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 + Gitlab.config.pages.stub(:external_http).and_return(nil) + Gitlab.config.pages.stub(:external_https).and_return(nil) + end + + step 'pages are exposed on external HTTP address' do + Gitlab.config.pages.stub(:external_http).and_return('1.1.1.1:80') + Gitlab.config.pages.stub(:external_https).and_return(nil) + end + + step 'pages are exposed on external HTTPS address' do + Gitlab.config.pages.stub(:external_http).and_return('1.1.1.1:80') + Gitlab.config.pages.stub(: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_page_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 -- GitLab From 120dfcad4514fef2f96e20a062adc266461c011d Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 14 Feb 2016 21:22:44 +0100 Subject: [PATCH 12/28] Split PagesController into PagesController and PagesDomainsController 1. PagesController is used to show all domains and general overview of Pages 2. PagesDomainsController is used to manage pages domains --- CHANGELOG-EE | 1 + app/controllers/projects/pages_controller.rb | 54 +------------------ .../projects/pages_domains_controller.rb | 49 +++++++++++++++++ app/views/projects/pages/_destroy.haml | 2 +- app/views/projects/pages/_list.html.haml | 4 +- app/views/projects/pages/index.html.haml | 26 --------- app/views/projects/pages/show.html.haml | 44 ++++++++------- .../{pages => pages_domains}/_form.html.haml | 2 +- .../{pages => pages_domains}/new.html.haml | 0 .../projects/pages_domains/show.html.haml | 22 ++++++++ config/routes.rb | 7 ++- features/steps/project/pages.rb | 2 +- 12 files changed, 106 insertions(+), 107 deletions(-) delete mode 100644 app/views/projects/pages/index.html.haml rename app/views/projects/{pages => pages_domains}/_form.html.haml (88%) rename app/views/projects/{pages => pages_domains}/new.html.haml (100%) create mode 100644 app/views/projects/pages_domains/show.html.haml diff --git a/CHANGELOG-EE b/CHANGELOG-EE index adea080676bd..a9985968398d 100644 --- a/CHANGELOG-EE +++ b/CHANGELOG-EE @@ -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 diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb index 2268d2d8aa25..b73f998392d1 100644 --- a/app/controllers/projects/pages_controller.rb +++ b/app/controllers/projects/pages_controller.rb @@ -1,49 +1,13 @@ class Projects::PagesController < Projects::ApplicationController layout 'project_settings' - before_action :authorize_update_pages!, except: [:show] - before_action :authorize_remove_pages!, only: [:remove_pages] - before_action :label, only: [:destroy] - before_action :domain, only: [:show] - - helper_method :valid_certificate?, :valid_certificate_key? - helper_method :valid_key_for_certificiate?, :valid_certificate_intermediates? - helper_method :certificate, :certificate_key - - def index - @domains = @project.pages_domains.order(:domain) - end + before_action :authorize_update_pages! 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 + @domains = @project.pages_domains.order(:domain) 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 - - def remove_pages project.remove_pages project.pages_domains.destroy_all @@ -54,18 +18,4 @@ def remove_pages end 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 diff --git a/app/controllers/projects/pages_domains_controller.rb b/app/controllers/projects/pages_domains_controller.rb index e69de29bb2d1..b8c253f6ae3f 100644 --- a/app/controllers/projects/pages_domains_controller.rb +++ b/app/controllers/projects/pages_domains_controller.rb @@ -0,0 +1,49 @@ +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 diff --git a/app/views/projects/pages/_destroy.haml b/app/views/projects/pages/_destroy.haml index c560aca5725e..0cd25f82cd4b 100644 --- a/app/views/projects/pages/_destroy.haml +++ b/app/views/projects/pages/_destroy.haml @@ -6,4 +6,4 @@ %p Removing the pages will prevent from exposing them to outside world. .form-actions - = link_to 'Remove', remove_pages_namespace_project_pages_path(@project.namespace, @project), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove" + = link_to 'Remove pages', namespace_project_pages_path(@project.namespace, @project), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove" diff --git a/app/views/projects/pages/_list.html.haml b/app/views/projects/pages/_list.html.haml index e88a001d636b..c1a6948a5746 100644 --- a/app/views/projects/pages/_list.html.haml +++ b/app/views/projects/pages/_list.html.haml @@ -6,8 +6,8 @@ - @domains.each do |domain| %li .pull-right - = link_to 'Details', namespace_project_page_path(@project.namespace, @project, domain), class: "btn btn-sm btn-grouped" - = link_to 'Remove', namespace_project_page_path(@project.namespace, @project, domain), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped" + = 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 diff --git a/app/views/projects/pages/index.html.haml b/app/views/projects/pages/index.html.haml deleted file mode 100644 index 1a5dbb79830d..000000000000 --- a/app/views/projects/pages/index.html.haml +++ /dev/null @@ -1,26 +0,0 @@ -- page_title "Pages" -%h3.page_title - Pages - - - if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https - = link_to new_namespace_project_page_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 for free 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' diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml index 8b7010b75b29..9be6f8678cf1 100644 --- a/app/views/projects/pages/show.html.haml +++ b/app/views/projects/pages/show.html.haml @@ -1,22 +1,26 @@ -- page_title "#{@domain.domain}", "Pages Domain" +- page_title "Pages" +%h3.page_title + Pages -%h3.page-title - Pages Domain + - 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 -.table-holder - %table.table - %tr - %td - Domain - %td - = link_to @domain.domain, @domain.url - %tr - %td - Certificate - %td - - if @domain.certificate_text - %pre - = @domain.certificate_text - - else - .light - missing +%p.light + With GitLab Pages you can host for free 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' diff --git a/app/views/projects/pages/_form.html.haml b/app/views/projects/pages_domains/_form.html.haml similarity index 88% rename from app/views/projects/pages/_form.html.haml rename to app/views/projects/pages_domains/_form.html.haml index fd4114623309..5458f9e7734b 100644 --- a/app/views/projects/pages/_form.html.haml +++ b/app/views/projects/pages_domains/_form.html.haml @@ -1,4 +1,4 @@ -= form_for [@domain], url: namespace_project_pages_path(@project.namespace, @project), html: { class: 'form-horizontal fieldset-form' } do |f| += form_for [@project.namespace, @project, @domain], html: { class: 'form-horizontal fieldset-form' } do |f| - if @domain.errors.any? #error_explanation .alert.alert-danger diff --git a/app/views/projects/pages/new.html.haml b/app/views/projects/pages_domains/new.html.haml similarity index 100% rename from app/views/projects/pages/new.html.haml rename to app/views/projects/pages_domains/new.html.haml diff --git a/app/views/projects/pages_domains/show.html.haml b/app/views/projects/pages_domains/show.html.haml new file mode 100644 index 000000000000..8b7010b75b29 --- /dev/null +++ b/app/views/projects/pages_domains/show.html.haml @@ -0,0 +1,22 @@ +- page_title "#{@domain.domain}", "Pages Domain" + +%h3.page-title + Pages Domain + +.table-holder + %table.table + %tr + %td + Domain + %td + = link_to @domain.domain, @domain.url + %tr + %td + Certificate + %td + - if @domain.certificate_text + %pre + = @domain.certificate_text + - else + .light + missing diff --git a/config/routes.rb b/config/routes.rb index 1b0f15ef880b..6a7ffbcf2b6d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -546,11 +546,10 @@ end end - resources :pages, except: [:edit, :update] do - collection do - delete :remove_pages - 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/ } diff --git a/features/steps/project/pages.rb b/features/steps/project/pages.rb index d484ae90bdc0..a5cb81b0ef37 100644 --- a/features/steps/project/pages.rb +++ b/features/steps/project/pages.rb @@ -67,7 +67,7 @@ class Spinach::Features::ProjectPages < Spinach::FeatureSteps end step 'I visit add a new Pages Domain' do - visit new_namespace_project_page_path(@project.namespace, @project) + visit new_namespace_project_pages_domain_path(@project.namespace, @project) end step 'I fill the domain' do -- GitLab From 36836589a056e9a5ca14793e55d8f41ac7443275 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 15 Feb 2016 14:44:54 +0100 Subject: [PATCH 13/28] Updated configuration saving --- .../projects/update_pages_configuration_service.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/services/projects/update_pages_configuration_service.rb b/app/services/projects/update_pages_configuration_service.rb index b5324587d0eb..188847b5ad66 100644 --- a/app/services/projects/update_pages_configuration_service.rb +++ b/app/services/projects/update_pages_configuration_service.rb @@ -7,7 +7,7 @@ def initialize(project) end def execute - update_file(pages_config_file, pages_config) + update_file(pages_config_file, pages_config.to_json) reload_daemon success rescue => e @@ -52,18 +52,18 @@ def pages_update_file def update_file(file, data) unless data - File.rm(file, force: true) + FileUtils.remove(file, force: true) return end temp_file = "#{file}.#{SecureRandom.hex(16)}" - File.open(temp_file, 'w') do |file| - file.write(data) + File.open(temp_file, 'w') do |f| + f.write(data) end - File.mv(temp_file, file, force: true) + FileUtils.move(temp_file, file, force: true) ensure # In case if the updating fails - File.rm(temp_file, force: true) + FileUtils.remove(temp_file, force: true) end end end -- GitLab From 4ef481bdef4f26889c9f1363f4655286ed5e4724 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 15 Feb 2016 14:45:11 +0100 Subject: [PATCH 14/28] Added information about the CNAME record --- app/views/projects/pages_domains/show.html.haml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/views/projects/pages_domains/show.html.haml b/app/views/projects/pages_domains/show.html.haml index 8b7010b75b29..e4c5922d863f 100644 --- a/app/views/projects/pages_domains/show.html.haml +++ b/app/views/projects/pages_domains/show.html.haml @@ -10,6 +10,14 @@ 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 -- GitLab From 313432a5e9e99357b9f83b98eaa2d5ac90d3cb63 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 15 Feb 2016 15:01:42 +0100 Subject: [PATCH 15/28] Updated according to comments --- app/models/pages_domain.rb | 4 ++-- app/services/projects/update_pages_service.rb | 3 +-- app/views/projects/pages/show.html.haml | 6 +++--- app/views/projects/pages_domains/_form.html.haml | 7 +++---- app/views/projects/pages_domains/new.html.haml | 2 +- app/views/projects/pages_domains/show.html.haml | 2 +- 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index 83fdc1c630d2..9155e57331d7 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -24,9 +24,9 @@ def url return unless domain if certificate - return "https://#{domain}" + "https://#{domain}" else - return "http://#{domain}" + "http://#{domain}" end end diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb index a9979bf1e96c..ceabd29fd52a 100644 --- a/app/services/projects/update_pages_service.rb +++ b/app/services/projects/update_pages_service.rb @@ -1,6 +1,5 @@ module Projects - class - UpdatePagesService < BaseService + class UpdatePagesService < BaseService BLOCK_SIZE = 32.kilobytes MAX_SIZE = 1.terabyte SITE_PATH = 'public/' diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml index 9be6f8678cf1..f4ca33f418b5 100644 --- a/app/views/projects/pages/show.html.haml +++ b/app/views/projects/pages/show.html.haml @@ -1,14 +1,14 @@ -- page_title "Pages" +- 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 + = 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 for free your static websites on GitLab. + 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. diff --git a/app/views/projects/pages_domains/_form.html.haml b/app/views/projects/pages_domains/_form.html.haml index 5458f9e7734b..e97d19653d5a 100644 --- a/app/views/projects/pages_domains/_form.html.haml +++ b/app/views/projects/pages_domains/_form.html.haml @@ -10,22 +10,21 @@ Domain .col-sm-10 = f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control' - %span.help-inline * required - 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', value: '' + = 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', value: '' - %span.help-inline Upload a certificate for your domain with all intermediates + = 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. diff --git a/app/views/projects/pages_domains/new.html.haml b/app/views/projects/pages_domains/new.html.haml index 2609df62aac2..e1477c71d06d 100644 --- a/app/views/projects/pages_domains/new.html.haml +++ b/app/views/projects/pages_domains/new.html.haml @@ -1,4 +1,4 @@ -- page_title 'Pages' +- page_title 'New Pages Domain' %h3.page_title New Pages Domain %hr.clearfix diff --git a/app/views/projects/pages_domains/show.html.haml b/app/views/projects/pages_domains/show.html.haml index e4c5922d863f..52dddb052a79 100644 --- a/app/views/projects/pages_domains/show.html.haml +++ b/app/views/projects/pages_domains/show.html.haml @@ -1,4 +1,4 @@ -- page_title "#{@domain.domain}", "Pages Domain" +- page_title "#{@domain.domain}", 'Pages Domains' %h3.page-title Pages Domain -- GitLab From bee5c73f5bdbf404b6c17783849a1e5afc46ef3c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 15 Feb 2016 15:18:34 +0100 Subject: [PATCH 16/28] Change the pages icon to cloud --- .../layouts/nav/_project_settings.html.haml | 2 +- db/schema.rb | 618 +++++++++--------- 2 files changed, 317 insertions(+), 303 deletions(-) diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 80a621edfbfe..76b9d39b7bca 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -51,7 +51,7 @@ Mirror Repository = nav_link(controller: :pages) do = link_to namespace_project_pages_path(@project.namespace, @project), title: 'Pages', data: {placement: 'right'} do - = icon('clone fw') + = icon('cloud fw') %span Pages = nav_link(controller: :audit_events) do diff --git a/db/schema.rb b/db/schema.rb index 0887f801ba13..37e67d972135 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -43,43 +43,43 @@ t.text "sign_in_text" t.datetime "created_at" t.datetime "updated_at" - t.string "home_page_url" - t.integer "default_branch_protection", default: 2 - t.boolean "twitter_sharing_enabled", default: true - t.text "help_text" + t.string "home_page_url", limit: 255 + t.integer "default_branch_protection", default: 2 + t.boolean "twitter_sharing_enabled", default: true t.text "restricted_visibility_levels" - t.boolean "version_check_enabled", default: true - t.integer "max_attachment_size", default: 10, null: false + t.integer "max_attachment_size", default: 10, null: false t.integer "default_project_visibility" t.integer "default_snippet_visibility" t.text "restricted_signup_domains" - t.boolean "user_oauth_applications", default: true - t.string "after_sign_out_path" - t.integer "session_expire_delay", default: 10080, null: false + t.boolean "version_check_enabled", default: true + t.boolean "user_oauth_applications", default: true + t.string "after_sign_out_path", limit: 255 + t.integer "session_expire_delay", default: 10080, null: false t.text "import_sources" t.text "help_page_text" - t.string "admin_notification_email" - t.boolean "shared_runners_enabled", default: true, null: false - t.integer "max_artifacts_size", default: 100, null: false + t.string "admin_notification_email", limit: 255 + t.boolean "shared_runners_enabled", default: true, null: false + t.integer "max_artifacts_size", default: 100, null: false t.string "runners_registration_token" - t.integer "max_pages_size", default: 100, null: false - t.boolean "require_two_factor_authentication", default: false - t.integer "two_factor_grace_period", default: 48 - t.boolean "metrics_enabled", default: false - t.string "metrics_host", default: "localhost" - t.integer "metrics_pool_size", default: 16 - t.integer "metrics_timeout", default: 10 - t.integer "metrics_method_call_threshold", default: 10 - t.boolean "recaptcha_enabled", default: false + t.integer "max_pages_size", default: 100, null: false + t.text "help_text" + t.boolean "require_two_factor_authentication", default: false + t.integer "two_factor_grace_period", default: 48 + t.boolean "metrics_enabled", default: false + t.string "metrics_host", default: "localhost" + t.integer "metrics_pool_size", default: 16 + t.integer "metrics_timeout", default: 10 + t.integer "metrics_method_call_threshold", default: 10 + t.boolean "recaptcha_enabled", default: false t.string "recaptcha_site_key" t.string "recaptcha_private_key" - t.integer "metrics_port", default: 8089 - t.integer "metrics_sample_interval", default: 15 - t.boolean "sentry_enabled", default: false + t.integer "metrics_port", default: 8089 + t.integer "metrics_sample_interval", default: 15 + t.boolean "sentry_enabled", default: false t.string "sentry_dsn" - t.boolean "akismet_enabled", default: false + t.boolean "akismet_enabled", default: false t.string "akismet_api_key" - t.boolean "email_author_in_body", default: false + t.boolean "email_author_in_body", default: false end create_table "approvals", force: :cascade do |t| @@ -101,10 +101,10 @@ add_index "approvers", ["user_id"], name: "index_approvers_on_user_id", using: :btree create_table "audit_events", force: :cascade do |t| - t.integer "author_id", null: false - t.string "type", null: false - t.integer "entity_id", null: false - t.string "entity_type", null: false + t.integer "author_id", null: false + t.string "type", limit: 255, null: false + t.integer "entity_id", null: false + t.string "entity_type", limit: 255, null: false t.text "details" t.datetime "created_at" t.datetime "updated_at" @@ -115,13 +115,13 @@ add_index "audit_events", ["type"], name: "index_audit_events_on_type", using: :btree create_table "broadcast_messages", force: :cascade do |t| - t.text "message", null: false + t.text "message", null: false t.datetime "starts_at" t.datetime "ends_at" - t.datetime "created_at" - t.datetime "updated_at" - t.string "color" - t.string "font" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "color", limit: 510 + t.string "font", limit: 510 end create_table "ci_application_settings", force: :cascade do |t| @@ -133,7 +133,7 @@ create_table "ci_builds", force: :cascade do |t| t.integer "project_id" - t.string "status" + t.string "status", limit: 255 t.datetime "finished_at" t.text "trace" t.datetime "created_at" @@ -144,19 +144,19 @@ t.integer "commit_id" t.text "commands" t.integer "job_id" - t.string "name" - t.boolean "deploy", default: false + t.string "name", limit: 255 + t.boolean "deploy", default: false t.text "options" - t.boolean "allow_failure", default: false, null: false - t.string "stage" + t.boolean "allow_failure", default: false, null: false + t.string "stage", limit: 255 t.integer "trigger_request_id" t.integer "stage_idx" t.boolean "tag" - t.string "ref" + t.string "ref", limit: 255 t.integer "user_id" - t.string "type" - t.string "target_url" - t.string "description" + t.string "type", limit: 255 + t.string "target_url", limit: 255 + t.string "description", limit: 255 t.text "artifacts_file" t.integer "gl_project_id" t.text "artifacts_metadata" @@ -167,22 +167,26 @@ add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree add_index "ci_builds", ["commit_id"], name: "index_ci_builds_on_commit_id", using: :btree + add_index "ci_builds", ["gl_project_id", "status", "type"], name: "index_ci_builds_on_gl_project_id_and_status_and_type", using: :btree + add_index "ci_builds", ["gl_project_id", "status"], name: "index_ci_builds_on_gl_project_id_and_status", using: :btree + add_index "ci_builds", ["gl_project_id", "type"], name: "index_ci_builds_on_gl_project_id_and_type", using: :btree add_index "ci_builds", ["gl_project_id"], name: "index_ci_builds_on_gl_project_id", using: :btree add_index "ci_builds", ["project_id", "commit_id"], name: "index_ci_builds_on_project_id_and_commit_id", using: :btree add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree + add_index "ci_builds", ["type", "status", "runner_id"], name: "index_ci_builds_on_test2", using: :btree add_index "ci_builds", ["type"], name: "index_ci_builds_on_type", using: :btree create_table "ci_commits", force: :cascade do |t| t.integer "project_id" - t.string "ref" - t.string "sha" - t.string "before_sha" + t.string "ref", limit: 255 + t.string "sha", limit: 255 + t.string "before_sha", limit: 255 t.text "push_data" t.datetime "created_at" t.datetime "updated_at" - t.boolean "tag", default: false + t.boolean "tag", default: false t.text "yaml_errors" t.datetime "committed_at" t.integer "gl_project_id" @@ -209,16 +213,16 @@ add_index "ci_events", ["project_id"], name: "index_ci_events_on_project_id", using: :btree create_table "ci_jobs", force: :cascade do |t| - t.integer "project_id", null: false + t.integer "project_id", null: false t.text "commands" - t.boolean "active", default: true, null: false + t.boolean "active", default: true, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "name" - t.boolean "build_branches", default: true, null: false - t.boolean "build_tags", default: false, null: false - t.string "job_type", default: "parallel" - t.string "refs" + t.string "name", limit: 255 + t.boolean "build_branches", default: true, null: false + t.boolean "build_tags", default: false, null: false + t.string "job_type", limit: 255, default: "parallel" + t.string "refs", limit: 255 t.datetime "deleted_at" end @@ -226,25 +230,25 @@ add_index "ci_jobs", ["project_id"], name: "index_ci_jobs_on_project_id", using: :btree create_table "ci_projects", force: :cascade do |t| - t.string "name" - t.integer "timeout", default: 3600, null: false + t.string "name", limit: 255 + t.integer "timeout", default: 3600, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "token" - t.string "default_ref" - t.string "path" - t.boolean "always_build", default: false, null: false + t.string "token", limit: 255 + t.string "default_ref", limit: 255 + t.string "path", limit: 255 + t.boolean "always_build", default: false, null: false t.integer "polling_interval" - t.boolean "public", default: false, null: false - t.string "ssh_url_to_repo" + t.boolean "public", default: false, null: false + t.string "ssh_url_to_repo", limit: 255 t.integer "gitlab_id" - t.boolean "allow_git_fetch", default: true, null: false - t.string "email_recipients", default: "", null: false - t.boolean "email_add_pusher", default: true, null: false - t.boolean "email_only_broken_builds", default: true, null: false - t.string "skip_refs" - t.string "coverage_regex" - t.boolean "shared_runners_enabled", default: false + t.boolean "allow_git_fetch", default: true, null: false + t.string "email_recipients", limit: 255, default: "", null: false + t.boolean "email_add_pusher", default: true, null: false + t.boolean "email_only_broken_builds", default: true, null: false + t.string "skip_refs", limit: 255 + t.string "coverage_regex", limit: 255 + t.boolean "shared_runners_enabled", default: false t.text "generated_yaml_config" end @@ -263,34 +267,34 @@ add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree create_table "ci_runners", force: :cascade do |t| - t.string "token" + t.string "token", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.string "description" + t.string "description", limit: 255 t.datetime "contacted_at" - t.boolean "active", default: true, null: false - t.boolean "is_shared", default: false - t.string "name" - t.string "version" - t.string "revision" - t.string "platform" - t.string "architecture" + t.boolean "active", default: true, null: false + t.boolean "is_shared", default: false + t.string "name", limit: 255 + t.string "version", limit: 255 + t.string "revision", limit: 255 + t.string "platform", limit: 255 + t.string "architecture", limit: 255 end create_table "ci_services", force: :cascade do |t| - t.string "type" - t.string "title" - t.integer "project_id", null: false + t.string "type", limit: 255 + t.string "title", limit: 255 + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.boolean "active", default: false, null: false + t.boolean "active", default: false, null: false t.text "properties" end add_index "ci_services", ["project_id"], name: "index_ci_services_on_project_id", using: :btree create_table "ci_sessions", force: :cascade do |t| - t.string "session_id", null: false + t.string "session_id", limit: 255, null: false t.text "data" t.datetime "created_at" t.datetime "updated_at" @@ -302,9 +306,9 @@ create_table "ci_taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" - t.string "taggable_type" + t.string "taggable_type", limit: 255 t.integer "tagger_id" - t.string "tagger_type" + t.string "tagger_type", limit: 255 t.string "context", limit: 128 t.datetime "created_at" end @@ -313,8 +317,8 @@ add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "ci_tags", force: :cascade do |t| - t.string "name" - t.integer "taggings_count", default: 0 + t.string "name", limit: 255 + t.integer "taggings_count", default: 0 end add_index "ci_tags", ["name"], name: "index_ci_tags_on_name", unique: true, using: :btree @@ -328,7 +332,7 @@ end create_table "ci_triggers", force: :cascade do |t| - t.string "token" + t.string "token", limit: 255 t.integer "project_id" t.datetime "deleted_at" t.datetime "created_at" @@ -341,19 +345,19 @@ create_table "ci_variables", force: :cascade do |t| t.integer "project_id" - t.string "key" + t.string "key", limit: 255 t.text "value" t.text "encrypted_value" - t.string "encrypted_value_salt" - t.string "encrypted_value_iv" + t.string "encrypted_value_salt", limit: 255 + t.string "encrypted_value_iv", limit: 255 t.integer "gl_project_id" end add_index "ci_variables", ["gl_project_id"], name: "index_ci_variables_on_gl_project_id", using: :btree create_table "ci_web_hooks", force: :cascade do |t| - t.string "url", null: false - t.integer "project_id", null: false + t.string "url", limit: 255, null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" end @@ -361,30 +365,30 @@ create_table "deploy_keys_projects", force: :cascade do |t| t.integer "deploy_key_id", null: false t.integer "project_id", null: false - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree create_table "emails", force: :cascade do |t| - t.integer "user_id", null: false - t.string "email", null: false + t.integer "user_id", null: false + t.string "email", limit: 510, null: false t.datetime "created_at" t.datetime "updated_at" end - add_index "emails", ["email"], name: "index_emails_on_email", unique: true, using: :btree + add_index "emails", ["email"], name: "emails_email_key", unique: true, using: :btree add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree create_table "events", force: :cascade do |t| - t.string "target_type" + t.string "target_type", limit: 510 t.integer "target_id" - t.string "title" + t.string "title", limit: 510 t.text "data" t.integer "project_id" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.integer "action" t.integer "author_id" end @@ -399,11 +403,11 @@ create_table "forked_project_links", force: :cascade do |t| t.integer "forked_to_project_id", null: false t.integer "forked_from_project_id", null: false - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end - add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree + add_index "forked_project_links", ["forked_to_project_id"], name: "forked_project_links_forked_to_project_id_key", unique: true, using: :btree create_table "geo_nodes", force: :cascade do |t| t.string "schema" @@ -439,8 +443,8 @@ end create_table "identities", force: :cascade do |t| - t.string "extern_uid" - t.string "provider" + t.string "extern_uid", limit: 255 + t.string "provider", limit: 255 t.integer "user_id" t.datetime "created_at" t.datetime "updated_at" @@ -461,17 +465,17 @@ add_index "index_statuses", ["project_id"], name: "index_index_statuses_on_project_id", unique: true, using: :btree create_table "issues", force: :cascade do |t| - t.string "title" + t.string "title", limit: 510 t.integer "assignee_id" t.integer "author_id" t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.integer "position", default: 0 - t.string "branch_name" + t.integer "position", default: 0 + t.string "branch_name", limit: 510 t.text "description" t.integer "milestone_id" - t.string "state" + t.string "state", limit: 510 t.integer "iid" t.integer "updated_by_id" t.integer "weight" @@ -492,10 +496,10 @@ t.datetime "created_at" t.datetime "updated_at" t.text "key" - t.string "title" - t.string "type" - t.string "fingerprint" - t.boolean "public", default: false, null: false + t.string "title", limit: 510 + t.string "type", limit: 510 + t.string "fingerprint", limit: 510 + t.boolean "public", default: false, null: false end add_index "keys", ["created_at", "id"], name: "index_keys_on_created_at_and_id", using: :btree @@ -504,7 +508,7 @@ create_table "label_links", force: :cascade do |t| t.integer "label_id" t.integer "target_id" - t.string "target_type" + t.string "target_type", limit: 255 t.datetime "created_at" t.datetime "updated_at" end @@ -513,12 +517,12 @@ add_index "label_links", ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree create_table "labels", force: :cascade do |t| - t.string "title" - t.string "color" + t.string "title", limit: 255 + t.string "color", limit: 255 t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.boolean "template", default: false + t.boolean "template", default: false end add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree @@ -533,11 +537,11 @@ end create_table "lfs_objects", force: :cascade do |t| - t.string "oid", null: false - t.integer "size", limit: 8, null: false + t.string "oid", limit: 255, null: false + t.integer "size", limit: 8, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "file" + t.string "file", limit: 255 end add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree @@ -558,17 +562,17 @@ end create_table "members", force: :cascade do |t| - t.integer "access_level", null: false - t.integer "source_id", null: false - t.string "source_type", null: false + t.integer "access_level", null: false + t.integer "source_id", null: false + t.string "source_type", limit: 255, null: false t.integer "user_id" - t.integer "notification_level", null: false - t.string "type" + t.integer "notification_level", null: false + t.string "type", limit: 255 t.datetime "created_at" t.datetime "updated_at" t.integer "created_by_id" - t.string "invite_email" - t.string "invite_token" + t.string "invite_email", limit: 255 + t.string "invite_token", limit: 255 t.datetime "invite_accepted_at" end @@ -580,10 +584,10 @@ add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree create_table "merge_request_diffs", force: :cascade do |t| - t.string "state" + t.string "state", limit: 255 t.text "st_commits" t.text "st_diffs" - t.integer "merge_request_id", null: false + t.integer "merge_request_id", null: false t.datetime "created_at" t.datetime "updated_at" t.string "base_commit_sha" @@ -592,26 +596,26 @@ add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree create_table "merge_requests", force: :cascade do |t| - t.string "target_branch", null: false - t.string "source_branch", null: false - t.integer "source_project_id", null: false + t.string "target_branch", limit: 510, null: false + t.string "source_branch", limit: 510, null: false + t.integer "source_project_id", null: false t.integer "author_id" t.integer "assignee_id" - t.string "title" + t.string "title", limit: 510 t.datetime "created_at" t.datetime "updated_at" t.integer "milestone_id" - t.string "state" - t.string "merge_status" - t.integer "target_project_id", null: false + t.string "state", limit: 510 + t.string "merge_status", limit: 510 + t.integer "target_project_id", null: false t.integer "iid" t.text "description" - t.integer "position", default: 0 + t.integer "position", default: 0 t.datetime "locked_at" t.integer "updated_by_id" - t.string "merge_error" + t.string "merge_error", limit: 255 t.text "merge_params" - t.boolean "merge_when_build_succeeds", default: false, null: false + t.boolean "merge_when_build_succeeds", default: false, null: false t.integer "merge_user_id" end @@ -627,13 +631,13 @@ add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree create_table "milestones", force: :cascade do |t| - t.string "title", null: false - t.integer "project_id", null: false + t.string "title", limit: 510, null: false + t.integer "project_id", null: false t.text "description" t.date "due_date" - t.datetime "created_at" - t.datetime "updated_at" - t.string "state" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "state", limit: 510 t.integer "iid" end @@ -644,16 +648,16 @@ add_index "milestones", ["title"], name: "index_milestones_on_title", using: :btree create_table "namespaces", force: :cascade do |t| - t.string "name", null: false - t.string "path", null: false + t.string "name", limit: 510, null: false + t.string "path", limit: 510, null: false t.integer "owner_id" - t.datetime "created_at" - t.datetime "updated_at" - t.string "type" - t.string "description", default: "", null: false - t.string "avatar" - t.boolean "membership_lock", default: false - t.boolean "share_with_group_lock", default: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "type", limit: 510 + t.string "description", limit: 510, default: "", null: false + t.string "avatar", limit: 510 + t.boolean "membership_lock", default: false + t.boolean "share_with_group_lock", default: false end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree @@ -664,19 +668,19 @@ create_table "notes", force: :cascade do |t| t.text "note" - t.string "noteable_type" + t.string "noteable_type", limit: 510 t.integer "author_id" t.datetime "created_at" t.datetime "updated_at" t.integer "project_id" - t.string "attachment" - t.string "line_code" - t.string "commit_id" + t.string "attachment", limit: 510 + t.string "line_code", limit: 510 + t.string "commit_id", limit: 510 t.integer "noteable_id" - t.boolean "system", default: false, null: false t.text "st_diff" + t.boolean "system", null: false t.integer "updated_by_id" - t.boolean "is_award", default: false, null: false + t.boolean "is_award", default: false, null: false end add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree @@ -692,14 +696,14 @@ add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree create_table "oauth_access_grants", force: :cascade do |t| - t.integer "resource_owner_id", null: false - t.integer "application_id", null: false - t.string "token", null: false - t.integer "expires_in", null: false - t.text "redirect_uri", null: false - t.datetime "created_at", null: false + t.integer "resource_owner_id", null: false + t.integer "application_id", null: false + t.string "token", limit: 255, null: false + t.integer "expires_in", null: false + t.text "redirect_uri", null: false + t.datetime "created_at", null: false t.datetime "revoked_at" - t.string "scopes" + t.string "scopes", limit: 255 end add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree @@ -707,12 +711,12 @@ create_table "oauth_access_tokens", force: :cascade do |t| t.integer "resource_owner_id" t.integer "application_id" - t.string "token", null: false - t.string "refresh_token" + t.string "token", limit: 255, null: false + t.string "refresh_token", limit: 255 t.integer "expires_in" t.datetime "revoked_at" - t.datetime "created_at", null: false - t.string "scopes" + t.datetime "created_at", null: false + t.string "scopes", limit: 255 end add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree @@ -720,15 +724,15 @@ add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree create_table "oauth_applications", force: :cascade do |t| - t.string "name", null: false - t.string "uid", null: false - t.string "secret", null: false - t.text "redirect_uri", null: false - t.string "scopes", default: "", null: false + t.string "name", limit: 255, null: false + t.string "uid", limit: 255, null: false + t.string "secret", limit: 255, null: false + t.text "redirect_uri", null: false + t.string "scopes", limit: 255, default: "", null: false t.datetime "created_at" t.datetime "updated_at" t.integer "owner_id" - t.string "owner_type" + t.string "owner_type", limit: 255 end add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree @@ -759,52 +763,62 @@ end create_table "projects", force: :cascade do |t| - t.string "name" - t.string "path" + t.string "name", limit: 510 + t.string "path", limit: 510 t.text "description" t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" - t.boolean "issues_enabled", default: true, null: false - t.boolean "wall_enabled", default: true, null: false - t.boolean "merge_requests_enabled", default: true, null: false - t.boolean "wiki_enabled", default: true, null: false + t.boolean "issues_enabled", null: false + t.boolean "wall_enabled", null: false + t.boolean "merge_requests_enabled", null: false + t.boolean "wiki_enabled", null: false t.integer "namespace_id" - t.string "issues_tracker", default: "gitlab", null: false - t.string "issues_tracker_id" - t.boolean "snippets_enabled", default: true, null: false + t.string "issues_tracker", limit: 510, default: "gitlab", null: false + t.string "issues_tracker_id", limit: 510 + t.boolean "snippets_enabled", null: false t.datetime "last_activity_at" - t.string "import_url" - t.integer "visibility_level", default: 0, null: false - t.boolean "archived", default: false, null: false - t.string "avatar" - t.string "import_status" - t.float "repository_size", default: 0.0 + t.string "import_url", limit: 510 + t.integer "visibility_level", default: 0, null: false + t.boolean "archived", null: false + t.string "import_status", limit: 255 + t.float "repository_size", default: 0.0 + t.integer "star_count", default: 0, null: false + t.string "import_type", limit: 255 + t.string "import_source", limit: 255 + t.string "avatar", limit: 255 + t.integer "commit_count", default: 0 + t.text "import_error" + t.integer "ci_id" + t.boolean "builds_enabled", default: true, null: false + t.boolean "shared_runners_enabled", default: true, null: false + t.string "runners_token" + t.string "build_coverage_regex" + t.boolean "build_allow_git_fetch", default: true, null: false + t.integer "build_timeout", default: 3600, null: false t.text "merge_requests_template" - t.integer "star_count", default: 0, null: false - t.boolean "merge_requests_rebase_enabled", default: false - t.string "import_type" - t.string "import_source" - t.integer "approvals_before_merge", default: 0, null: false - t.boolean "reset_approvals_on_push", default: true - t.integer "commit_count", default: 0 - t.boolean "merge_requests_ff_only_enabled", default: false + t.boolean "merge_requests_rebase_enabled", default: false + t.integer "approvals_before_merge", default: 0, null: false + t.boolean "reset_approvals_on_push", default: true + t.boolean "merge_requests_ff_only_enabled", default: false t.text "issues_template" - t.boolean "mirror", default: false, null: false + t.boolean "mirror", default: false, null: false t.datetime "mirror_last_update_at" t.datetime "mirror_last_successful_update_at" t.integer "mirror_user_id" - t.text "import_error" - t.integer "ci_id" - t.boolean "builds_enabled", default: true, null: false - t.boolean "shared_runners_enabled", default: true, null: false - t.string "runners_token" - t.string "build_coverage_regex" - t.boolean "build_allow_git_fetch", default: true, null: false - t.integer "build_timeout", default: 3600, null: false - t.boolean "mirror_trigger_builds", default: false, null: false - t.boolean "pending_delete", default: false - t.boolean "public_builds", default: true, null: false + t.boolean "mirror_trigger_builds", default: false, null: false + t.boolean "pending_delete", default: false + t.boolean "allow_guest_to_access_builds", default: true, null: false + t.boolean "public_builds", default: true, null: false + t.text "pages_custom_certificate" + t.text "pages_custom_certificate_key" + t.string "pages_custom_certificate_key_iv" + t.string "pages_custom_certificate_key_salt" + t.string "pages_custom_domain" + t.boolean "pages_redirect_http", default: false, null: false + t.text "encrypted_pages_custom_certificate_key" + t.string "encrypted_pages_custom_certificate_key_iv" + t.string "encrypted_pages_custom_certificate_key_salt" end add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree @@ -820,17 +834,17 @@ add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree create_table "protected_branches", force: :cascade do |t| - t.integer "project_id", null: false - t.string "name", null: false - t.datetime "created_at" - t.datetime "updated_at" - t.boolean "developers_can_push", default: false, null: false + t.integer "project_id", null: false + t.string "name", limit: 510, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "developers_can_push", default: false, null: false end add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree create_table "releases", force: :cascade do |t| - t.string "tag" + t.string "tag", limit: 255 t.text "description" t.integer "project_id" t.datetime "created_at" @@ -843,32 +857,32 @@ create_table "sent_notifications", force: :cascade do |t| t.integer "project_id" t.integer "noteable_id" - t.string "noteable_type" + t.string "noteable_type", limit: 255 t.integer "recipient_id" - t.string "commit_id" - t.string "reply_key", null: false - t.string "line_code" + t.string "commit_id", limit: 255 + t.string "reply_key", limit: 255, null: false + t.string "line_code", limit: 255 end add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree create_table "services", force: :cascade do |t| - t.string "type" - t.string "title" + t.string "type", limit: 510 + t.string "title", limit: 510 t.integer "project_id" - t.datetime "created_at" - t.datetime "updated_at" - t.boolean "active", default: false, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "active", null: false t.text "properties" - t.boolean "template", default: false - t.boolean "push_events", default: true - t.boolean "issues_events", default: true - t.boolean "merge_requests_events", default: true - t.boolean "tag_push_events", default: true - t.boolean "note_events", default: true, null: false - t.boolean "build_events", default: false, null: false - t.string "category", default: "common", null: false - t.boolean "default", default: false + t.boolean "template", default: false + t.boolean "push_events", default: true + t.boolean "issues_events", default: true + t.boolean "merge_requests_events", default: true + t.boolean "tag_push_events", default: true + t.boolean "note_events", default: true, null: false + t.boolean "build_events", default: false, null: false + t.string "category", default: "common", null: false + t.boolean "default", default: false end add_index "services", ["category"], name: "index_services_on_category", using: :btree @@ -878,16 +892,16 @@ add_index "services", ["template"], name: "index_services_on_template", using: :btree create_table "snippets", force: :cascade do |t| - t.string "title" + t.string "title", limit: 510 t.text "content" - t.integer "author_id", null: false + t.integer "author_id", null: false t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.string "file_name" + t.string "file_name", limit: 510 t.datetime "expires_at" - t.string "type" - t.integer "visibility_level", default: 0, null: false + t.string "type", limit: 510 + t.integer "visibility_level", default: 0, null: false end add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree @@ -914,7 +928,7 @@ create_table "subscriptions", force: :cascade do |t| t.integer "user_id" t.integer "subscribable_id" - t.string "subscribable_type" + t.string "subscribable_type", limit: 255 t.boolean "subscribed" t.datetime "created_at" t.datetime "updated_at" @@ -925,10 +939,10 @@ create_table "taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" - t.string "taggable_type" + t.string "taggable_type", limit: 510 t.integer "tagger_id" - t.string "tagger_type" - t.string "context" + t.string "tagger_type", limit: 510 + t.string "context", limit: 510 t.datetime "created_at" end @@ -936,82 +950,82 @@ add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "tags", force: :cascade do |t| - t.string "name" - t.integer "taggings_count", default: 0 + t.string "name", limit: 510 + t.integer "taggings_count", default: 0 end add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" + t.string "email", limit: 510, default: "", null: false + t.string "encrypted_password", limit: 256, default: "", null: false + t.string "reset_password_token", limit: 510 t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0 + t.integer "sign_in_count", default: 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" - t.string "current_sign_in_ip" - t.string "last_sign_in_ip" + t.string "current_sign_in_ip", limit: 510 + t.string "last_sign_in_ip", limit: 510 t.datetime "created_at" t.datetime "updated_at" - t.string "name" - t.boolean "admin", default: false, null: false - t.integer "projects_limit", default: 10 - t.string "skype", default: "", null: false - t.string "linkedin", default: "", null: false - t.string "twitter", default: "", null: false - t.string "authentication_token" - t.integer "theme_id", default: 1, null: false - t.string "bio" - t.integer "failed_attempts", default: 0 + t.string "name", limit: 510 + t.boolean "admin", null: false + t.integer "projects_limit", default: 10 + t.string "skype", limit: 510, default: "", null: false + t.string "linkedin", limit: 510, default: "", null: false + t.string "twitter", limit: 510, default: "", null: false + t.string "authentication_token", limit: 510 + t.integer "theme_id", default: 1, null: false + t.string "bio", limit: 510 + t.integer "failed_attempts", default: 0 t.datetime "locked_at" - t.string "username" - t.boolean "can_create_group", default: true, null: false - t.boolean "can_create_team", default: true, null: false - t.string "state" - t.integer "color_scheme_id", default: 1, null: false - t.integer "notification_level", default: 1, null: false + t.string "username", limit: 510 + t.boolean "can_create_group", null: false + t.boolean "can_create_team", null: false + t.string "state", limit: 510 + t.integer "color_scheme_id", default: 1, null: false + t.integer "notification_level", default: 1, null: false t.datetime "password_expires_at" t.integer "created_by_id" - t.datetime "last_credential_check_at" - t.string "avatar" - t.string "confirmation_token" + t.string "avatar", limit: 510 + t.string "confirmation_token", limit: 510 t.datetime "confirmed_at" t.datetime "confirmation_sent_at" - t.string "unconfirmed_email" - t.boolean "hide_no_ssh_key", default: false - t.string "website_url", default: "", null: false - t.datetime "admin_email_unsubscribed_at" - t.string "notification_email" - t.boolean "hide_no_password", default: false - t.boolean "password_automatically_set", default: false - t.string "location" - t.string "encrypted_otp_secret" - t.string "encrypted_otp_secret_iv" - t.string "encrypted_otp_secret_salt" - t.boolean "otp_required_for_login", default: false, null: false + t.string "unconfirmed_email", limit: 510 + t.boolean "hide_no_ssh_key" + t.string "website_url", limit: 510, default: "", null: false + t.datetime "last_credential_check_at" + t.string "notification_email", limit: 255 + t.boolean "hide_no_password", default: false + t.boolean "password_automatically_set", default: false + t.string "location", limit: 255 + t.string "public_email", limit: 255, default: "", null: false + t.string "encrypted_otp_secret", limit: 255 + t.string "encrypted_otp_secret_iv", limit: 255 + t.string "encrypted_otp_secret_salt", limit: 255 + t.boolean "otp_required_for_login", default: false, null: false t.text "otp_backup_codes" - t.string "public_email", default: "", null: false - t.integer "dashboard", default: 0 - t.integer "project_view", default: 0 + t.integer "dashboard", default: 0 + t.integer "project_view", default: 0 t.integer "consumed_timestep" - t.integer "layout", default: 0 - t.boolean "hide_project_limit", default: false - t.text "note" + t.integer "layout", default: 0 + t.boolean "hide_project_limit", default: false t.string "unlock_token" + t.datetime "admin_email_unsubscribed_at" + t.text "note" t.datetime "otp_grace_period_started_at" - t.boolean "ldap_email", default: false, null: false + t.boolean "ldap_email", default: false, null: false end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree - add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree - add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree + add_index "users", ["authentication_token"], name: "users_authentication_token_key", unique: true, using: :btree + add_index "users", ["confirmation_token"], name: "users_confirmation_token_key", unique: true, using: :btree add_index "users", ["created_at", "id"], name: "index_users_on_created_at_and_id", using: :btree add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree - add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree + add_index "users", ["email"], name: "users_email_key", unique: true, using: :btree add_index "users", ["name"], name: "index_users_on_name", using: :btree - add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree + add_index "users", ["reset_password_token"], name: "users_reset_password_token_key", unique: true, using: :btree add_index "users", ["username"], name: "index_users_on_username", using: :btree create_table "users_star_projects", force: :cascade do |t| @@ -1030,16 +1044,16 @@ t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.string "type", default: "ProjectHook" + t.string "type", limit: 510, default: "ProjectHook" t.integer "service_id" - t.boolean "push_events", default: true, null: false - t.boolean "issues_events", default: false, null: false - t.boolean "merge_requests_events", default: false, null: false + t.boolean "push_events", null: false + t.boolean "issues_events", null: false + t.boolean "merge_requests_events", null: false t.boolean "tag_push_events", default: false - t.integer "group_id" t.boolean "note_events", default: false, null: false t.boolean "enable_ssl_verification", default: true t.boolean "build_events", default: false, null: false + t.integer "group_id" end add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree -- GitLab From 030551ecda02544a4abe32bdb3b0ad82b74784fc Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 15 Feb 2016 16:09:57 +0100 Subject: [PATCH 17/28] Change icon to cloud-upload --- app/views/layouts/nav/_project_settings.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 76b9d39b7bca..b3418beecf3f 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -51,7 +51,7 @@ Mirror Repository = nav_link(controller: :pages) do = link_to namespace_project_pages_path(@project.namespace, @project), title: 'Pages', data: {placement: 'right'} do - = icon('cloud fw') + = icon('cloud-upload fw') %span Pages = nav_link(controller: :audit_events) do -- GitLab From 44f5645f1927245610a1672028f94cc6798e3f93 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 16 Feb 2016 10:48:51 +0100 Subject: [PATCH 18/28] Final fixes --- config/gitlab.yml.example | 4 ++-- features/steps/project/pages.rb | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 152fe1c3fa16..7f1993e7c55a 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -160,8 +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 notifies the GitLab pages do support Custom Domains - # external_https: "1.1.1.1:443" # if defined notifies the GitLab pages do support Custom Domains with Certificates + # 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 diff --git a/features/steps/project/pages.rb b/features/steps/project/pages.rb index a5cb81b0ef37..ac44aac9e384 100644 --- a/features/steps/project/pages.rb +++ b/features/steps/project/pages.rb @@ -4,14 +4,14 @@ class Spinach::Features::ProjectPages < Spinach::FeatureSteps include SharedProject step 'pages are enabled' do - Gitlab.config.pages.stub(:enabled).and_return(true) - Gitlab.config.pages.stub(:host).and_return('example.com') - Gitlab.config.pages.stub(:port).and_return(80) - Gitlab.config.pages.stub(:https).and_return(false) + 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 - Gitlab.config.pages.stub(:enabled).and_return(false) + allow(Gitlab.config.pages).to receive(:enabled).and_return(false) end step 'I visit the Project Pages' do @@ -48,18 +48,18 @@ class Spinach::Features::ProjectPages < Spinach::FeatureSteps end step 'support for external domains is disabled' do - Gitlab.config.pages.stub(:external_http).and_return(nil) - Gitlab.config.pages.stub(:external_https).and_return(nil) + 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 - Gitlab.config.pages.stub(:external_http).and_return('1.1.1.1:80') - Gitlab.config.pages.stub(:external_https).and_return(nil) + 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 - Gitlab.config.pages.stub(:external_http).and_return('1.1.1.1:80') - Gitlab.config.pages.stub(:external_https).and_return('1.1.1.1:443') + 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 -- GitLab From b3e6081680d91f9763e818e7cc31c2eeb4a90d91 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 16 Feb 2016 11:33:15 +0100 Subject: [PATCH 19/28] Update init scripts and installation guide --- doc/install/installation.md | 8 +++ doc/pages/README.md | 35 +++++++++++-- doc/update/8.5-ce-to-ee.md | 24 ++++++++- lib/support/init.d/gitlab | 61 ++++++++++++++++++++--- lib/support/init.d/gitlab.default.example | 13 +++++ lib/support/nginx/gitlab-pages | 18 +++---- lib/support/nginx/gitlab-pages-ssl | 20 ++++---- 7 files changed, 146 insertions(+), 33 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index a6bcd2a5d620..49617d942612 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -363,6 +363,14 @@ GitLab Shell is an SSH access and repository management software developed speci cd gitlab-workhorse sudo -u git -H git checkout 0.6.4 sudo -u git -H make + +### Install gitlab-pages daemon + + cd /home/git + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git + cd gitlab-pages + sudo -u git -H git checkout 0.1.0 + sudo -u git -H make ### Initialize Database and Activate Advanced Features diff --git a/doc/pages/README.md b/doc/pages/README.md index 90c6fb8433d1..f83720f80a8e 100644 --- a/doc/pages/README.md +++ b/doc/pages/README.md @@ -47,13 +47,42 @@ URL it will be accessible. ## Enable the pages feature in your project -The GitLab Pages feature needs to be explicitly enabled for each project -under its **Settings**. +The GitLab Pages feature is enabled when the valid `.gitlab-ci.yml` is configured +in your project. + +## Use custom domain + +You can define multiple domains for your pages. +Go to **Settings > Pages** and click a **New Domain**. +You will be asked to fill simple form where you put the **Domain** name. +You can use the specified domain only once. + +After adding domain you need to configure your DNS to point to your Pages. +To do so add the **CNAME** record for your domain pointing to: `walter.example.com`. +Where `walter` is your group or username. + +If you are unable to add a **CNAME**, because your is top-level domain. +You can check the IP address of server serving GitLab Pages: + + $ dig walter.example.com + walter.example.com. 300 IN A 1.1.1.1 + +Add a **A** record pointing to **1.1.1.1**. + +## Use custom domain with custom certificates + +_**Note:** This feature was [introduced][ee-80] in GitLab EE 8.5_ + +When defining the domain you can also paste the custom certificates. +You need to paste the certificate with all intermediate certificates required to build a trust chain in PEM format. +The second you need to send us the private key for your certificate. + +_**Note:** This feature was [introduced][ee-80] in GitLab EE 8.5 and needs to be explicitly enabled by your Administrator_ ## Remove the contents of your pages Pages can be explicitly removed from a project by clicking **Remove Pages** -in a project's **Settings**. +in a project's **Settings > Pages**. ## Explore the contents of .gitlab-ci.yml diff --git a/doc/update/8.5-ce-to-ee.md b/doc/update/8.5-ce-to-ee.md index 0f2342c4dc4d..09140039ac37 100644 --- a/doc/update/8.5-ce-to-ee.md +++ b/doc/update/8.5-ce-to-ee.md @@ -47,12 +47,32 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production ``` -### 4. Start application +### 4. Install gitlab-pages daemon + + cd /home/git + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git + cd gitlab-pages + sudo -u git -H git checkout 0.1.0 + sudo -u git -H make + +### 5. Update the init scripts + +Download the init script (will be `/etc/init.d/gitlab`): + + sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab + +And if you are installing with a non-default folder or user copy and edit the defaults file: + + sudo cp lib/support/init.d/gitlab.default.example /etc/default/gitlab + +**Note:** This is required to have a support for GitLab Pages. + +### 6. Start application sudo service gitlab start sudo service nginx restart -### 5. Check application status +### 7. Check application status Check if GitLab and its environment are configured correctly: diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index d95e7023d2e2..0a4d3cf82310 100755 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/gitlab @@ -89,6 +89,13 @@ check_pids(){ mpid=0 fi fi + if [ "$gitlab_pages_enabled" = true ]; then + if [ -f "$gitlab_pages_pid_path" ]; then + gppid=$(cat "$gitlab_pages_pid_path") + else + gppid=0 + fi + fi } ## Called when we have started the two processes and are waiting for their pid files. @@ -144,7 +151,15 @@ check_status(){ mail_room_status="-1" fi fi - if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_workhorse_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; }; then + if [ "$gitlab_pages_enabled" = true ]; then + if [ $gppid -ne 0 ]; then + kill -0 "$gppid" 2>/dev/null + gitlab_pages_status="$?" + else + gitlab_pages_status="-1" + fi + fi + if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_workhorse_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; } && { [ "$gitlab_pages_enabled" != true ] || [ $gitlab_pages_status = 0 ]; }; then gitlab_status=0 else # http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html @@ -186,12 +201,19 @@ check_stale_pids(){ exit 1 fi fi + if [ "$gitlab_pages_enabled" = true ] && [ "$gppid" != "0" ] && [ "$gitlab_pages_status" != "0" ]; then + echo "Removing stale GitLab Pages job dispatcher pid. This is most likely caused by GitLab Pages crashing the last time it ran." + if ! rm "$gitlab_pages_pid_path"; then + echo "Unable to remove stale pid, exiting" + exit 1 + fi + fi } ## If no parts of the service is running, bail out. exit_if_not_running(){ check_stale_pids - if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then + if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then echo "GitLab is not running." exit fi @@ -213,6 +235,9 @@ start_gitlab() { if [ "$mail_room_enabled" = true ] && [ "$mail_room_status" != "0" ]; then echo "Starting GitLab MailRoom" fi + if [ "$mail_room_enabled" = true ] && [ "$gitlab_pages_status" != "0" ]; then + echo "Starting GitLab Pages" + fi # Then check if the service is running. If it is: don't start again. if [ "$web_status" = "0" ]; then @@ -252,6 +277,16 @@ start_gitlab() { fi fi + if [ "$gitlab_pages_enabled" = true ]; then + if [ "$gitlab_pages_status" = "0" ]; then + echo "The GitLab Pages is already running with pid $spid, not restarting" + else + $app_root/bin/daemon_with_pidfile $gitlab_pages_pid_path \ + $gitlab_pages_dir/gitlab-pages $gitlab_pages_options \ + >> $gitlab_pages_log 2>&1 & + fi + fi + # Wait for the pids to be planted wait_for_pids # Finally check the status to tell wether or not GitLab is running @@ -278,13 +313,17 @@ stop_gitlab() { echo "Shutting down GitLab MailRoom" RAILS_ENV=$RAILS_ENV bin/mail_room stop fi + if [ "$gitlab_pages_status" = "0" ]; then + echo "Shutting down gitlab-pages" + kill -- $(cat $gitlab_pages_pid_path) + fi # If something needs to be stopped, lets wait for it to stop. Never use SIGKILL in a script. - while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; do + while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; }; do sleep 1 check_status printf "." - if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then + if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then printf "\n" break fi @@ -298,6 +337,7 @@ stop_gitlab() { if [ "$mail_room_enabled" = true ]; then rm "$mail_room_pid_path" 2>/dev/null fi + rm -f "$gitlab_pages_pid_path" print_status } @@ -305,7 +345,7 @@ stop_gitlab() { ## Prints the status of GitLab and its components. print_status() { check_status - if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then + if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then echo "GitLab is not running." return fi @@ -331,7 +371,14 @@ print_status() { printf "The GitLab MailRoom email processor is \033[31mnot running\033[0m.\n" fi fi - if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; }; then + if [ "$gitlab_pages_enabled" = true ]; then + if [ "$gitlab_pages_status" = "0" ]; then + echo "The GitLab Pages with pid $mpid is running." + else + printf "The GitLab Pages is \033[31mnot running\033[0m.\n" + fi + fi + if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" = "0" ]; }; then printf "GitLab and all its components are \033[32mup and running\033[0m.\n" fi } @@ -362,7 +409,7 @@ reload_gitlab(){ ## Restarts Sidekiq and Unicorn. restart_gitlab(){ check_status - if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; then + if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; }; then stop_gitlab fi start_gitlab diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example index cc8617b72caf..2254c9376f79 100755 --- a/lib/support/init.d/gitlab.default.example +++ b/lib/support/init.d/gitlab.default.example @@ -47,6 +47,19 @@ gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid" gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $socket_path/gitlab.socket -documentRoot $app_root/public" gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log" +# The GitLab Pages Daemon needs to use separate IP address on which it will listen +# You can also use the different ports (80 or 443) that will be forwarded to GitLab Pages Daemon +# To enable HTTP support for custom domains: +# -listen-http 1.1.1.1:80 +# The value of -listen-http must be set to `gitlab.yml:pages:external_http` +# To enable HTTPS support for custom domains and certificates: +# -listen-https 1.1.1.1:443 -root-cert /path/to/example.com.crt -root-key /path/to/example.com.key +# The value of -listen-https must be set to `gitlab.yml:pages:external_http` +# The -pages-domain must be specified the same as in `gitlab.yml` +gitlab_pages_enabled=true +gitlab_pages_options="-pages-domain example.com -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8282" +gitlab_pages_log="$app_root/log/gitlab-pages.log" + # mail_room_enabled specifies whether mail_room, which is used to process incoming email, is enabled. # This is required for the Reply by email feature. # The default is "false" diff --git a/lib/support/nginx/gitlab-pages b/lib/support/nginx/gitlab-pages index ed4f7e4316a0..d56a91c65001 100644 --- a/lib/support/nginx/gitlab-pages +++ b/lib/support/nginx/gitlab-pages @@ -7,21 +7,19 @@ server { listen [::]:80 ipv6only=on; ## Replace this with something like pages.gitlab.com - server_name ~^(?.*)\.YOUR_GITLAB_PAGES\.DOMAIN$; - root /home/git/gitlab/shared/pages/${group}; + server_name *.YOUR_GITLAB_PAGES.DOMAIN; ## Individual nginx logs for GitLab pages access_log /var/log/nginx/gitlab_pages_access.log; error_log /var/log/nginx/gitlab_pages_error.log; - # 1. Try to get /path/ from shared/pages/${group}/${path}/public/ - # 2. Try to get / from shared/pages/${group}/${host}/public/ - location ~ ^/([^/]*)(/.*)?$ { - try_files "/$1/public$2" - "/$1/public$2/index.html" - "/${host}/public/${uri}" - "/${host}/public/${uri}/index.html" - =404; + location / { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + # The same address as passed to GitLab Pages: `-listen-proxy` + proxy_pass http://localhost:8282/; } # Define custom error pages diff --git a/lib/support/nginx/gitlab-pages-ssl b/lib/support/nginx/gitlab-pages-ssl index dcbbee4042ae..389292ba8822 100644 --- a/lib/support/nginx/gitlab-pages-ssl +++ b/lib/support/nginx/gitlab-pages-ssl @@ -11,7 +11,7 @@ server { listen [::]:80 ipv6only=on; ## Replace this with something like pages.gitlab.com - server_name ~^(?.*)\.YOUR_GITLAB_PAGES\.DOMAIN$; + server_name *.YOUR_GITLAB_PAGES.DOMAIN; server_tokens off; ## Don't show the nginx version number, a security best practice return 301 https://$http_host$request_uri; @@ -26,9 +26,8 @@ server { listen [::]:443 ipv6only=on ssl; ## Replace this with something like pages.gitlab.com - server_name ~^(?.*)\.YOUR_GITLAB_PAGES\.DOMAIN$; + server_name *.YOUR_GITLAB_PAGES.DOMAIN; server_tokens off; ## Don't show the nginx version number, a security best practice - root /home/git/gitlab/shared/pages/${group}; ## Strong SSL Security ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/ @@ -63,14 +62,13 @@ server { access_log /var/log/nginx/gitlab_pages_access.log; error_log /var/log/nginx/gitlab_pages_error.log; - # 1. Try to get /path/ from shared/pages/${group}/${path}/public/ - # 2. Try to get / from shared/pages/${group}/${host}/public/ - location ~ ^/([^/]*)(/.*)?$ { - try_files "/$1/public$2" - "/$1/public$2/index.html" - "/${host}/public/${uri}" - "/${host}/public/${uri}/index.html" - =404; + location / { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + # The same address as passed to GitLab Pages: `-listen-proxy` + proxy_pass http://localhost:8282/; } # Define custom error pages -- GitLab From 49e8269065372f7edce43c072f22b831845746e4 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 16 Feb 2016 11:59:43 +0100 Subject: [PATCH 20/28] Update pages administration guide --- doc/pages/administration.md | 112 ++++++++++++++++++++++++++--- lib/support/nginx/gitlab-pages | 2 +- lib/support/nginx/gitlab-pages-ssl | 4 +- 3 files changed, 107 insertions(+), 11 deletions(-) diff --git a/doc/pages/administration.md b/doc/pages/administration.md index dda59cb327d2..e2ee5e1b8998 100644 --- a/doc/pages/administration.md +++ b/doc/pages/administration.md @@ -12,19 +12,35 @@ GitLab EE instance. 1. You need to properly configure your DNS to point to the domain that pages will be served -1. Pages use a separate Nginx configuration file which needs to be explicitly - added in the server under which GitLab EE runs +1. Decide whether the Pages should be served on separate IP address + or with the shared IP address in single Nginx server 1. Optionally but recommended, you can add some [shared runners](../ci/runners/README.md) so that your users don't have to bring their own. Both of these settings are described in detail in the sections below. +## Serving Pages on shared IP address on single Nginx server + +This is the most basic method of operation. +It allows you to serve the GitLab Pages on some predefined domain. +This method also doesn't require the separate IP address for only the Pages feature. + +## Serving Pages on separate IP address + +If you have a spare IP address or you can setup the separate load balancer to serve the GitLab Pages. +With this method of operation you can use additional features like: custom domains and support for custom certificates. +When configured in this mode the Pages daemon listen exclusively on ports that you do specify. + +This is also a advised method of operation. + ### DNS configuration GitLab Pages expect to run on their own virtual host. In your DNS server/provider you need to add a [wildcard DNS A record][wiki-wildcard-dns] pointing to the -host that GitLab runs. For example, an entry would look like this: +the IP address that will be used for GitLab Pages. +It can be an address of GitLab server or a separate IP address. +For example, an entry would look like this: ``` *.example.com. 60 IN A 1.2.3.4 @@ -40,7 +56,7 @@ see the [security section](#security). See the relevant documentation at . -### Installations from source +### Installations from source for shared IP address using Nginx server 1. Go to the GitLab installation directory: @@ -65,6 +81,12 @@ See the relevant documentation at .*)\.YOUR_GITLAB_PAGES\.DOMAIN$; + server_name ~^.*\.YOUR_GITLAB_PAGES\.DOMAIN$; ``` with ``` - server_name ~^(?.*)\.example\.com$; + server_name ~^.*\.example\.com$; ``` You must be extra careful to not remove the backslashes. If you are using @@ -91,7 +113,7 @@ See the relevant documentation at .*)\.pages\.example\.com$; + server_name ~^.*\.pages\.example\.com$; ``` 1. Restart Nginx and GitLab: @@ -101,7 +123,7 @@ See the relevant documentation at Date: Tue, 16 Feb 2016 13:32:38 +0100 Subject: [PATCH 21/28] Added pages version [ci skip] --- GITLAB_PAGES_VERSION | 1 + 1 file changed, 1 insertion(+) create mode 100644 GITLAB_PAGES_VERSION diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION new file mode 100644 index 000000000000..6e8bf73aa550 --- /dev/null +++ b/GITLAB_PAGES_VERSION @@ -0,0 +1 @@ +0.1.0 -- GitLab From d8b6de3f5be801982b98f4b24f14a61f285f9932 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 16 Feb 2016 14:39:58 +0100 Subject: [PATCH 22/28] Verify trusted certificate chain --- spec/factories/pages_domains.rb | 78 +++++++++++++++++++++++++++++++- spec/models/pages_domain_spec.rb | 14 ++++-- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/spec/factories/pages_domains.rb b/spec/factories/pages_domains.rb index 4608867087cf..ff72df8dc023 100644 --- a/spec/factories/pages_domains.rb +++ b/spec/factories/pages_domains.rb @@ -38,8 +38,9 @@ -----END PRIVATE KEY-----' end - trait :with_certificate_chain do + 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 @@ -61,6 +62,81 @@ -----END CERTIFICATE-----' end + trait :with_trusted_chain do + # This is + # [Intermediate #2 (SHA-2)] 'Comodo RSA Domain Validation Secure Server CA' + # [Intermediate #1 (SHA-2)] COMODO RSA Certification Authority + # We only validate that we want to rebuild the trust chain, + # we don't need end-to-end certificate to do that + 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 diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb index 929b2a26549e..3e083ba90010 100644 --- a/spec/models/pages_domain_spec.rb +++ b/spec/models/pages_domain_spec.rb @@ -63,7 +63,7 @@ end context 'for not matching key' do - let(:domain) { build(:pages_domain, :with_certificate_chain, :with_key) } + let(:domain) { build(:pages_domain, :with_missing_chain, :with_key) } it { is_expected.to_not be_valid } end @@ -95,7 +95,7 @@ end context 'for invalid key' do - let(:domain) { build(:pages_domain, :with_certificate_chain, :with_key) } + let(:domain) { build(:pages_domain, :with_missing_chain, :with_key) } it { is_expected.to be_falsey } end @@ -110,11 +110,17 @@ it { is_expected.to be_truthy } end - context 'for certificate chain without the root' do - let(:domain) { build(:pages_domain, :with_certificate_chain) } + 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 + let(:domain) { build(:pages_domain, :with_trusted_chain) } + + it { is_expected.to be_truthy } + end end describe :expired? do -- GitLab From 4f15bdcde1aeabe290cb7e52fc7b3d64f787a7d5 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 16 Feb 2016 14:40:54 +0100 Subject: [PATCH 23/28] Fix certificate validators --- app/validators/certificate_key_validator.rb | 2 +- app/validators/certificate_validator.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/validators/certificate_key_validator.rb b/app/validators/certificate_key_validator.rb index 7039bd5a6213..098b16017d27 100644 --- a/app/validators/certificate_key_validator.rb +++ b/app/validators/certificate_key_validator.rb @@ -16,7 +16,7 @@ def validate_each(record, attribute, value) private def valid_private_key_pem?(value) - return unless value + return false unless value pkey = OpenSSL::PKey::RSA.new(value) pkey.private? rescue OpenSSL::PKey::PKeyError diff --git a/app/validators/certificate_validator.rb b/app/validators/certificate_validator.rb index 2a04c76d4b9a..e3d18097f71b 100644 --- a/app/validators/certificate_validator.rb +++ b/app/validators/certificate_validator.rb @@ -16,9 +16,9 @@ def validate_each(record, attribute, value) private def valid_certificate_pem?(value) - return unless value - OpenSSL::X509::Certificate.new(value) + return false unless value + OpenSSL::X509::Certificate.new(value).present? rescue OpenSSL::X509::CertificateError - nil + false end end -- GitLab From d4e2aa54d6f0424f0abf899a45e7d5806479755f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 16 Feb 2016 19:12:43 +0100 Subject: [PATCH 24/28] Revert "Update pages administration guide" This reverts commit 1e4f43e854b3dda901ee6d3469153ea72c75959f. --- doc/pages/administration.md | 112 +++-------------------------- lib/support/nginx/gitlab-pages | 2 +- lib/support/nginx/gitlab-pages-ssl | 4 +- 3 files changed, 11 insertions(+), 107 deletions(-) diff --git a/doc/pages/administration.md b/doc/pages/administration.md index e2ee5e1b8998..dda59cb327d2 100644 --- a/doc/pages/administration.md +++ b/doc/pages/administration.md @@ -12,35 +12,19 @@ GitLab EE instance. 1. You need to properly configure your DNS to point to the domain that pages will be served -1. Decide whether the Pages should be served on separate IP address - or with the shared IP address in single Nginx server +1. Pages use a separate Nginx configuration file which needs to be explicitly + added in the server under which GitLab EE runs 1. Optionally but recommended, you can add some [shared runners](../ci/runners/README.md) so that your users don't have to bring their own. Both of these settings are described in detail in the sections below. -## Serving Pages on shared IP address on single Nginx server - -This is the most basic method of operation. -It allows you to serve the GitLab Pages on some predefined domain. -This method also doesn't require the separate IP address for only the Pages feature. - -## Serving Pages on separate IP address - -If you have a spare IP address or you can setup the separate load balancer to serve the GitLab Pages. -With this method of operation you can use additional features like: custom domains and support for custom certificates. -When configured in this mode the Pages daemon listen exclusively on ports that you do specify. - -This is also a advised method of operation. - ### DNS configuration GitLab Pages expect to run on their own virtual host. In your DNS server/provider you need to add a [wildcard DNS A record][wiki-wildcard-dns] pointing to the -the IP address that will be used for GitLab Pages. -It can be an address of GitLab server or a separate IP address. -For example, an entry would look like this: +host that GitLab runs. For example, an entry would look like this: ``` *.example.com. 60 IN A 1.2.3.4 @@ -56,7 +40,7 @@ see the [security section](#security). See the relevant documentation at . -### Installations from source for shared IP address using Nginx server +### Installations from source 1. Go to the GitLab installation directory: @@ -81,12 +65,6 @@ See the relevant documentation at .*)\.YOUR_GITLAB_PAGES\.DOMAIN$; ``` with ``` - server_name ~^.*\.example\.com$; + server_name ~^(?.*)\.example\.com$; ``` You must be extra careful to not remove the backslashes. If you are using @@ -113,7 +91,7 @@ See the relevant documentation at .*)\.pages\.example\.com$; ``` 1. Restart Nginx and GitLab: @@ -123,7 +101,7 @@ See the relevant documentation at Date: Tue, 16 Feb 2016 19:12:54 +0100 Subject: [PATCH 25/28] Revert "Update init scripts and installation guide" This reverts commit 0bb6c63b19f3574735838190b415a8b37eca3900. --- doc/install/installation.md | 8 --- doc/pages/README.md | 35 ++----------- doc/update/8.5-ce-to-ee.md | 24 +-------- lib/support/init.d/gitlab | 61 +++-------------------- lib/support/init.d/gitlab.default.example | 13 ----- lib/support/nginx/gitlab-pages | 18 ++++--- lib/support/nginx/gitlab-pages-ssl | 20 ++++---- 7 files changed, 33 insertions(+), 146 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 49617d942612..a6bcd2a5d620 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -363,14 +363,6 @@ GitLab Shell is an SSH access and repository management software developed speci cd gitlab-workhorse sudo -u git -H git checkout 0.6.4 sudo -u git -H make - -### Install gitlab-pages daemon - - cd /home/git - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git - cd gitlab-pages - sudo -u git -H git checkout 0.1.0 - sudo -u git -H make ### Initialize Database and Activate Advanced Features diff --git a/doc/pages/README.md b/doc/pages/README.md index f83720f80a8e..90c6fb8433d1 100644 --- a/doc/pages/README.md +++ b/doc/pages/README.md @@ -47,42 +47,13 @@ URL it will be accessible. ## Enable the pages feature in your project -The GitLab Pages feature is enabled when the valid `.gitlab-ci.yml` is configured -in your project. - -## Use custom domain - -You can define multiple domains for your pages. -Go to **Settings > Pages** and click a **New Domain**. -You will be asked to fill simple form where you put the **Domain** name. -You can use the specified domain only once. - -After adding domain you need to configure your DNS to point to your Pages. -To do so add the **CNAME** record for your domain pointing to: `walter.example.com`. -Where `walter` is your group or username. - -If you are unable to add a **CNAME**, because your is top-level domain. -You can check the IP address of server serving GitLab Pages: - - $ dig walter.example.com - walter.example.com. 300 IN A 1.1.1.1 - -Add a **A** record pointing to **1.1.1.1**. - -## Use custom domain with custom certificates - -_**Note:** This feature was [introduced][ee-80] in GitLab EE 8.5_ - -When defining the domain you can also paste the custom certificates. -You need to paste the certificate with all intermediate certificates required to build a trust chain in PEM format. -The second you need to send us the private key for your certificate. - -_**Note:** This feature was [introduced][ee-80] in GitLab EE 8.5 and needs to be explicitly enabled by your Administrator_ +The GitLab Pages feature needs to be explicitly enabled for each project +under its **Settings**. ## Remove the contents of your pages Pages can be explicitly removed from a project by clicking **Remove Pages** -in a project's **Settings > Pages**. +in a project's **Settings**. ## Explore the contents of .gitlab-ci.yml diff --git a/doc/update/8.5-ce-to-ee.md b/doc/update/8.5-ce-to-ee.md index 09140039ac37..0f2342c4dc4d 100644 --- a/doc/update/8.5-ce-to-ee.md +++ b/doc/update/8.5-ce-to-ee.md @@ -47,32 +47,12 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production ``` -### 4. Install gitlab-pages daemon - - cd /home/git - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git - cd gitlab-pages - sudo -u git -H git checkout 0.1.0 - sudo -u git -H make - -### 5. Update the init scripts - -Download the init script (will be `/etc/init.d/gitlab`): - - sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab - -And if you are installing with a non-default folder or user copy and edit the defaults file: - - sudo cp lib/support/init.d/gitlab.default.example /etc/default/gitlab - -**Note:** This is required to have a support for GitLab Pages. - -### 6. Start application +### 4. Start application sudo service gitlab start sudo service nginx restart -### 7. Check application status +### 5. Check application status Check if GitLab and its environment are configured correctly: diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index 0a4d3cf82310..d95e7023d2e2 100755 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/gitlab @@ -89,13 +89,6 @@ check_pids(){ mpid=0 fi fi - if [ "$gitlab_pages_enabled" = true ]; then - if [ -f "$gitlab_pages_pid_path" ]; then - gppid=$(cat "$gitlab_pages_pid_path") - else - gppid=0 - fi - fi } ## Called when we have started the two processes and are waiting for their pid files. @@ -151,15 +144,7 @@ check_status(){ mail_room_status="-1" fi fi - if [ "$gitlab_pages_enabled" = true ]; then - if [ $gppid -ne 0 ]; then - kill -0 "$gppid" 2>/dev/null - gitlab_pages_status="$?" - else - gitlab_pages_status="-1" - fi - fi - if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_workhorse_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; } && { [ "$gitlab_pages_enabled" != true ] || [ $gitlab_pages_status = 0 ]; }; then + if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_workhorse_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; }; then gitlab_status=0 else # http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html @@ -201,19 +186,12 @@ check_stale_pids(){ exit 1 fi fi - if [ "$gitlab_pages_enabled" = true ] && [ "$gppid" != "0" ] && [ "$gitlab_pages_status" != "0" ]; then - echo "Removing stale GitLab Pages job dispatcher pid. This is most likely caused by GitLab Pages crashing the last time it ran." - if ! rm "$gitlab_pages_pid_path"; then - echo "Unable to remove stale pid, exiting" - exit 1 - fi - fi } ## If no parts of the service is running, bail out. exit_if_not_running(){ check_stale_pids - if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then + if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then echo "GitLab is not running." exit fi @@ -235,9 +213,6 @@ start_gitlab() { if [ "$mail_room_enabled" = true ] && [ "$mail_room_status" != "0" ]; then echo "Starting GitLab MailRoom" fi - if [ "$mail_room_enabled" = true ] && [ "$gitlab_pages_status" != "0" ]; then - echo "Starting GitLab Pages" - fi # Then check if the service is running. If it is: don't start again. if [ "$web_status" = "0" ]; then @@ -277,16 +252,6 @@ start_gitlab() { fi fi - if [ "$gitlab_pages_enabled" = true ]; then - if [ "$gitlab_pages_status" = "0" ]; then - echo "The GitLab Pages is already running with pid $spid, not restarting" - else - $app_root/bin/daemon_with_pidfile $gitlab_pages_pid_path \ - $gitlab_pages_dir/gitlab-pages $gitlab_pages_options \ - >> $gitlab_pages_log 2>&1 & - fi - fi - # Wait for the pids to be planted wait_for_pids # Finally check the status to tell wether or not GitLab is running @@ -313,17 +278,13 @@ stop_gitlab() { echo "Shutting down GitLab MailRoom" RAILS_ENV=$RAILS_ENV bin/mail_room stop fi - if [ "$gitlab_pages_status" = "0" ]; then - echo "Shutting down gitlab-pages" - kill -- $(cat $gitlab_pages_pid_path) - fi # If something needs to be stopped, lets wait for it to stop. Never use SIGKILL in a script. - while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; }; do + while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; do sleep 1 check_status printf "." - if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then + if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then printf "\n" break fi @@ -337,7 +298,6 @@ stop_gitlab() { if [ "$mail_room_enabled" = true ]; then rm "$mail_room_pid_path" 2>/dev/null fi - rm -f "$gitlab_pages_pid_path" print_status } @@ -345,7 +305,7 @@ stop_gitlab() { ## Prints the status of GitLab and its components. print_status() { check_status - if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then + if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then echo "GitLab is not running." return fi @@ -371,14 +331,7 @@ print_status() { printf "The GitLab MailRoom email processor is \033[31mnot running\033[0m.\n" fi fi - if [ "$gitlab_pages_enabled" = true ]; then - if [ "$gitlab_pages_status" = "0" ]; then - echo "The GitLab Pages with pid $mpid is running." - else - printf "The GitLab Pages is \033[31mnot running\033[0m.\n" - fi - fi - if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" = "0" ]; }; then + if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; }; then printf "GitLab and all its components are \033[32mup and running\033[0m.\n" fi } @@ -409,7 +362,7 @@ reload_gitlab(){ ## Restarts Sidekiq and Unicorn. restart_gitlab(){ check_status - if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; }; then + if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; then stop_gitlab fi start_gitlab diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example index 2254c9376f79..cc8617b72caf 100755 --- a/lib/support/init.d/gitlab.default.example +++ b/lib/support/init.d/gitlab.default.example @@ -47,19 +47,6 @@ gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid" gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $socket_path/gitlab.socket -documentRoot $app_root/public" gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log" -# The GitLab Pages Daemon needs to use separate IP address on which it will listen -# You can also use the different ports (80 or 443) that will be forwarded to GitLab Pages Daemon -# To enable HTTP support for custom domains: -# -listen-http 1.1.1.1:80 -# The value of -listen-http must be set to `gitlab.yml:pages:external_http` -# To enable HTTPS support for custom domains and certificates: -# -listen-https 1.1.1.1:443 -root-cert /path/to/example.com.crt -root-key /path/to/example.com.key -# The value of -listen-https must be set to `gitlab.yml:pages:external_http` -# The -pages-domain must be specified the same as in `gitlab.yml` -gitlab_pages_enabled=true -gitlab_pages_options="-pages-domain example.com -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8282" -gitlab_pages_log="$app_root/log/gitlab-pages.log" - # mail_room_enabled specifies whether mail_room, which is used to process incoming email, is enabled. # This is required for the Reply by email feature. # The default is "false" diff --git a/lib/support/nginx/gitlab-pages b/lib/support/nginx/gitlab-pages index d56a91c65001..ed4f7e4316a0 100644 --- a/lib/support/nginx/gitlab-pages +++ b/lib/support/nginx/gitlab-pages @@ -7,19 +7,21 @@ server { listen [::]:80 ipv6only=on; ## Replace this with something like pages.gitlab.com - server_name *.YOUR_GITLAB_PAGES.DOMAIN; + server_name ~^(?.*)\.YOUR_GITLAB_PAGES\.DOMAIN$; + root /home/git/gitlab/shared/pages/${group}; ## Individual nginx logs for GitLab pages access_log /var/log/nginx/gitlab_pages_access.log; error_log /var/log/nginx/gitlab_pages_error.log; - location / { - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - # The same address as passed to GitLab Pages: `-listen-proxy` - proxy_pass http://localhost:8282/; + # 1. Try to get /path/ from shared/pages/${group}/${path}/public/ + # 2. Try to get / from shared/pages/${group}/${host}/public/ + location ~ ^/([^/]*)(/.*)?$ { + try_files "/$1/public$2" + "/$1/public$2/index.html" + "/${host}/public/${uri}" + "/${host}/public/${uri}/index.html" + =404; } # Define custom error pages diff --git a/lib/support/nginx/gitlab-pages-ssl b/lib/support/nginx/gitlab-pages-ssl index 389292ba8822..dcbbee4042ae 100644 --- a/lib/support/nginx/gitlab-pages-ssl +++ b/lib/support/nginx/gitlab-pages-ssl @@ -11,7 +11,7 @@ server { listen [::]:80 ipv6only=on; ## Replace this with something like pages.gitlab.com - server_name *.YOUR_GITLAB_PAGES.DOMAIN; + server_name ~^(?.*)\.YOUR_GITLAB_PAGES\.DOMAIN$; server_tokens off; ## Don't show the nginx version number, a security best practice return 301 https://$http_host$request_uri; @@ -26,8 +26,9 @@ server { listen [::]:443 ipv6only=on ssl; ## Replace this with something like pages.gitlab.com - server_name *.YOUR_GITLAB_PAGES.DOMAIN; + server_name ~^(?.*)\.YOUR_GITLAB_PAGES\.DOMAIN$; server_tokens off; ## Don't show the nginx version number, a security best practice + root /home/git/gitlab/shared/pages/${group}; ## Strong SSL Security ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/ @@ -62,13 +63,14 @@ server { access_log /var/log/nginx/gitlab_pages_access.log; error_log /var/log/nginx/gitlab_pages_error.log; - location / { - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - # The same address as passed to GitLab Pages: `-listen-proxy` - proxy_pass http://localhost:8282/; + # 1. Try to get /path/ from shared/pages/${group}/${path}/public/ + # 2. Try to get / from shared/pages/${group}/${host}/public/ + location ~ ^/([^/]*)(/.*)?$ { + try_files "/$1/public$2" + "/$1/public$2/index.html" + "/${host}/public/${uri}" + "/${host}/public/${uri}/index.html" + =404; } # Define custom error pages -- GitLab From 2b998dd479395a9ea04c975d3de437aa161f8c1c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 16 Feb 2016 19:13:16 +0100 Subject: [PATCH 26/28] Revert db/schema.rb --- db/schema.rb | 618 +++++++++++++++++++++++++-------------------------- 1 file changed, 302 insertions(+), 316 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 37e67d972135..0887f801ba13 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -43,43 +43,43 @@ t.text "sign_in_text" t.datetime "created_at" t.datetime "updated_at" - t.string "home_page_url", limit: 255 - t.integer "default_branch_protection", default: 2 - t.boolean "twitter_sharing_enabled", default: true + t.string "home_page_url" + t.integer "default_branch_protection", default: 2 + t.boolean "twitter_sharing_enabled", default: true + t.text "help_text" t.text "restricted_visibility_levels" - t.integer "max_attachment_size", default: 10, null: false + t.boolean "version_check_enabled", default: true + t.integer "max_attachment_size", default: 10, null: false t.integer "default_project_visibility" t.integer "default_snippet_visibility" t.text "restricted_signup_domains" - t.boolean "version_check_enabled", default: true - t.boolean "user_oauth_applications", default: true - t.string "after_sign_out_path", limit: 255 - t.integer "session_expire_delay", default: 10080, null: false + t.boolean "user_oauth_applications", default: true + t.string "after_sign_out_path" + t.integer "session_expire_delay", default: 10080, null: false t.text "import_sources" t.text "help_page_text" - t.string "admin_notification_email", limit: 255 - t.boolean "shared_runners_enabled", default: true, null: false - t.integer "max_artifacts_size", default: 100, null: false + t.string "admin_notification_email" + t.boolean "shared_runners_enabled", default: true, null: false + t.integer "max_artifacts_size", default: 100, null: false t.string "runners_registration_token" - t.integer "max_pages_size", default: 100, null: false - t.text "help_text" - t.boolean "require_two_factor_authentication", default: false - t.integer "two_factor_grace_period", default: 48 - t.boolean "metrics_enabled", default: false - t.string "metrics_host", default: "localhost" - t.integer "metrics_pool_size", default: 16 - t.integer "metrics_timeout", default: 10 - t.integer "metrics_method_call_threshold", default: 10 - t.boolean "recaptcha_enabled", default: false + t.integer "max_pages_size", default: 100, null: false + t.boolean "require_two_factor_authentication", default: false + t.integer "two_factor_grace_period", default: 48 + t.boolean "metrics_enabled", default: false + t.string "metrics_host", default: "localhost" + t.integer "metrics_pool_size", default: 16 + t.integer "metrics_timeout", default: 10 + t.integer "metrics_method_call_threshold", default: 10 + t.boolean "recaptcha_enabled", default: false t.string "recaptcha_site_key" t.string "recaptcha_private_key" - t.integer "metrics_port", default: 8089 - t.integer "metrics_sample_interval", default: 15 - t.boolean "sentry_enabled", default: false + t.integer "metrics_port", default: 8089 + t.integer "metrics_sample_interval", default: 15 + t.boolean "sentry_enabled", default: false t.string "sentry_dsn" - t.boolean "akismet_enabled", default: false + t.boolean "akismet_enabled", default: false t.string "akismet_api_key" - t.boolean "email_author_in_body", default: false + t.boolean "email_author_in_body", default: false end create_table "approvals", force: :cascade do |t| @@ -101,10 +101,10 @@ add_index "approvers", ["user_id"], name: "index_approvers_on_user_id", using: :btree create_table "audit_events", force: :cascade do |t| - t.integer "author_id", null: false - t.string "type", limit: 255, null: false - t.integer "entity_id", null: false - t.string "entity_type", limit: 255, null: false + t.integer "author_id", null: false + t.string "type", null: false + t.integer "entity_id", null: false + t.string "entity_type", null: false t.text "details" t.datetime "created_at" t.datetime "updated_at" @@ -115,13 +115,13 @@ add_index "audit_events", ["type"], name: "index_audit_events_on_type", using: :btree create_table "broadcast_messages", force: :cascade do |t| - t.text "message", null: false + t.text "message", null: false t.datetime "starts_at" t.datetime "ends_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "color", limit: 510 - t.string "font", limit: 510 + t.datetime "created_at" + t.datetime "updated_at" + t.string "color" + t.string "font" end create_table "ci_application_settings", force: :cascade do |t| @@ -133,7 +133,7 @@ create_table "ci_builds", force: :cascade do |t| t.integer "project_id" - t.string "status", limit: 255 + t.string "status" t.datetime "finished_at" t.text "trace" t.datetime "created_at" @@ -144,19 +144,19 @@ t.integer "commit_id" t.text "commands" t.integer "job_id" - t.string "name", limit: 255 - t.boolean "deploy", default: false + t.string "name" + t.boolean "deploy", default: false t.text "options" - t.boolean "allow_failure", default: false, null: false - t.string "stage", limit: 255 + t.boolean "allow_failure", default: false, null: false + t.string "stage" t.integer "trigger_request_id" t.integer "stage_idx" t.boolean "tag" - t.string "ref", limit: 255 + t.string "ref" t.integer "user_id" - t.string "type", limit: 255 - t.string "target_url", limit: 255 - t.string "description", limit: 255 + t.string "type" + t.string "target_url" + t.string "description" t.text "artifacts_file" t.integer "gl_project_id" t.text "artifacts_metadata" @@ -167,26 +167,22 @@ add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree add_index "ci_builds", ["commit_id"], name: "index_ci_builds_on_commit_id", using: :btree - add_index "ci_builds", ["gl_project_id", "status", "type"], name: "index_ci_builds_on_gl_project_id_and_status_and_type", using: :btree - add_index "ci_builds", ["gl_project_id", "status"], name: "index_ci_builds_on_gl_project_id_and_status", using: :btree - add_index "ci_builds", ["gl_project_id", "type"], name: "index_ci_builds_on_gl_project_id_and_type", using: :btree add_index "ci_builds", ["gl_project_id"], name: "index_ci_builds_on_gl_project_id", using: :btree add_index "ci_builds", ["project_id", "commit_id"], name: "index_ci_builds_on_project_id_and_commit_id", using: :btree add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree - add_index "ci_builds", ["type", "status", "runner_id"], name: "index_ci_builds_on_test2", using: :btree add_index "ci_builds", ["type"], name: "index_ci_builds_on_type", using: :btree create_table "ci_commits", force: :cascade do |t| t.integer "project_id" - t.string "ref", limit: 255 - t.string "sha", limit: 255 - t.string "before_sha", limit: 255 + t.string "ref" + t.string "sha" + t.string "before_sha" t.text "push_data" t.datetime "created_at" t.datetime "updated_at" - t.boolean "tag", default: false + t.boolean "tag", default: false t.text "yaml_errors" t.datetime "committed_at" t.integer "gl_project_id" @@ -213,16 +209,16 @@ add_index "ci_events", ["project_id"], name: "index_ci_events_on_project_id", using: :btree create_table "ci_jobs", force: :cascade do |t| - t.integer "project_id", null: false + t.integer "project_id", null: false t.text "commands" - t.boolean "active", default: true, null: false + t.boolean "active", default: true, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "name", limit: 255 - t.boolean "build_branches", default: true, null: false - t.boolean "build_tags", default: false, null: false - t.string "job_type", limit: 255, default: "parallel" - t.string "refs", limit: 255 + t.string "name" + t.boolean "build_branches", default: true, null: false + t.boolean "build_tags", default: false, null: false + t.string "job_type", default: "parallel" + t.string "refs" t.datetime "deleted_at" end @@ -230,25 +226,25 @@ add_index "ci_jobs", ["project_id"], name: "index_ci_jobs_on_project_id", using: :btree create_table "ci_projects", force: :cascade do |t| - t.string "name", limit: 255 - t.integer "timeout", default: 3600, null: false + t.string "name" + t.integer "timeout", default: 3600, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "token", limit: 255 - t.string "default_ref", limit: 255 - t.string "path", limit: 255 - t.boolean "always_build", default: false, null: false + t.string "token" + t.string "default_ref" + t.string "path" + t.boolean "always_build", default: false, null: false t.integer "polling_interval" - t.boolean "public", default: false, null: false - t.string "ssh_url_to_repo", limit: 255 + t.boolean "public", default: false, null: false + t.string "ssh_url_to_repo" t.integer "gitlab_id" - t.boolean "allow_git_fetch", default: true, null: false - t.string "email_recipients", limit: 255, default: "", null: false - t.boolean "email_add_pusher", default: true, null: false - t.boolean "email_only_broken_builds", default: true, null: false - t.string "skip_refs", limit: 255 - t.string "coverage_regex", limit: 255 - t.boolean "shared_runners_enabled", default: false + t.boolean "allow_git_fetch", default: true, null: false + t.string "email_recipients", default: "", null: false + t.boolean "email_add_pusher", default: true, null: false + t.boolean "email_only_broken_builds", default: true, null: false + t.string "skip_refs" + t.string "coverage_regex" + t.boolean "shared_runners_enabled", default: false t.text "generated_yaml_config" end @@ -267,34 +263,34 @@ add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree create_table "ci_runners", force: :cascade do |t| - t.string "token", limit: 255 + t.string "token" t.datetime "created_at" t.datetime "updated_at" - t.string "description", limit: 255 + t.string "description" t.datetime "contacted_at" - t.boolean "active", default: true, null: false - t.boolean "is_shared", default: false - t.string "name", limit: 255 - t.string "version", limit: 255 - t.string "revision", limit: 255 - t.string "platform", limit: 255 - t.string "architecture", limit: 255 + t.boolean "active", default: true, null: false + t.boolean "is_shared", default: false + t.string "name" + t.string "version" + t.string "revision" + t.string "platform" + t.string "architecture" end create_table "ci_services", force: :cascade do |t| - t.string "type", limit: 255 - t.string "title", limit: 255 - t.integer "project_id", null: false + t.string "type" + t.string "title" + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.boolean "active", default: false, null: false + t.boolean "active", default: false, null: false t.text "properties" end add_index "ci_services", ["project_id"], name: "index_ci_services_on_project_id", using: :btree create_table "ci_sessions", force: :cascade do |t| - t.string "session_id", limit: 255, null: false + t.string "session_id", null: false t.text "data" t.datetime "created_at" t.datetime "updated_at" @@ -306,9 +302,9 @@ create_table "ci_taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" - t.string "taggable_type", limit: 255 + t.string "taggable_type" t.integer "tagger_id" - t.string "tagger_type", limit: 255 + t.string "tagger_type" t.string "context", limit: 128 t.datetime "created_at" end @@ -317,8 +313,8 @@ add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "ci_tags", force: :cascade do |t| - t.string "name", limit: 255 - t.integer "taggings_count", default: 0 + t.string "name" + t.integer "taggings_count", default: 0 end add_index "ci_tags", ["name"], name: "index_ci_tags_on_name", unique: true, using: :btree @@ -332,7 +328,7 @@ end create_table "ci_triggers", force: :cascade do |t| - t.string "token", limit: 255 + t.string "token" t.integer "project_id" t.datetime "deleted_at" t.datetime "created_at" @@ -345,19 +341,19 @@ create_table "ci_variables", force: :cascade do |t| t.integer "project_id" - t.string "key", limit: 255 + t.string "key" t.text "value" t.text "encrypted_value" - t.string "encrypted_value_salt", limit: 255 - t.string "encrypted_value_iv", limit: 255 + t.string "encrypted_value_salt" + t.string "encrypted_value_iv" t.integer "gl_project_id" end add_index "ci_variables", ["gl_project_id"], name: "index_ci_variables_on_gl_project_id", using: :btree create_table "ci_web_hooks", force: :cascade do |t| - t.string "url", limit: 255, null: false - t.integer "project_id", null: false + t.string "url", null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" end @@ -365,30 +361,30 @@ create_table "deploy_keys_projects", force: :cascade do |t| t.integer "deploy_key_id", null: false t.integer "project_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" end add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree create_table "emails", force: :cascade do |t| - t.integer "user_id", null: false - t.string "email", limit: 510, null: false + t.integer "user_id", null: false + t.string "email", null: false t.datetime "created_at" t.datetime "updated_at" end - add_index "emails", ["email"], name: "emails_email_key", unique: true, using: :btree + add_index "emails", ["email"], name: "index_emails_on_email", unique: true, using: :btree add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree create_table "events", force: :cascade do |t| - t.string "target_type", limit: 510 + t.string "target_type" t.integer "target_id" - t.string "title", limit: 510 + t.string "title" t.text "data" t.integer "project_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.integer "action" t.integer "author_id" end @@ -403,11 +399,11 @@ create_table "forked_project_links", force: :cascade do |t| t.integer "forked_to_project_id", null: false t.integer "forked_from_project_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" end - add_index "forked_project_links", ["forked_to_project_id"], name: "forked_project_links_forked_to_project_id_key", unique: true, using: :btree + add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree create_table "geo_nodes", force: :cascade do |t| t.string "schema" @@ -443,8 +439,8 @@ end create_table "identities", force: :cascade do |t| - t.string "extern_uid", limit: 255 - t.string "provider", limit: 255 + t.string "extern_uid" + t.string "provider" t.integer "user_id" t.datetime "created_at" t.datetime "updated_at" @@ -465,17 +461,17 @@ add_index "index_statuses", ["project_id"], name: "index_index_statuses_on_project_id", unique: true, using: :btree create_table "issues", force: :cascade do |t| - t.string "title", limit: 510 + t.string "title" t.integer "assignee_id" t.integer "author_id" t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.integer "position", default: 0 - t.string "branch_name", limit: 510 + t.integer "position", default: 0 + t.string "branch_name" t.text "description" t.integer "milestone_id" - t.string "state", limit: 510 + t.string "state" t.integer "iid" t.integer "updated_by_id" t.integer "weight" @@ -496,10 +492,10 @@ t.datetime "created_at" t.datetime "updated_at" t.text "key" - t.string "title", limit: 510 - t.string "type", limit: 510 - t.string "fingerprint", limit: 510 - t.boolean "public", default: false, null: false + t.string "title" + t.string "type" + t.string "fingerprint" + t.boolean "public", default: false, null: false end add_index "keys", ["created_at", "id"], name: "index_keys_on_created_at_and_id", using: :btree @@ -508,7 +504,7 @@ create_table "label_links", force: :cascade do |t| t.integer "label_id" t.integer "target_id" - t.string "target_type", limit: 255 + t.string "target_type" t.datetime "created_at" t.datetime "updated_at" end @@ -517,12 +513,12 @@ add_index "label_links", ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree create_table "labels", force: :cascade do |t| - t.string "title", limit: 255 - t.string "color", limit: 255 + t.string "title" + t.string "color" t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.boolean "template", default: false + t.boolean "template", default: false end add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree @@ -537,11 +533,11 @@ end create_table "lfs_objects", force: :cascade do |t| - t.string "oid", limit: 255, null: false - t.integer "size", limit: 8, null: false + t.string "oid", null: false + t.integer "size", limit: 8, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "file", limit: 255 + t.string "file" end add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree @@ -562,17 +558,17 @@ end create_table "members", force: :cascade do |t| - t.integer "access_level", null: false - t.integer "source_id", null: false - t.string "source_type", limit: 255, null: false + t.integer "access_level", null: false + t.integer "source_id", null: false + t.string "source_type", null: false t.integer "user_id" - t.integer "notification_level", null: false - t.string "type", limit: 255 + t.integer "notification_level", null: false + t.string "type" t.datetime "created_at" t.datetime "updated_at" t.integer "created_by_id" - t.string "invite_email", limit: 255 - t.string "invite_token", limit: 255 + t.string "invite_email" + t.string "invite_token" t.datetime "invite_accepted_at" end @@ -584,10 +580,10 @@ add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree create_table "merge_request_diffs", force: :cascade do |t| - t.string "state", limit: 255 + t.string "state" t.text "st_commits" t.text "st_diffs" - t.integer "merge_request_id", null: false + t.integer "merge_request_id", null: false t.datetime "created_at" t.datetime "updated_at" t.string "base_commit_sha" @@ -596,26 +592,26 @@ add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree create_table "merge_requests", force: :cascade do |t| - t.string "target_branch", limit: 510, null: false - t.string "source_branch", limit: 510, null: false - t.integer "source_project_id", null: false + t.string "target_branch", null: false + t.string "source_branch", null: false + t.integer "source_project_id", null: false t.integer "author_id" t.integer "assignee_id" - t.string "title", limit: 510 + t.string "title" t.datetime "created_at" t.datetime "updated_at" t.integer "milestone_id" - t.string "state", limit: 510 - t.string "merge_status", limit: 510 - t.integer "target_project_id", null: false + t.string "state" + t.string "merge_status" + t.integer "target_project_id", null: false t.integer "iid" t.text "description" - t.integer "position", default: 0 + t.integer "position", default: 0 t.datetime "locked_at" t.integer "updated_by_id" - t.string "merge_error", limit: 255 + t.string "merge_error" t.text "merge_params" - t.boolean "merge_when_build_succeeds", default: false, null: false + t.boolean "merge_when_build_succeeds", default: false, null: false t.integer "merge_user_id" end @@ -631,13 +627,13 @@ add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree create_table "milestones", force: :cascade do |t| - t.string "title", limit: 510, null: false - t.integer "project_id", null: false + t.string "title", null: false + t.integer "project_id", null: false t.text "description" t.date "due_date" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "state", limit: 510 + t.datetime "created_at" + t.datetime "updated_at" + t.string "state" t.integer "iid" end @@ -648,16 +644,16 @@ add_index "milestones", ["title"], name: "index_milestones_on_title", using: :btree create_table "namespaces", force: :cascade do |t| - t.string "name", limit: 510, null: false - t.string "path", limit: 510, null: false + t.string "name", null: false + t.string "path", null: false t.integer "owner_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "type", limit: 510 - t.string "description", limit: 510, default: "", null: false - t.string "avatar", limit: 510 - t.boolean "membership_lock", default: false - t.boolean "share_with_group_lock", default: false + t.datetime "created_at" + t.datetime "updated_at" + t.string "type" + t.string "description", default: "", null: false + t.string "avatar" + t.boolean "membership_lock", default: false + t.boolean "share_with_group_lock", default: false end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree @@ -668,19 +664,19 @@ create_table "notes", force: :cascade do |t| t.text "note" - t.string "noteable_type", limit: 510 + t.string "noteable_type" t.integer "author_id" t.datetime "created_at" t.datetime "updated_at" t.integer "project_id" - t.string "attachment", limit: 510 - t.string "line_code", limit: 510 - t.string "commit_id", limit: 510 + t.string "attachment" + t.string "line_code" + t.string "commit_id" t.integer "noteable_id" + t.boolean "system", default: false, null: false t.text "st_diff" - t.boolean "system", null: false t.integer "updated_by_id" - t.boolean "is_award", default: false, null: false + t.boolean "is_award", default: false, null: false end add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree @@ -696,14 +692,14 @@ add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree create_table "oauth_access_grants", force: :cascade do |t| - t.integer "resource_owner_id", null: false - t.integer "application_id", null: false - t.string "token", limit: 255, null: false - t.integer "expires_in", null: false - t.text "redirect_uri", null: false - t.datetime "created_at", null: false + t.integer "resource_owner_id", null: false + t.integer "application_id", null: false + t.string "token", null: false + t.integer "expires_in", null: false + t.text "redirect_uri", null: false + t.datetime "created_at", null: false t.datetime "revoked_at" - t.string "scopes", limit: 255 + t.string "scopes" end add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree @@ -711,12 +707,12 @@ create_table "oauth_access_tokens", force: :cascade do |t| t.integer "resource_owner_id" t.integer "application_id" - t.string "token", limit: 255, null: false - t.string "refresh_token", limit: 255 + t.string "token", null: false + t.string "refresh_token" t.integer "expires_in" t.datetime "revoked_at" - t.datetime "created_at", null: false - t.string "scopes", limit: 255 + t.datetime "created_at", null: false + t.string "scopes" end add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree @@ -724,15 +720,15 @@ add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree create_table "oauth_applications", force: :cascade do |t| - t.string "name", limit: 255, null: false - t.string "uid", limit: 255, null: false - t.string "secret", limit: 255, null: false - t.text "redirect_uri", null: false - t.string "scopes", limit: 255, default: "", null: false + t.string "name", null: false + t.string "uid", null: false + t.string "secret", null: false + t.text "redirect_uri", null: false + t.string "scopes", default: "", null: false t.datetime "created_at" t.datetime "updated_at" t.integer "owner_id" - t.string "owner_type", limit: 255 + t.string "owner_type" end add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree @@ -763,62 +759,52 @@ end create_table "projects", force: :cascade do |t| - t.string "name", limit: 510 - t.string "path", limit: 510 + t.string "name" + t.string "path" t.text "description" t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" - t.boolean "issues_enabled", null: false - t.boolean "wall_enabled", null: false - t.boolean "merge_requests_enabled", null: false - t.boolean "wiki_enabled", null: false + t.boolean "issues_enabled", default: true, null: false + t.boolean "wall_enabled", default: true, null: false + t.boolean "merge_requests_enabled", default: true, null: false + t.boolean "wiki_enabled", default: true, null: false t.integer "namespace_id" - t.string "issues_tracker", limit: 510, default: "gitlab", null: false - t.string "issues_tracker_id", limit: 510 - t.boolean "snippets_enabled", null: false + t.string "issues_tracker", default: "gitlab", null: false + t.string "issues_tracker_id" + t.boolean "snippets_enabled", default: true, null: false t.datetime "last_activity_at" - t.string "import_url", limit: 510 - t.integer "visibility_level", default: 0, null: false - t.boolean "archived", null: false - t.string "import_status", limit: 255 - t.float "repository_size", default: 0.0 - t.integer "star_count", default: 0, null: false - t.string "import_type", limit: 255 - t.string "import_source", limit: 255 - t.string "avatar", limit: 255 - t.integer "commit_count", default: 0 - t.text "import_error" - t.integer "ci_id" - t.boolean "builds_enabled", default: true, null: false - t.boolean "shared_runners_enabled", default: true, null: false - t.string "runners_token" - t.string "build_coverage_regex" - t.boolean "build_allow_git_fetch", default: true, null: false - t.integer "build_timeout", default: 3600, null: false + t.string "import_url" + t.integer "visibility_level", default: 0, null: false + t.boolean "archived", default: false, null: false + t.string "avatar" + t.string "import_status" + t.float "repository_size", default: 0.0 t.text "merge_requests_template" - t.boolean "merge_requests_rebase_enabled", default: false - t.integer "approvals_before_merge", default: 0, null: false - t.boolean "reset_approvals_on_push", default: true - t.boolean "merge_requests_ff_only_enabled", default: false + t.integer "star_count", default: 0, null: false + t.boolean "merge_requests_rebase_enabled", default: false + t.string "import_type" + t.string "import_source" + t.integer "approvals_before_merge", default: 0, null: false + t.boolean "reset_approvals_on_push", default: true + t.integer "commit_count", default: 0 + t.boolean "merge_requests_ff_only_enabled", default: false t.text "issues_template" - t.boolean "mirror", default: false, null: false + t.boolean "mirror", default: false, null: false t.datetime "mirror_last_update_at" t.datetime "mirror_last_successful_update_at" t.integer "mirror_user_id" - t.boolean "mirror_trigger_builds", default: false, null: false - t.boolean "pending_delete", default: false - t.boolean "allow_guest_to_access_builds", default: true, null: false - t.boolean "public_builds", default: true, null: false - t.text "pages_custom_certificate" - t.text "pages_custom_certificate_key" - t.string "pages_custom_certificate_key_iv" - t.string "pages_custom_certificate_key_salt" - t.string "pages_custom_domain" - t.boolean "pages_redirect_http", default: false, null: false - t.text "encrypted_pages_custom_certificate_key" - t.string "encrypted_pages_custom_certificate_key_iv" - t.string "encrypted_pages_custom_certificate_key_salt" + t.text "import_error" + t.integer "ci_id" + t.boolean "builds_enabled", default: true, null: false + t.boolean "shared_runners_enabled", default: true, null: false + t.string "runners_token" + t.string "build_coverage_regex" + t.boolean "build_allow_git_fetch", default: true, null: false + t.integer "build_timeout", default: 3600, null: false + t.boolean "mirror_trigger_builds", default: false, null: false + t.boolean "pending_delete", default: false + t.boolean "public_builds", default: true, null: false end add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree @@ -834,17 +820,17 @@ add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree create_table "protected_branches", force: :cascade do |t| - t.integer "project_id", null: false - t.string "name", limit: 510, null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.boolean "developers_can_push", default: false, null: false + t.integer "project_id", null: false + t.string "name", null: false + t.datetime "created_at" + t.datetime "updated_at" + t.boolean "developers_can_push", default: false, null: false end add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree create_table "releases", force: :cascade do |t| - t.string "tag", limit: 255 + t.string "tag" t.text "description" t.integer "project_id" t.datetime "created_at" @@ -857,32 +843,32 @@ create_table "sent_notifications", force: :cascade do |t| t.integer "project_id" t.integer "noteable_id" - t.string "noteable_type", limit: 255 + t.string "noteable_type" t.integer "recipient_id" - t.string "commit_id", limit: 255 - t.string "reply_key", limit: 255, null: false - t.string "line_code", limit: 255 + t.string "commit_id" + t.string "reply_key", null: false + t.string "line_code" end add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree create_table "services", force: :cascade do |t| - t.string "type", limit: 510 - t.string "title", limit: 510 + t.string "type" + t.string "title" t.integer "project_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.boolean "active", null: false + t.datetime "created_at" + t.datetime "updated_at" + t.boolean "active", default: false, null: false t.text "properties" - t.boolean "template", default: false - t.boolean "push_events", default: true - t.boolean "issues_events", default: true - t.boolean "merge_requests_events", default: true - t.boolean "tag_push_events", default: true - t.boolean "note_events", default: true, null: false - t.boolean "build_events", default: false, null: false - t.string "category", default: "common", null: false - t.boolean "default", default: false + t.boolean "template", default: false + t.boolean "push_events", default: true + t.boolean "issues_events", default: true + t.boolean "merge_requests_events", default: true + t.boolean "tag_push_events", default: true + t.boolean "note_events", default: true, null: false + t.boolean "build_events", default: false, null: false + t.string "category", default: "common", null: false + t.boolean "default", default: false end add_index "services", ["category"], name: "index_services_on_category", using: :btree @@ -892,16 +878,16 @@ add_index "services", ["template"], name: "index_services_on_template", using: :btree create_table "snippets", force: :cascade do |t| - t.string "title", limit: 510 + t.string "title" t.text "content" - t.integer "author_id", null: false + t.integer "author_id", null: false t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.string "file_name", limit: 510 + t.string "file_name" t.datetime "expires_at" - t.string "type", limit: 510 - t.integer "visibility_level", default: 0, null: false + t.string "type" + t.integer "visibility_level", default: 0, null: false end add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree @@ -928,7 +914,7 @@ create_table "subscriptions", force: :cascade do |t| t.integer "user_id" t.integer "subscribable_id" - t.string "subscribable_type", limit: 255 + t.string "subscribable_type" t.boolean "subscribed" t.datetime "created_at" t.datetime "updated_at" @@ -939,10 +925,10 @@ create_table "taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" - t.string "taggable_type", limit: 510 + t.string "taggable_type" t.integer "tagger_id" - t.string "tagger_type", limit: 510 - t.string "context", limit: 510 + t.string "tagger_type" + t.string "context" t.datetime "created_at" end @@ -950,82 +936,82 @@ add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "tags", force: :cascade do |t| - t.string "name", limit: 510 - t.integer "taggings_count", default: 0 + t.string "name" + t.integer "taggings_count", default: 0 end add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree create_table "users", force: :cascade do |t| - t.string "email", limit: 510, default: "", null: false - t.string "encrypted_password", limit: 256, default: "", null: false - t.string "reset_password_token", limit: 510 + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0 + t.integer "sign_in_count", default: 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" - t.string "current_sign_in_ip", limit: 510 - t.string "last_sign_in_ip", limit: 510 + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" t.datetime "created_at" t.datetime "updated_at" - t.string "name", limit: 510 - t.boolean "admin", null: false - t.integer "projects_limit", default: 10 - t.string "skype", limit: 510, default: "", null: false - t.string "linkedin", limit: 510, default: "", null: false - t.string "twitter", limit: 510, default: "", null: false - t.string "authentication_token", limit: 510 - t.integer "theme_id", default: 1, null: false - t.string "bio", limit: 510 - t.integer "failed_attempts", default: 0 + t.string "name" + t.boolean "admin", default: false, null: false + t.integer "projects_limit", default: 10 + t.string "skype", default: "", null: false + t.string "linkedin", default: "", null: false + t.string "twitter", default: "", null: false + t.string "authentication_token" + t.integer "theme_id", default: 1, null: false + t.string "bio" + t.integer "failed_attempts", default: 0 t.datetime "locked_at" - t.string "username", limit: 510 - t.boolean "can_create_group", null: false - t.boolean "can_create_team", null: false - t.string "state", limit: 510 - t.integer "color_scheme_id", default: 1, null: false - t.integer "notification_level", default: 1, null: false + t.string "username" + t.boolean "can_create_group", default: true, null: false + t.boolean "can_create_team", default: true, null: false + t.string "state" + t.integer "color_scheme_id", default: 1, null: false + t.integer "notification_level", default: 1, null: false t.datetime "password_expires_at" t.integer "created_by_id" - t.string "avatar", limit: 510 - t.string "confirmation_token", limit: 510 + t.datetime "last_credential_check_at" + t.string "avatar" + t.string "confirmation_token" t.datetime "confirmed_at" t.datetime "confirmation_sent_at" - t.string "unconfirmed_email", limit: 510 - t.boolean "hide_no_ssh_key" - t.string "website_url", limit: 510, default: "", null: false - t.datetime "last_credential_check_at" - t.string "notification_email", limit: 255 - t.boolean "hide_no_password", default: false - t.boolean "password_automatically_set", default: false - t.string "location", limit: 255 - t.string "public_email", limit: 255, default: "", null: false - t.string "encrypted_otp_secret", limit: 255 - t.string "encrypted_otp_secret_iv", limit: 255 - t.string "encrypted_otp_secret_salt", limit: 255 - t.boolean "otp_required_for_login", default: false, null: false + t.string "unconfirmed_email" + t.boolean "hide_no_ssh_key", default: false + t.string "website_url", default: "", null: false + t.datetime "admin_email_unsubscribed_at" + t.string "notification_email" + t.boolean "hide_no_password", default: false + t.boolean "password_automatically_set", default: false + t.string "location" + t.string "encrypted_otp_secret" + t.string "encrypted_otp_secret_iv" + t.string "encrypted_otp_secret_salt" + t.boolean "otp_required_for_login", default: false, null: false t.text "otp_backup_codes" - t.integer "dashboard", default: 0 - t.integer "project_view", default: 0 + t.string "public_email", default: "", null: false + t.integer "dashboard", default: 0 + t.integer "project_view", default: 0 t.integer "consumed_timestep" - t.integer "layout", default: 0 - t.boolean "hide_project_limit", default: false - t.string "unlock_token" - t.datetime "admin_email_unsubscribed_at" + t.integer "layout", default: 0 + t.boolean "hide_project_limit", default: false t.text "note" + t.string "unlock_token" t.datetime "otp_grace_period_started_at" - t.boolean "ldap_email", default: false, null: false + t.boolean "ldap_email", default: false, null: false end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree - add_index "users", ["authentication_token"], name: "users_authentication_token_key", unique: true, using: :btree - add_index "users", ["confirmation_token"], name: "users_confirmation_token_key", unique: true, using: :btree + add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree + add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree add_index "users", ["created_at", "id"], name: "index_users_on_created_at_and_id", using: :btree add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree - add_index "users", ["email"], name: "users_email_key", unique: true, using: :btree + add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree add_index "users", ["name"], name: "index_users_on_name", using: :btree - add_index "users", ["reset_password_token"], name: "users_reset_password_token_key", unique: true, using: :btree + add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree add_index "users", ["username"], name: "index_users_on_username", using: :btree create_table "users_star_projects", force: :cascade do |t| @@ -1044,16 +1030,16 @@ t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.string "type", limit: 510, default: "ProjectHook" + t.string "type", default: "ProjectHook" t.integer "service_id" - t.boolean "push_events", null: false - t.boolean "issues_events", null: false - t.boolean "merge_requests_events", null: false + t.boolean "push_events", default: true, null: false + t.boolean "issues_events", default: false, null: false + t.boolean "merge_requests_events", default: false, null: false t.boolean "tag_push_events", default: false + t.integer "group_id" t.boolean "note_events", default: false, null: false t.boolean "enable_ssl_verification", default: true t.boolean "build_events", default: false, null: false - t.integer "group_id" end add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree -- GitLab From 054c71a9336d80f0417042242667c80a3bbb29c7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 16 Feb 2016 19:13:28 +0100 Subject: [PATCH 27/28] Use GitLab Pages 0.2.0 --- GITLAB_PAGES_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 6e8bf73aa550..0ea3a944b399 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -0.1.0 +0.2.0 -- GitLab From 787d4f9137ea963cc2587f0100dbfc9f1216fab1 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 17 Feb 2016 10:05:26 +0100 Subject: [PATCH 28/28] Update comments --- features/steps/project/pages.rb | 2 +- spec/factories/pages_domains.rb | 6 ++---- spec/models/pages_domain_spec.rb | 4 ++++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/features/steps/project/pages.rb b/features/steps/project/pages.rb index ac44aac9e384..b3a6b93c5d01 100644 --- a/features/steps/project/pages.rb +++ b/features/steps/project/pages.rb @@ -34,7 +34,7 @@ class Spinach::Features::ProjectPages < Spinach::FeatureSteps 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 diff --git a/spec/factories/pages_domains.rb b/spec/factories/pages_domains.rb index ff72df8dc023..6d2e45f41ba2 100644 --- a/spec/factories/pages_domains.rb +++ b/spec/factories/pages_domains.rb @@ -63,11 +63,9 @@ end trait :with_trusted_chain do - # This is + # This contains # [Intermediate #2 (SHA-2)] 'Comodo RSA Domain Validation Secure Server CA' - # [Intermediate #1 (SHA-2)] COMODO RSA Certification Authority - # We only validate that we want to rebuild the trust chain, - # we don't need end-to-end certificate to do that + # [Intermediate #1 (SHA-2)] 'COMODO RSA Certification Authority' certificate '-----BEGIN CERTIFICATE----- MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb index 3e083ba90010..0b95bf594c52 100644 --- a/spec/models/pages_domain_spec.rb +++ b/spec/models/pages_domain_spec.rb @@ -117,6 +117,10 @@ 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 } -- GitLab