BigW Consortium Gitlab

application_setting.rb 16.9 KB
Newer Older
1
class ApplicationSetting < ActiveRecord::Base
2
  include CacheMarkdownField
3
  include TokenAuthenticatable
4

5
  add_authentication_token_field :runners_registration_token
6
  add_authentication_token_field :health_check_access_token
7

8
  CACHE_KEY = 'application_setting.last'.freeze
9 10 11 12 13 14
  DOMAIN_LIST_SEPARATOR = %r{\s*[,;]\s*     # comma or semicolon, optionally surrounded by whitespace
                            |               # or
                            \s              # any whitespace character
                            |               # or
                            [\r\n]          # any number of newline characters
                          }x
15

16 17
  # Setting a key restriction to `-1` means that all keys of this type are
  # forbidden.
18
  FORBIDDEN_KEY_VALUE = KeyRestrictionValidator::FORBIDDEN
19 20
  SUPPORTED_KEY_TYPES = %i[rsa dsa ecdsa ed25519].freeze

21 22 23 24 25 26 27
  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
  serialize :sidekiq_throttling_queues, Array # rubocop:disable Cop/ActiveRecordSerialize
28

29 30 31 32 33
  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

34
  attr_accessor :domain_whitelist_raw, :domain_blacklist_raw
35

36 37
  default_value_for :id, 1

38 39
  validates :uuid, presence: true

40
  validates :session_expire_delay,
41 42
            presence: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 0 }
43

44
  validates :home_page_url,
45 46
            allow_blank: true,
            url: true,
47 48 49 50 51 52
            if: :home_page_url_column_exists?

  validates :help_page_support_url,
            allow_blank: true,
            url: true,
            if: :help_page_support_url_column_exists?
53

54
  validates :after_sign_out_path,
55 56
            allow_blank: true,
            url: true
57

58
  validates :admin_notification_email,
59
            email: true,
60
            allow_blank: true
61

62
  validates :two_factor_grace_period,
63 64 65 66 67 68 69 70 71
            numericality: { greater_than_or_equal_to: 0 }

  validates :recaptcha_site_key,
            presence: true,
            if: :recaptcha_enabled

  validates :recaptcha_private_key,
            presence: true,
            if: :recaptcha_enabled
72

Jeroen Nijhof committed
73 74 75 76
  validates :sentry_dsn,
            presence: true,
            if: :sentry_enabled

77 78 79 80
  validates :clientside_sentry_dsn,
            presence: true,
            if: :clientside_sentry_enabled

81 82 83 84
  validates :akismet_api_key,
            presence: true,
            if: :akismet_enabled

85 86 87 88 89 90 91 92 93 94
  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

95 96 97 98
  validates :koding_url,
            presence: true,
            if: :koding_enabled

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

103 104 105 106
  validates :max_attachment_size,
            presence: true,
            numericality: { only_integer: true, greater_than: 0 }

107 108 109 110
  validates :max_artifacts_size,
            presence: true,
            numericality: { only_integer: true, greater_than: 0 }

111
  validates :default_artifacts_expire_in, presence: true, duration: true
112

113 114 115 116
  validates :container_registry_token_expire_delay,
            presence: true,
            numericality: { only_integer: true, greater_than: 0 }

117 118
  validates :repository_storages, presence: true
  validate :check_repository_storages
119

120
  validates :enabled_git_access_protocol,
121
            inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true }
122

123
  validates :domain_blacklist,
124
            presence: { message: 'Domain blacklist cannot be empty if Blacklist is enabled.' },
125 126
            if: :domain_blacklist_enabled?

127 128 129 130 131 132 133 134 135
  validates :sidekiq_throttling_factor,
            numericality: { greater_than: 0, less_than: 1 },
            presence: { message: 'Throttling factor cannot be empty if Sidekiq Throttling is enabled.' },
            if: :sidekiq_throttling_enabled?

  validates :sidekiq_throttling_queues,
            presence: { message: 'Queues to throttle cannot be empty if Sidekiq Throttling is enabled.' },
            if: :sidekiq_throttling_enabled?

136 137 138 139 140 141
  validates :housekeeping_incremental_repack_period,
            presence: true,
            numericality: { only_integer: true, greater_than: 0 }

  validates :housekeeping_full_repack_period,
            presence: true,
