Read about our upcoming Code of Conduct on this issue

application_setting.rb 12.9 KB
Newer Older
1
2
# frozen_string_literal: true

3
class ApplicationSetting < ApplicationRecord
4
  include CacheableAttributes
5
  include CacheMarkdownField
6
  include TokenAuthenticatable
7
  include ChronicDurationAttribute
8

9
10
11
12
13
14
  # Only remove this >= %12.6 and >= 2019-12-01
  self.ignored_columns += %i[
      pendo_enabled
      pendo_url
    ]

15
  add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
16
  add_authentication_token_field :health_check_access_token
17
  add_authentication_token_field :static_objects_external_storage_auth_token
18

19
20
  belongs_to :instance_administration_project, class_name: "Project"

21
22
23
24
25
26
  # Include here so it can override methods from
  # `add_authentication_token_field`
  # We don't prepend for now because otherwise we'll need to
  # fix a lot of tests using allow_any_instance_of
  include ApplicationSettingImplementation

27
28
29
30
31
32
  serialize :restricted_visibility_levels # rubocop:disable Cop/ActiveRecordSerialize
  serialize :import_sources # rubocop:disable Cop/ActiveRecordSerialize
  serialize :disabled_oauth_sign_in_sources, Array # rubocop:disable Cop/ActiveRecordSerialize
  serialize :domain_whitelist, Array # rubocop:disable Cop/ActiveRecordSerialize
  serialize :domain_blacklist, Array # rubocop:disable Cop/ActiveRecordSerialize
  serialize :repository_storages # rubocop:disable Cop/ActiveRecordSerialize
33
  serialize :asset_proxy_whitelist, Array # rubocop:disable Cop/ActiveRecordSerialize
34

35
36
37
38
39
  cache_markdown_field :sign_in_text
  cache_markdown_field :help_page_text
  cache_markdown_field :shared_runners_text, pipeline: :plain_markdown
  cache_markdown_field :after_sign_up_text

40
41
  default_value_for :id, 1

42
43
  chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds

44
45
  validates :uuid, presence: true

46
  validates :outbound_local_requests_whitelist,
47
48
49
            length: { maximum: 1_000, message: N_('is too long (maximum is 1000 entries)') },
            allow_nil: false,
            qualified_domain_array: true
50

51
  validates :session_expire_delay,
52
53
            presence: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 0 }
54

55
  validates :home_page_url,
56
            allow_blank: true,
57
            addressable_url: true,
58
59
60
61
            if: :home_page_url_column_exists?

  validates :help_page_support_url,
            allow_blank: true,
62
            addressable_url: true,
63
            if: :help_page_support_url_column_exists?
64

65
  validates :after_sign_out_path,
66
            allow_blank: true,
67
            addressable_url: true
68

69
  validates :admin_notification_email,
70
            devise_email: true,
71
            allow_blank: true
72

73
  validates :two_factor_grace_period,
74
75
76
77
            numericality: { greater_than_or_equal_to: 0 }

  validates :recaptcha_site_key,
            presence: true,
78
            if: :recaptcha_or_login_protection_enabled
79
80
81

  validates :recaptcha_private_key,
            presence: true,
82
            if: :recaptcha_or_login_protection_enabled
83

84
85
86
87
  validates :akismet_api_key,
            presence: true,
            if: :akismet_enabled

Pawel Chojnacki's avatar
Pawel Chojnacki committed
88
89
90
91
92
93
94
95
96
97
  validates :unique_ips_limit_per_user,
            numericality: { greater_than_or_equal_to: 1 },
            presence: true,
            if: :unique_ips_limit_enabled

  validates :unique_ips_limit_time_window,
            numericality: { greater_than_or_equal_to: 0 },
            presence: true,
            if: :unique_ips_limit_enabled

98
99
100
101
  validates :plantuml_url,
            presence: true,
            if: :plantuml_enabled

102
103
104
105
106
  validates :snowplow_collector_hostname,
            presence: true,
            hostname: true,
            if: :snowplow_enabled

107
108
109
110
111
  validates :snowplow_iglu_registry_url,
            addressable_url: true,
            allow_blank: true,
            if: :snowplow_enabled

112
113
114
115
  validates :max_attachment_size,
            presence: true,
            numericality: { only_integer: true, greater_than: 0 }

116
117
118
119
  validates :max_artifacts_size,
            presence: true,
            numericality: { only_integer: true, greater_than: 0 }

120
  validates :default_artifacts_expire_in, presence: true, duration: true
121

122
123
124
125
  validates :container_registry_token_expire_delay,
            presence: true,
            numericality: { only_integer: true, greater_than: 0 }

126
127
  validates :repository_storages, presence: true
  validate :check_repository_storages
