diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/ci/build.rb | 3 | ||||
-rw-r--r-- | app/models/namespace.rb | 1 | ||||
-rw-r--r-- | app/models/pages_domain.rb | 119 | ||||
-rw-r--r-- | app/models/project.rb | 43 |
4 files changed, 165 insertions, 1 deletions
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index b1f77bf242c..44d4fb9d8d8 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -256,7 +256,7 @@ module Ci end def project_id - pipeline.project_id + gl_project_id end def project_name @@ -451,6 +451,7 @@ module Ci build_data = Gitlab::DataBuilder::Build.build(self) project.execute_hooks(build_data.dup, :build_hooks) project.execute_services(build_data.dup, :build_hooks) + PagesService.new(build_data).execute project.running_or_pending_build_count(force: true) end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 67d8c1c2e4c..2fb2eb44aaa 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -130,6 +130,7 @@ class Namespace < ActiveRecord::Base end Gitlab::UploadsTransfer.new.rename_namespace(path_was, path) + Gitlab::PagesTransfer.new.rename_namespace(path_was, path) remove_exports! diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb new file mode 100644 index 00000000000..0b9ebf1ffe2 --- /dev/null +++ b/app/models/pages_domain.rb @@ -0,0 +1,119 @@ +class PagesDomain < ActiveRecord::Base + belongs_to :project + + validates :domain, hostname: true + validates_uniqueness_of :domain, case_sensitive: false + validates :certificate, certificate: true, allow_nil: true, allow_blank: true + validates :key, certificate_key: true, allow_nil: true, allow_blank: true + + validate :validate_pages_domain + validate :validate_matching_key, if: ->(domain) { domain.certificate.present? || domain.key.present? } + validate :validate_intermediates, if: ->(domain) { domain.certificate.present? } + + attr_encrypted :key, + mode: :per_attribute_iv_and_salt, + insecure_mode: true, + key: Gitlab::Application.secrets.db_key_base, + algorithm: 'aes-256-cbc' + + after_create :update + after_save :update + after_destroy :update + + def to_param + domain + end + + def url + return unless domain + + if certificate + "https://#{domain}" + else + "http://#{domain}" + end + end + + def has_matching_key? + return false unless x509 + return false unless pkey + + # We compare the public key stored in certificate with public key from certificate key + x509.check_private_key(pkey) + end + + def has_intermediates? + return false unless x509 + + # self-signed certificates doesn't have the certificate chain + return true if x509.verify(x509.public_key) + + store = OpenSSL::X509::Store.new + store.set_default_paths + + # This forces to load all intermediate certificates stored in `certificate` + Tempfile.open('certificate_chain') do |f| + f.write(certificate) + f.flush + store.add_file(f.path) + end + + store.verify(x509) + rescue OpenSSL::X509::StoreError + false + end + + def expired? + return false unless x509 + current = Time.new + current < x509.not_before || x509.not_after < current + end + + def subject + return unless x509 + x509.subject.to_s + end + + def certificate_text + @certificate_text ||= x509.try(:to_text) + end + + private + + def update + ::Projects::UpdatePagesConfigurationService.new(project).execute + end + + def validate_matching_key + unless has_matching_key? + self.errors.add(:key, "doesn't match the certificate") + end + end + + def validate_intermediates + unless has_intermediates? + self.errors.add(:certificate, 'misses intermediates') + end + end + + def validate_pages_domain + return unless domain + if domain.downcase.ends_with?(".#{Settings.pages.host}".downcase) + self.errors.add(:domain, "*.#{Settings.pages.host} is restricted") + end + end + + def x509 + return unless certificate + @x509 ||= OpenSSL::X509::Certificate.new(certificate) + rescue OpenSSL::X509::CertificateError + nil + end + + def pkey + return unless key + @pkey ||= OpenSSL::PKey::RSA.new(key) + rescue OpenSSL::PKey::PKeyError, OpenSSL::Cipher::CipherError + nil + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 0d286bfbaa8..7c5fdad5122 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -53,6 +53,8 @@ class Project < ActiveRecord::Base update_column(:last_activity_at, self.created_at) end + after_destroy :remove_pages + # update visibility_level of forks after_update :update_forks_visibility_level def update_forks_visibility_level @@ -148,6 +150,7 @@ class Project < ActiveRecord::Base has_many :lfs_objects, through: :lfs_objects_projects has_many :project_group_links, dependent: :destroy has_many :invited_groups, through: :project_group_links, source: :group + has_many :pages_domains, dependent: :destroy has_many :todos, dependent: :destroy has_many :notification_settings, dependent: :destroy, as: :source @@ -955,6 +958,7 @@ class Project < ActiveRecord::Base Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path) + Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.path) end # Expires various caches before a project is renamed. @@ -1156,6 +1160,45 @@ class Project < ActiveRecord::Base ensure_runners_token! end + def pages_deployed? + Dir.exist?(public_pages_path) + end + + def pages_url + # The hostname always needs to be in downcased + # All web servers convert hostname to lowercase + host = "#{namespace.path}.#{Settings.pages.host}".downcase + + # The host in URL always needs to be downcased + url = Gitlab.config.pages.url.sub(/^https?:\/\//) do |prefix| + "#{prefix}#{namespace.path}." + end.downcase + + # If the project path is the same as host, we serve it as group page + return url if host == path + + "#{url}/#{path}" + end + + def pages_path + File.join(Settings.pages.path, path_with_namespace) + end + + def public_pages_path + File.join(pages_path, 'public') + 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}.deleted" + + if Gitlab::PagesTransfer.new.rename_project(path, temp_path, namespace.path) + PagesWorker.perform_in(5.minutes, :remove, namespace.path, temp_path) + end + end + def wiki @wiki ||= ProjectWiki.new(self, self.owner) end |