142
            numericality: { only_integer: true, greater_than_or_equal_to: :housekeeping_incremental_repack_period }
143 144 145

  validates :housekeeping_gc_period,
            presence: true,
146
            numericality: { only_integer: true, greater_than_or_equal_to: :housekeeping_full_repack_period }
147

148 149 150 151
  validates :terminal_max_session_time,
            presence: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 0 }

152 153 154 155
  validates :polling_interval_multiplier,
            presence: true,
            numericality: { greater_than_or_equal_to: 0 }

156 157
  validates :circuitbreaker_backoff_threshold,
            :circuitbreaker_failure_count_threshold,
158 159 160 161 162 163
            :circuitbreaker_failure_wait_time,
            :circuitbreaker_failure_reset_time,
            :circuitbreaker_storage_timeout,
            presence: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 0 }

164 165 166 167 168 169 170 171 172 173 174
  validates :circuitbreaker_access_retries,
            presence: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 1 }

  validates_each :circuitbreaker_backoff_threshold do |record, attr, value|
    if value.to_i >= record.circuitbreaker_failure_count_threshold
      record.errors.add(attr, _("The circuitbreaker backoff threshold should be "\
                                "lower than the failure count threshold"))
    end
  end

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
  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

196
  SUPPORTED_KEY_TYPES.each do |type|
197
    validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
198
  end
199

Nick Thomas committed
200 201
  validates :allowed_key_types, presence: true

202
  validates_each :restricted_visibility_levels do |record, attr, value|
203
    value&.each do |level|
204
      unless Gitlab::VisibilityLevel.options.value?(level)
205
        record.errors.add(attr, "'#{level}' is not a valid visibility level")
206 207 208 209
      end
    end
  end

210
  validates_each :import_sources do |record, attr, value|
211
    value&.each do |source|
212
      unless Gitlab::ImportSources.options.value?(source)
213
        record.errors.add(attr, "'#{source}' is not a import source")
214 215 216 217
      end
    end
  end

218
  validates_each :disabled_oauth_sign_in_sources do |record, attr, value|
219 220 221
    value&.each do |source|
      unless Devise.omniauth_providers.include?(source.to_sym)
        record.errors.add(attr, "'#{source}' is not an OAuth sign-in source")
222 223 224 225
      end
    end
  end

226
  before_validation :ensure_uuid!
227

228
  before_save :ensure_runners_registration_token
229
  before_save :ensure_health_check_access_token
230

231
  after_commit do
232
    Rails.cache.write(CACHE_KEY, self)
233 234
  end

235
  def self.current
236 237
    ensure_cache_setup

238
    Rails.cache.fetch(CACHE_KEY) do
239 240 241 242
      ApplicationSetting.last.tap do |settings|
        # do not cache nils
        raise 'missing settings' unless settings
      end
243
    end
244 245 246
  rescue
    # Fall back to an uncached value if there are any problems (e.g. redis down)
    ApplicationSetting.last
247
  end
248

249
  def self.expire
250
    Rails.cache.delete(CACHE_KEY)
251 252
  rescue
    # Gracefully handle when Redis is not available. For example,
253
    # omnibus may fail here during gitlab:assets:compile.
254 255
  end

256
  def self.cached
257 258 259
    value = Rails.cache.read(CACHE_KEY)
    ensure_cache_setup if value.present?
    value
260 261
  end

262 263 264 265 266 267
  def self.ensure_cache_setup
    # This is a workaround for a Rails bug that causes attribute methods not
    # to be loaded when read from cache: https://github.com/rails/rails/issues/27348
    ApplicationSetting.define_attribute_methods
  end

268
  def self.defaults