128

129
130
131
132
133
  validates :auto_devops_domain,
            allow_blank: true,
            hostname: { allow_numeric_hostname: true, require_valid_tld: true },
            if: :auto_devops_enabled?

134
  validates :enabled_git_access_protocol,
135
            inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true }
136

137
  validates :domain_blacklist,
138
            presence: { message: 'Domain blacklist cannot be empty if Blacklist is enabled.' },
139
140
            if: :domain_blacklist_enabled?

Jacob Vosmaer's avatar
Jacob Vosmaer committed
141
142
143
144
145
146
  validates :housekeeping_incremental_repack_period,
            presence: true,
            numericality: { only_integer: true, greater_than: 0 }

  validates :housekeeping_full_repack_period,
            presence: true,
147
            numericality: { only_integer: true, greater_than_or_equal_to: :housekeeping_incremental_repack_period }
Jacob Vosmaer's avatar
Jacob Vosmaer committed
148
149
150

  validates :housekeeping_gc_period,
            presence: true,
151
            numericality: { only_integer: true, greater_than_or_equal_to: :housekeeping_full_repack_period }
Jacob Vosmaer's avatar
Jacob Vosmaer committed
152

153
154
155
156
  validates :terminal_max_session_time,
            presence: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 0 }

157
158
159
160
  validates :polling_interval_multiplier,
            presence: true,
            numericality: { greater_than_or_equal_to: 0 }

161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  validates :gitaly_timeout_default,
            presence: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 0 }

  validates :gitaly_timeout_medium,
            presence: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 0 }
  validates :gitaly_timeout_medium,
            numericality: { less_than_or_equal_to: :gitaly_timeout_default },
            if: :gitaly_timeout_default
  validates :gitaly_timeout_medium,
            numericality: { greater_than_or_equal_to: :gitaly_timeout_fast },
            if: :gitaly_timeout_fast

  validates :gitaly_timeout_fast,
            presence: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 0 }
  validates :gitaly_timeout_fast,
            numericality: { less_than_or_equal_to: :gitaly_timeout_default },
            if: :gitaly_timeout_default

182
183
184
185
186
187
  validates :diff_max_patch_bytes,
            presence: true,
            numericality: { only_integer: true,
                            greater_than_or_equal_to: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES,
                            less_than_or_equal_to: Gitlab::Git::Diff::MAX_PATCH_BYTES_UPPER_BOUND }

188
189
  validates :user_default_internal_regex, js_regex: true, allow_nil: true

190
191
  validates :commit_email_hostname, format: { with: /\A[^@]+\z/ }

192
193
194
195
  validates :archive_builds_in_seconds,
            allow_nil: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 1.day.seconds }

Jan Provaznik's avatar
Jan Provaznik committed
196
197
198
199
  validates :local_markdown_version,
            allow_nil: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than: 65536 }

200
201
202
203
204
205
206
207
208
209
210
  validates :asset_proxy_url,
            presence: true,
            allow_blank: false,
            url: true,
            if: :asset_proxy_enabled?

  validates :asset_proxy_secret_key,
            presence: true,
            allow_blank: false,
            if: :asset_proxy_enabled?

211
212
213
214
215
216
217
  validates :static_objects_external_storage_url,
            addressable_url: true, allow_blank: true

  validates :static_objects_external_storage_auth_token,
            presence: true,
            if: :static_objects_external_storage_url?

218
219
220
221
  validates :protected_paths,
            length: { maximum: 100, message: N_('is too long (maximum is 100 entries)') },
            allow_nil: false

222
223
224
  validates :push_event_hooks_limit,
            numericality: { greater_than_or_equal_to: 0 }

225
226
227
  validates :push_event_activities_limit,
            numericality: { greater_than_or_equal_to: 0 }

228
  SUPPORTED_KEY_TYPES.each do |type|
229
    validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
230
  end
231

Nick Thomas's avatar
Nick Thomas committed
232
233
  validates :allowed_key_types, presence: true

234
  validates_each :restricted_visibility_levels do |record, attr, value|
Z.J. van de Weg's avatar
Z.J. van de Weg committed
235
    value&.each do |level|
236
      unless Gitlab::VisibilityLevel.options.value?(level)
237
        record.errors.add(attr, _("'%{level}' is not a valid visibility level") % { level: level })
238
239
240
241
      end
    end
  end

242
  validates_each :import_sources do |record, attr, value|
Z.J. van de Weg's avatar
Z.J. van de Weg committed
243
    value&.each do |source|
244
      unless Gitlab::ImportSources.options.value?(source)
245
        record.errors.add(attr, _("'%{source}' is not a import source") % { source: source })
246
247
248
249
      end
    end
  end

