summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/ci/build.rb3
-rw-r--r--app/models/namespace.rb1
-rw-r--r--app/models/pages_domain.rb119
-rw-r--r--app/models/project.rb43
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