BigW Consortium Gitlab

pages_domain.rb 2.76 KB
Newer Older
Kamil Trzcinski committed
1 2 3 4
class PagesDomain < ActiveRecord::Base
  belongs_to :project

  validates :domain, hostname: true
Douwe Maan committed
5
  validates :domain, uniqueness: { case_sensitive: false }
Kamil Trzcinski committed
6 7 8
  validates :certificate, certificate: true, allow_nil: true, allow_blank: true
  validates :key, certificate_key: true, allow_nil: true, allow_blank: true

9 10
  validate :validate_pages_domain
  validate :validate_matching_key, if: ->(domain) { domain.certificate.present? || domain.key.present? }
11 12
  validate :validate_intermediates, if: ->(domain) { domain.certificate.present? }

13 14
  attr_encrypted :key,
    mode: :per_attribute_iv_and_salt,
James Lopez committed
15
    insecure_mode: true,
16 17
    key: Gitlab::Application.secrets.db_key_base,
    algorithm: 'aes-256-cbc'
Kamil Trzcinski committed
18 19 20 21 22

  after_create :update
  after_save :update
  after_destroy :update

23 24 25 26
  def to_param
    domain
  end

Kamil Trzcinski committed
27 28 29 30
  def url
    return unless domain

    if certificate
31
      "https://#{domain}"
Kamil Trzcinski committed
32
    else
33
      "http://#{domain}"
Kamil Trzcinski committed
34 35 36
    end
  end

37
  def has_matching_key?
38 39
    return false unless x509
    return false unless pkey
40 41 42 43 44 45 46 47

    # 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

48 49 50
    # self-signed certificates doesn't have the certificate chain
    return true if x509.verify(x509.public_key)

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
    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
69
    current < x509.not_before || x509.not_after < current
70 71 72 73
  end

  def subject
    return unless x509
74
    x509.subject.to_s
75 76
  end

77 78
  def certificate_text
    @certificate_text ||= x509.try(:to_text)
79 80
  end

Kamil Trzcinski committed
81 82
  private

Kamil Trzcinski committed
83
  def update
84 85 86 87 88 89 90 91 92 93 94 95 96
    ::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
Kamil Trzcinski committed
97
  end
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118

  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
Kamil Trzcinski committed
119
end