Bob Van Landuyt's avatar
Bob Van Landuyt committed
250
251
  validate :terms_exist, if: :enforce_terms?

252
253
254
255
256
  validates :external_authorization_service_default_label,
            presence: true,
            if: :external_authorization_service_enabled

  validates :external_authorization_service_url,
257
            addressable_url: true, allow_blank: true,
258
259
260
261
262
263
264
265
266
267
            if: :external_authorization_service_enabled

  validates :external_authorization_service_timeout,
            numericality: { greater_than: 0, less_than_or_equal_to: 10 },
            if: :external_authorization_service_enabled

  validates :external_auth_client_key,
            presence: true,
            if: -> (setting) { setting.external_auth_client_cert.present? }

268
269
270
271
272
273
274
275
276
277
  validates :lets_encrypt_notification_email,
            devise_email: true,
            format: { without: /@example\.(com|org|net)\z/,
                      message: N_("Let's Encrypt does not accept emails on example.com") },
            allow_blank: true

  validates :lets_encrypt_notification_email,
            presence: true,
            if: :lets_encrypt_terms_of_service_accepted?

278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
  validates :eks_integration_enabled,
            inclusion: { in: [true, false] }

  validates :eks_account_id,
            format: { with: Gitlab::Regex.aws_account_id_regex,
                      message: Gitlab::Regex.aws_account_id_message },
            if: :eks_integration_enabled?

  validates :eks_access_key_id,
            length: { in: 16..128 },
            if: :eks_integration_enabled?

  validates :eks_secret_access_key,
            presence: true,
            if: :eks_integration_enabled?

294
295
296
297
298
299
  validates_with X509CertificateCredentialsValidator,
                 certificate: :external_auth_client_cert,
                 pkey: :external_auth_client_key,
                 pass: :external_auth_client_key_pass,
                 if: -> (setting) { setting.external_auth_client_cert.present? }

300
301
302
303
304
305
  validates :default_ci_config_path,
    format: { without: %r{(\.{2}|\A/)},
              message: N_('cannot include leading slash or directory traversal.') },
    length: { maximum: 255 },
    allow_blank: true

306
307
308
309
310
311
  attr_encrypted :asset_proxy_secret_key,
                 mode: :per_attribute_iv,
                 key: Settings.attr_encrypted_db_key_base_truncated,
                 algorithm: 'aes-256-cbc',
                 insecure_mode: true

312
313
314
315
316
317
318
319
320
321
322
323
  attr_encrypted :external_auth_client_key,
                 mode: :per_attribute_iv,
                 key: Settings.attr_encrypted_db_key_base_truncated,
                 algorithm: 'aes-256-gcm',
                 encode: true

  attr_encrypted :external_auth_client_key_pass,
                 mode: :per_attribute_iv,
                 key: Settings.attr_encrypted_db_key_base_truncated,
                 algorithm: 'aes-256-gcm',
                 encode: true

324
325
326
327
328
  attr_encrypted :lets_encrypt_private_key,
                 mode: :per_attribute_iv,
                 key: Settings.attr_encrypted_db_key_base_truncated,
                 algorithm: 'aes-256-gcm',
                 encode: true
329
330
331
332
333
334

  attr_encrypted :eks_secret_access_key,
                 mode: :per_attribute_iv,
                 key: Settings.attr_encrypted_db_key_base_truncated,
                 algorithm: 'aes-256-gcm',
                 encode: true
335

336
  before_validation :ensure_uuid!
337

338
  before_save :ensure_runners_registration_token
339
  before_save :ensure_health_check_access_token
340

341
  after_commit do
Bob Van Landuyt's avatar
Bob Van Landuyt committed
342
    reset_memoized_terms
343
  end
344
  after_commit :expire_performance_bar_allowed_user_ids_cache, if: -> { previous_changes.key?('performance_bar_allowed_group_id') }
345
346

  def self.create_from_defaults
347
348
349
    transaction(requires_new: true) do
      super
    end
350
351
352
353
  rescue ActiveRecord::RecordNotUnique
    # We already have an ApplicationSetting record, so just return it.
    current_without_cache
  end
354
355
356
357
358
359
360
361

  # By default, the backend is Rails.cache, which uses
  # ActiveSupport::Cache::RedisStore. Since loading ApplicationSetting
  # can cause a significant amount of load on Redis, let's cache it in
  # memory.
  def self.cache_backend
    Gitlab::ThreadMemoryCache.cache_backend
  end
362
363
364
365

  def recaptcha_or_login_protection_enabled
    recaptcha_enabled || login_recaptcha_protection_enabled
  end
366
end
367
368

ApplicationSetting.prepend_if_ee('EE::ApplicationSetting')