Class: PagesDomain

Inherits:
ApplicationRecord show all
Includes:
AfterCommitQueue, FromUnion, Gitlab::EncryptedAttribute, Presentable
Defined in:
app/models/pages_domain.rb

Constant Summary collapse

VERIFICATION_KEY =
'gitlab-pages-verification-code'
VERIFICATION_THRESHOLD =
3.days.freeze
SSL_RENEWAL_THRESHOLD =
30.days.freeze
MAX_CERTIFICATE_KEY_LENGTH =
8192
X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN =
19

Constants inherited from ApplicationRecord

ApplicationRecord::MAX_PLUCK

Constants included from HasCheckConstraints

HasCheckConstraints::NOT_NULL_CHECK_PATTERN

Constants included from ResetOnColumnErrors

ResetOnColumnErrors::MAX_RESET_PERIOD

Class Method Summary collapse

Instance Method Summary collapse

Methods included from AfterCommitQueue

#run_after_commit, #run_after_commit_or_now

Methods included from Presentable

#present

Methods inherited from ApplicationRecord

===, cached_column_list, #create_or_load_association, current_transaction, declarative_enum, default_select_columns, delete_all_returning, #deleted_from_database?, id_in, id_not_in, iid_in, nullable_column?, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order

Methods included from Organizations::Sharding

#sharding_organization

Methods included from ResetOnColumnErrors

#reset_on_union_error, #reset_on_unknown_attribute_error

Methods included from Gitlab::SensitiveSerializableHash

#serializable_hash

Class Method Details

.find_by_domain_case_insensitive(domain) ⇒ Object



84
85
86
# File 'app/models/pages_domain.rb', line 84

def self.find_by_domain_case_insensitive(domain)
  find_by("LOWER(domain) = LOWER(?)", domain)
end

.ids_for_project(project_id) ⇒ Object



88
89
90
# File 'app/models/pages_domain.rb', line 88

def self.ids_for_project(project_id)
  where(project_id: project_id).ids
end

Instance Method Details

#certificate=(certificate) ⇒ Object



195
196
197
198
199
200
201
# File 'app/models/pages_domain.rb', line 195

def certificate=(certificate)
  super(certificate)

  # set nil, if certificate is nil
  self.certificate_valid_not_before = x509&.not_before
  self.certificate_valid_not_after = x509&.not_after
end

#certificate_textObject



173
174
175
# File 'app/models/pages_domain.rb', line 173

def certificate_text
  @certificate_text ||= x509.try(:to_text)
end

#clear_auto_ssl_failureObject



231
232
233
# File 'app/models/pages_domain.rb', line 231

def clear_auto_ssl_failure
  self.auto_ssl_failed = false
end

#enabled?Boolean

Returns:

  • (Boolean)


100
101
102
# File 'app/models/pages_domain.rb', line 100

def enabled?
  !Gitlab::CurrentSettings.pages_domain_verification_enabled? || enabled_until.present?
end

#expirationObject



163
164
165
# File 'app/models/pages_domain.rb', line 163

def expiration
  x509&.not_after
end

#expired?Boolean

Returns:

  • (Boolean)


156
157
158
159
160
161
# File 'app/models/pages_domain.rb', line 156

def expired?
  return false unless x509

  current = Time.current
  current < x509.not_before || x509.not_after < current
end

#gitlab_provided_certificate=(certificate) ⇒ Object



221
222
223
224
# File 'app/models/pages_domain.rb', line 221

def gitlab_provided_certificate=(certificate)
  self.certificate = certificate
  self.certificate_source = 'gitlab_provided' if certificate_changed?
end

#gitlab_provided_key=(key) ⇒ Object



226
227
228
229
# File 'app/models/pages_domain.rb', line 226

def gitlab_provided_key=(key)
  self.key = key
  self.certificate_source = 'gitlab_provided' if attribute_changed?(:key)
end

#has_matching_key?Boolean

Returns:

  • (Boolean)


122
123
124
125
126
127
128
# File 'app/models/pages_domain.rb', line 122

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

#has_valid_intermediates?Boolean