269 270 271 272
    {
      after_sign_up_text: nil,
      akismet_enabled: false,
      container_registry_token_expire_delay: 5,
273
      default_artifacts_expire_in: '30 days',
274 275 276 277
      default_branch_protection: Settings.gitlab['default_branch_protection'],
      default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
      default_projects_limit: Settings.gitlab['default_projects_limit'],
      default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
278
      default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'],
279 280
      disabled_oauth_sign_in_sources: [],
      domain_whitelist: Settings.gitlab['domain_whitelist'],
281 282 283
      dsa_key_restriction: 0,
      ecdsa_key_restriction: 0,
      ed25519_key_restriction: 0,
284 285
      gravatar_enabled: Settings.gravatar['enabled'],
      help_page_text: nil,
286
      help_page_hide_commercial_content: false,
287 288 289
      unique_ips_limit_per_user: 10,
      unique_ips_limit_time_window: 3600,
      unique_ips_limit_enabled: false,
290 291 292 293 294
      housekeeping_bitmaps_enabled: true,
      housekeeping_enabled: true,
      housekeeping_full_repack_period: 50,
      housekeeping_gc_period: 200,
      housekeeping_incremental_repack_period: 10,
295
      import_sources: Settings.gitlab['import_sources'],
296 297 298 299
      koding_enabled: false,
      koding_url: nil,
      max_artifacts_size: Settings.artifacts['max_size'],
      max_attachment_size: Settings.gitlab['max_attachment_size'],
300 301
      password_authentication_enabled_for_web: Settings.gitlab['signin_enabled'],
      password_authentication_enabled_for_git: true,
302
      performance_bar_allowed_group_id: nil,
303
      rsa_key_restriction: 0,
304 305
      plantuml_enabled: false,
      plantuml_url: nil,
306
      project_export_enabled: true,
307 308 309 310 311 312 313 314 315 316 317 318
      recaptcha_enabled: false,
      repository_checks_enabled: true,
      repository_storages: ['default'],
      require_two_factor_authentication: false,
      restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
      session_expire_delay: Settings.gitlab['session_expire_delay'],
      send_user_confirmation_email: false,
      shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
      shared_runners_text: nil,
      sidekiq_throttling_enabled: false,
      sign_in_text: nil,
      signup_enabled: Settings.gitlab['signup_enabled'],
319
      terminal_max_session_time: 0,
320 321 322 323 324 325 326 327 328
      throttle_unauthenticated_enabled: false,
      throttle_unauthenticated_requests_per_period: 3600,
      throttle_unauthenticated_period_in_seconds: 3600,
      throttle_authenticated_web_enabled: false,
      throttle_authenticated_web_requests_per_period: 7200,
      throttle_authenticated_web_period_in_seconds: 3600,
      throttle_authenticated_api_enabled: false,
      throttle_authenticated_api_requests_per_period: 7200,
      throttle_authenticated_api_period_in_seconds: 3600,
329
      two_factor_grace_period: 48,
330
      user_default_external: false,
331
      polling_interval_multiplier: 1,
332 333 334 335
      usage_ping_enabled: Settings.gitlab['usage_ping_enabled'],
      gitaly_timeout_fast: 10,
      gitaly_timeout_medium: 30,
      gitaly_timeout_default: 55
336 337 338
    }
  end

339
  def self.create_from_defaults
340
    create(defaults)
341
  end
342

343 344 345 346 347 348 349 350
  def self.human_attribute_name(attr, _options = {})
    if attr == :default_artifacts_expire_in
      'Default artifacts expiration'
    else
      super
    end
  end

351
  def home_page_url_column_exists?
352 353
    ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
  end
354

355 356 357 358
  def help_page_support_url_column_exists?
    ActiveRecord::Base.connection.column_exists?(:application_settings, :help_page_support_url)
  end

359 360 361 362
  def sidekiq_throttling_column_exists?
    ActiveRecord::Base.connection.column_exists?(:application_settings, :sidekiq_throttling_enabled)
  end

363
  def domain_whitelist_raw
364
    self.domain_whitelist&.join("\n")
365 366
  end

367
  def domain_blacklist_raw
368
    self.domain_blacklist&.join("\n")
369 370
  end

371 372 373 374 375
  def domain_whitelist_raw=(values)
    self.domain_whitelist = []
    self.domain_whitelist = values.split(DOMAIN_LIST_SEPARATOR)
    self.domain_whitelist.reject! { |d| d.empty? }
    self.domain_whitelist
376
  end
377

378 379
  def domain_blacklist_raw=(values)
    self.domain_blacklist = []
380
    self.domain_blacklist = values.split(DOMAIN_LIST_SEPARATOR)
381
    self.domain_blacklist.reject! { |d| d.empty? }
382
    self.domain_blacklist
383 384 385 386 387 388
  end

  def domain_blacklist_file=(file)
    self.domain_blacklist_raw = file.read
  end