Returns:

  • (Boolean)


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'app/models/pages_domain.rb', line 130

def has_valid_intermediates?
  return false unless x509

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

  store = OpenSSL::X509::Store.new
  store.set_default_paths

  store.verify_callback = ->(is_valid, store_ctx) {
    # allow self signed certs, see https://gitlab.com/gitlab-org/gitlab/-/issues/356447
    return true if store_ctx.error == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN

    self.errors.add(:certificate, store_ctx.error_string) unless is_valid
    is_valid
  }

  store.verify(x509, untrusted_ca_certs_bundle)
rescue OpenSSL::X509::StoreError
  false
end

#https?Boolean

Returns:

  • (Boolean)


104
105
106
# File 'app/models/pages_domain.rb', line 104

def https?
  certificate.present?
end

#keyed_verification_codeObject



185
186
187
188
189
# File 'app/models/pages_domain.rb', line 185

def keyed_verification_code
  return unless verification_code.present?

  "#{VERIFICATION_KEY}=#{verification_code}"
end

#pages_deployed?Boolean

Returns:

  • (Boolean)


245
246
247
# File 'app/models/pages_domain.rb', line 245

def pages_deployed?
  project&.pages_deployed?
end

#subjectObject



167
168
169
170
171
# File 'app/models/pages_domain.rb', line 167

def subject
  return unless x509

  x509.subject.to_s
end

#to_paramObject



108
109
110
# File 'app/models/pages_domain.rb', line 108

def to_param
  domain
end

#untrusted_ca_certs_bundleObject



152
153
154
# File 'app/models/pages_domain.rb', line 152

def untrusted_ca_certs_bundle
  ::Gitlab::X509::Certificate.load_ca_certs_bundle(certificate)
end

#unverified?Boolean

Returns:

  • (Boolean)


96
97
98
# File 'app/models/pages_domain.rb', line 96

def unverified?
  !verified?
end

#urlObject



112
113
114
115
116
117
118
119
120
# File 'app/models/pages_domain.rb', line 112

def url
  return unless domain

  if certificate.present?
    "https://#{domain}"
  else
    "http://#{domain}"
  end
end

#user_provided_certificateObject



212
213
214
# File 'app/models/pages_domain.rb', line 212

def user_provided_certificate
  certificate if certificate_user_provided?
end

#user_provided_certificate=(certificate) ⇒ Object



216
217
218
219
# File 'app/models/pages_domain.rb', line 216

def user_provided_certificate=(certificate)
  self.certificate = certificate
  self.certificate_source = 'user_provided' if certificate_changed?
end

#user_provided_keyObject



203
204
205
# File 'app/models/pages_domain.rb', line 203

def user_provided_key
  key if certificate_user_provided?
end

#user_provided_key=(key) ⇒ Object



207
208
209
210
# File 'app/models/pages_domain.rb', line 207

def user_provided_key=(key)
  self.key = key
  self.certificate_source = 'user_provided' if attribute_changed?(:key)
end

#validate_custom_domain_count_per_projectObject



235
236
237
238
239
240
241
242
243
# File 'app/models/pages_domain.rb', line 235

def validate_custom_domain_count_per_project
  return unless project

  unless project.can_create_custom_domains?
    self.errors.add(
      :base,
      _("This project reached the limit of custom domains. (Max %d)") % Gitlab::CurrentSettings.max_pages_custom_domains_per_project)
  end
end

#verification_domainObject

Verification codes may be TXT records for domain or verification_domain, to support the use of CNAME records on domain.



179
180
181
182
183
# File 'app/models/pages_domain.rb', line 179

def verification_domain
  return unless domain.present?

  "_#{VERIFICATION_KEY}.#{domain}"
end

#verification_recordObject



191
192
193
# File 'app/models/pages_domain.rb', line 191

def verification_record
  "#{verification_domain} TXT #{keyed_verification_code}"
end

#verified?Boolean

Returns:

  • (Boolean)


92
93
94
# File 'app/models/pages_domain.rb', line 92

def verified?
  !!verified_at
end