389
  def repository_storages
390
    Array(read_attribute(:repository_storages))
391 392
  end

393
  # DEPRECATED
394
  # repository_storage is still required in the API. Remove in 9.0
395
  # Still used in API v3
396 397 398 399 400 401 402 403
  def repository_storage
    repository_storages.first
  end

  def repository_storage=(value)
    self.repository_storages = [value]
  end

404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
  def default_project_visibility=(level)
    super(Gitlab::VisibilityLevel.level_value(level))
  end

  def default_snippet_visibility=(level)
    super(Gitlab::VisibilityLevel.level_value(level))
  end

  def default_group_visibility=(level)
    super(Gitlab::VisibilityLevel.level_value(level))
  end

  def restricted_visibility_levels=(levels)
    super(levels.map { |level| Gitlab::VisibilityLevel.level_value(level) })
  end

420
  def performance_bar_allowed_group_id=(group_full_path)
421 422 423 424 425 426 427 428 429 430
    group_full_path = nil if group_full_path.blank?

    if group_full_path.nil?
      if group_full_path != performance_bar_allowed_group_id
        super(group_full_path)
        Gitlab::PerformanceBar.expire_allowed_user_ids_cache
      end
      return
    end

431 432
    group = Group.find_by_full_path(group_full_path)

433 434 435 436 437 438 439 440 441
    if group
      if group.id != performance_bar_allowed_group_id
        super(group.id)
        Gitlab::PerformanceBar.expire_allowed_user_ids_cache
      end
    else
      super(nil)
      Gitlab::PerformanceBar.expire_allowed_user_ids_cache
    end
442 443 444 445 446 447
  end

  def performance_bar_allowed_group
    Group.find_by_id(performance_bar_allowed_group_id)
  end

448 449 450
  # Return true if the Performance Bar is enabled for a given group
  def performance_bar_enabled
    performance_bar_allowed_group_id.present?
451 452
  end

453 454 455
  # - If `enable` is true, we early return since the actual attribute that holds
  #   the enabling/disabling is `performance_bar_allowed_group_id`
  # - If `enable` is false, we set `performance_bar_allowed_group_id` to `nil`
456
  def performance_bar_enabled=(enable)
457
    return if Gitlab::Utils.to_boolean(enable)
458

459
    self.performance_bar_allowed_group_id = nil
460 461
  end

462 463 464 465 466 467
  # Choose one of the available repository storage options. Currently all have
  # equal weighting.
  def pick_repository_storage
    repository_storages.sample
  end

468 469 470
  def runners_registration_token
    ensure_runners_registration_token!
  end
471 472 473 474

  def health_check_access_token
    ensure_health_check_access_token!
  end
475

476 477 478 479 480 481
  def sidekiq_throttling_enabled?
    return false unless sidekiq_throttling_column_exists?

    sidekiq_throttling_enabled
  end

482 483 484 485 486 487 488 489
  def usage_ping_can_be_configured?
    Settings.gitlab.usage_ping_enabled
  end

  def usage_ping_enabled
    usage_ping_can_be_configured? && super
  end

490 491 492 493 494 495 496 497 498
  def allowed_key_types
    SUPPORTED_KEY_TYPES.select do |type|
      key_restriction_for(type) != FORBIDDEN_KEY_VALUE
    end
  end

  def key_restriction_for(type)
    attr_name = "#{type}_key_restriction"

Nick Thomas committed
499
    has_attribute?(attr_name) ? public_send(attr_name) : FORBIDDEN_KEY_VALUE # rubocop:disable GitlabSecurity/PublicSend
500 501
  end

502 503 504 505 506 507 508 509
  def allow_signup?
    signup_enabled? && password_authentication_enabled_for_web?
  end

  def password_authentication_enabled?
    password_authentication_enabled_for_web? || password_authentication_enabled_for_git?
  end

510 511
  private

512 513 514 515 516 517
  def ensure_uuid!
    return if uuid?

    self.uuid = SecureRandom.uuid
  end

518 519 520 521 522
  def check_repository_storages
    invalid = repository_storages - Gitlab.config.repositories.storages.keys
    errors.add(:repository_storages, "can't include: #{invalid.join(", ")}") unless
      invalid.empty?
  end
523
end