diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index cd36c963ee5..56cc7c3784f 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -10,17 +10,11 @@ class ApplicationSetting < ActiveRecord::Base add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption) ? :optional : :required } add_authentication_token_field :health_check_access_token - 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 - - # Setting a key restriction to `-1` means that all keys of this type are - # forbidden. - FORBIDDEN_KEY_VALUE = KeyRestrictionValidator::FORBIDDEN - SUPPORTED_KEY_TYPES = %i[rsa dsa ecdsa ed25519].freeze + # 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 serialize :restricted_visibility_levels # rubocop:disable Cop/ActiveRecordSerialize serialize :import_sources # rubocop:disable Cop/ActiveRecordSerialize @@ -42,8 +36,6 @@ class ApplicationSetting < ActiveRecord::Base cache_markdown_field :shared_runners_text, pipeline: :plain_markdown cache_markdown_field :after_sign_up_text - attr_accessor :domain_whitelist_raw, :domain_blacklist_raw - default_value_for :id, 1 chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds @@ -231,266 +223,4 @@ class ApplicationSetting < ActiveRecord::Base reset_memoized_terms end after_commit :expire_performance_bar_allowed_user_ids_cache, if: -> { previous_changes.key?('performance_bar_allowed_group_id') } - - def self.defaults - { - after_sign_up_text: nil, - akismet_enabled: false, - allow_local_requests_from_hooks_and_services: false, - authorized_keys_enabled: true, # TODO default to false if the instance is configured to use AuthorizedKeysCommand - container_registry_token_expire_delay: 5, - default_artifacts_expire_in: '30 days', - default_branch_protection: Settings.gitlab['default_branch_protection'], - default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'], - 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'], - disabled_oauth_sign_in_sources: [], - domain_whitelist: Settings.gitlab['domain_whitelist'], - dsa_key_restriction: 0, - ecdsa_key_restriction: 0, - ed25519_key_restriction: 0, - first_day_of_week: 0, - gitaly_timeout_default: 55, - gitaly_timeout_fast: 10, - gitaly_timeout_medium: 30, - gravatar_enabled: Settings.gravatar['enabled'], - help_page_hide_commercial_content: false, - help_page_text: nil, - hide_third_party_offers: false, - housekeeping_bitmaps_enabled: true, - housekeeping_enabled: true, - housekeeping_full_repack_period: 50, - housekeeping_gc_period: 200, - housekeeping_incremental_repack_period: 10, - import_sources: Settings.gitlab['import_sources'], - max_artifacts_size: Settings.artifacts['max_size'], - max_attachment_size: Settings.gitlab['max_attachment_size'], - mirror_available: true, - password_authentication_enabled_for_git: true, - password_authentication_enabled_for_web: Settings.gitlab['signin_enabled'], - performance_bar_allowed_group_id: nil, - rsa_key_restriction: 0, - plantuml_enabled: false, - plantuml_url: nil, - polling_interval_multiplier: 1, - project_export_enabled: true, - 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, - sign_in_text: nil, - signup_enabled: Settings.gitlab['signup_enabled'], - terminal_max_session_time: 0, - throttle_authenticated_api_enabled: false, - throttle_authenticated_api_period_in_seconds: 3600, - throttle_authenticated_api_requests_per_period: 7200, - throttle_authenticated_web_enabled: false, - throttle_authenticated_web_period_in_seconds: 3600, - throttle_authenticated_web_requests_per_period: 7200, - throttle_unauthenticated_enabled: false, - throttle_unauthenticated_period_in_seconds: 3600, - throttle_unauthenticated_requests_per_period: 3600, - two_factor_grace_period: 48, - unique_ips_limit_enabled: false, - unique_ips_limit_per_user: 10, - unique_ips_limit_time_window: 3600, - usage_ping_enabled: Settings.gitlab['usage_ping_enabled'], - instance_statistics_visibility_private: false, - user_default_external: false, - user_default_internal_regex: nil, - user_show_add_ssh_key_message: true, - usage_stats_set_by_user_id: nil, - diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES, - commit_email_hostname: default_commit_email_hostname, - protected_ci_variables: false, - local_markdown_version: 0 - } - end - - def self.default_commit_email_hostname - "users.noreply.#{Gitlab.config.gitlab.host}" - end - - def self.create_from_defaults - build_from_defaults.tap(&:save) - end - - def self.human_attribute_name(attr, _options = {}) - if attr == :default_artifacts_expire_in - 'Default artifacts expiration' - else - super - end - end - - def home_page_url_column_exists? - ::Gitlab::Database.cached_column_exists?(:application_settings, :home_page_url) - end - - def help_page_support_url_column_exists? - ::Gitlab::Database.cached_column_exists?(:application_settings, :help_page_support_url) - end - - def disabled_oauth_sign_in_sources=(sources) - sources = (sources || []).map(&:to_s) & Devise.omniauth_providers.map(&:to_s) - super(sources) - end - - def domain_whitelist_raw - self.domain_whitelist&.join("\n") - end - - def domain_blacklist_raw - self.domain_blacklist&.join("\n") - end - - 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 - end - - def domain_blacklist_raw=(values) - self.domain_blacklist = [] - self.domain_blacklist = values.split(DOMAIN_LIST_SEPARATOR) - self.domain_blacklist.reject! { |d| d.empty? } - self.domain_blacklist - end - - def domain_blacklist_file=(file) - self.domain_blacklist_raw = file.read - end - - def repository_storages - Array(read_attribute(:repository_storages)) - end - - def commit_email_hostname - super.presence || self.class.default_commit_email_hostname - end - - 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 - - def strip_sentry_values - sentry_dsn.strip! if sentry_dsn.present? - clientside_sentry_dsn.strip! if clientside_sentry_dsn.present? - end - - def performance_bar_allowed_group - Group.find_by_id(performance_bar_allowed_group_id) - end - - # Return true if the Performance Bar is enabled for a given group - def performance_bar_enabled - performance_bar_allowed_group_id.present? - end - - # Choose one of the available repository storage options. Currently all have - # equal weighting. - def pick_repository_storage - repository_storages.sample - end - - def runners_registration_token - ensure_runners_registration_token! - end - - def health_check_access_token - ensure_health_check_access_token! - end - - def usage_ping_can_be_configured? - Settings.gitlab.usage_ping_enabled - end - - def usage_ping_enabled - usage_ping_can_be_configured? && super - end - - 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" - - has_attribute?(attr_name) ? public_send(attr_name) : FORBIDDEN_KEY_VALUE # rubocop:disable GitlabSecurity/PublicSend - end - - 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 - - def user_default_internal_regex_enabled? - user_default_external? && user_default_internal_regex.present? - end - - def user_default_internal_regex_instance - Regexp.new(user_default_internal_regex, Regexp::IGNORECASE) - end - - delegate :terms, to: :latest_terms, allow_nil: true - def latest_terms - @latest_terms ||= Term.latest - end - - def reset_memoized_terms - @latest_terms = nil - latest_terms - end - - def archive_builds_older_than - archive_builds_in_seconds.seconds.ago if archive_builds_in_seconds - end - - private - - def ensure_uuid! - return if uuid? - - self.uuid = SecureRandom.uuid - end - - 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 - - def terms_exist - return unless enforce_terms? - - errors.add(:terms, "You need to set terms to be enforced") unless terms.present? - end - - def expire_performance_bar_allowed_user_ids_cache - Gitlab::PerformanceBar.expire_allowed_user_ids_cache - end end diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb new file mode 100644 index 00000000000..265aa1d4965 --- /dev/null +++ b/app/models/application_setting_implementation.rb @@ -0,0 +1,281 @@ +# frozen_string_literal: true + +module ApplicationSettingImplementation + extend ActiveSupport::Concern + + 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 + + # Setting a key restriction to `-1` means that all keys of this type are + # forbidden. + FORBIDDEN_KEY_VALUE = KeyRestrictionValidator::FORBIDDEN + SUPPORTED_KEY_TYPES = %i[rsa dsa ecdsa ed25519].freeze + + class_methods do + def defaults + { + after_sign_up_text: nil, + akismet_enabled: false, + allow_local_requests_from_hooks_and_services: false, + authorized_keys_enabled: true, # TODO default to false if the instance is configured to use AuthorizedKeysCommand + container_registry_token_expire_delay: 5, + default_artifacts_expire_in: '30 days', + default_branch_protection: Settings.gitlab['default_branch_protection'], + default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'], + 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'], + disabled_oauth_sign_in_sources: [], + domain_whitelist: Settings.gitlab['domain_whitelist'], + dsa_key_restriction: 0, + ecdsa_key_restriction: 0, + ed25519_key_restriction: 0, + first_day_of_week: 0, + gitaly_timeout_default: 55, + gitaly_timeout_fast: 10, + gitaly_timeout_medium: 30, + gravatar_enabled: Settings.gravatar['enabled'], + help_page_hide_commercial_content: false, + help_page_text: nil, + hide_third_party_offers: false, + housekeeping_bitmaps_enabled: true, + housekeeping_enabled: true, + housekeeping_full_repack_period: 50, + housekeeping_gc_period: 200, + housekeeping_incremental_repack_period: 10, + import_sources: Settings.gitlab['import_sources'], + max_artifacts_size: Settings.artifacts['max_size'], + max_attachment_size: Settings.gitlab['max_attachment_size'], + mirror_available: true, + password_authentication_enabled_for_git: true, + password_authentication_enabled_for_web: Settings.gitlab['signin_enabled'], + performance_bar_allowed_group_id: nil, + rsa_key_restriction: 0, + plantuml_enabled: false, + plantuml_url: nil, + polling_interval_multiplier: 1, + project_export_enabled: true, + 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, + sign_in_text: nil, + signup_enabled: Settings.gitlab['signup_enabled'], + terminal_max_session_time: 0, + throttle_authenticated_api_enabled: false, + throttle_authenticated_api_period_in_seconds: 3600, + throttle_authenticated_api_requests_per_period: 7200, + throttle_authenticated_web_enabled: false, + throttle_authenticated_web_period_in_seconds: 3600, + throttle_authenticated_web_requests_per_period: 7200, + throttle_unauthenticated_enabled: false, + throttle_unauthenticated_period_in_seconds: 3600, + throttle_unauthenticated_requests_per_period: 3600, + two_factor_grace_period: 48, + unique_ips_limit_enabled: false, + unique_ips_limit_per_user: 10, + unique_ips_limit_time_window: 3600, + usage_ping_enabled: Settings.gitlab['usage_ping_enabled'], + instance_statistics_visibility_private: false, + user_default_external: false, + user_default_internal_regex: nil, + user_show_add_ssh_key_message: true, + usage_stats_set_by_user_id: nil, + diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES, + commit_email_hostname: default_commit_email_hostname, + protected_ci_variables: false, + local_markdown_version: 0 + } + end + + def default_commit_email_hostname + "users.noreply.#{Gitlab.config.gitlab.host}" + end + + def create_from_defaults + build_from_defaults.tap(&:save) + end + + def human_attribute_name(attr, _options = {}) + if attr == :default_artifacts_expire_in + 'Default artifacts expiration' + else + super + end + end + end + + def home_page_url_column_exists? + ::Gitlab::Database.cached_column_exists?(:application_settings, :home_page_url) + end + + def help_page_support_url_column_exists? + ::Gitlab::Database.cached_column_exists?(:application_settings, :help_page_support_url) + end + + def disabled_oauth_sign_in_sources=(sources) + sources = (sources || []).map(&:to_s) & Devise.omniauth_providers.map(&:to_s) + super(sources) + end + + def domain_whitelist_raw + self.domain_whitelist&.join("\n") + end + + def domain_blacklist_raw + self.domain_blacklist&.join("\n") + end + + 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 + end + + def domain_blacklist_raw=(values) + self.domain_blacklist = [] + self.domain_blacklist = values.split(DOMAIN_LIST_SEPARATOR) + self.domain_blacklist.reject! { |d| d.empty? } + self.domain_blacklist + end + + def domain_blacklist_file=(file) + self.domain_blacklist_raw = file.read + end + + def repository_storages + Array(read_attribute(:repository_storages)) + end + + def commit_email_hostname + super.presence || self.class.default_commit_email_hostname + end + + 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 + + def strip_sentry_values + sentry_dsn.strip! if sentry_dsn.present? + clientside_sentry_dsn.strip! if clientside_sentry_dsn.present? + end + + def performance_bar_allowed_group + Group.find_by_id(performance_bar_allowed_group_id) + end + + # Return true if the Performance Bar is enabled for a given group + def performance_bar_enabled + performance_bar_allowed_group_id.present? + end + + # Choose one of the available repository storage options. Currently all have + # equal weighting. + def pick_repository_storage + repository_storages.sample + end + + def runners_registration_token + ensure_runners_registration_token! + end + + def health_check_access_token + ensure_health_check_access_token! + end + + def usage_ping_can_be_configured? + Settings.gitlab.usage_ping_enabled + end + + def usage_ping_enabled + usage_ping_can_be_configured? && super + end + + 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" + + has_attribute?(attr_name) ? public_send(attr_name) : FORBIDDEN_KEY_VALUE # rubocop:disable GitlabSecurity/PublicSend + end + + 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 + + def user_default_internal_regex_enabled? + user_default_external? && user_default_internal_regex.present? + end + + def user_default_internal_regex_instance + Regexp.new(user_default_internal_regex, Regexp::IGNORECASE) + end + + delegate :terms, to: :latest_terms, allow_nil: true + def latest_terms + @latest_terms ||= ApplicationSetting::Term.latest + end + + def reset_memoized_terms + @latest_terms = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables + latest_terms + end + + def archive_builds_older_than + archive_builds_in_seconds.seconds.ago if archive_builds_in_seconds + end + + private + + def ensure_uuid! + return if uuid? + + self.uuid = SecureRandom.uuid + end + + 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 + + def terms_exist + return unless enforce_terms? + + errors.add(:terms, "You need to set terms to be enforced") unless terms.present? + end + + def expire_performance_bar_allowed_user_ids_cache + Gitlab::PerformanceBar.expire_allowed_user_ids_cache + end +end diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb index f5bb559ceda..8c769be0489 100644 --- a/app/models/concerns/token_authenticatable.rb +++ b/app/models/concerns/token_authenticatable.rb @@ -26,34 +26,41 @@ module TokenAuthenticatable end end - define_method(token_field) do + mod = token_authenticatable_module + + mod.define_method(token_field) do strategy.get_token(self) end - define_method("set_#{token_field}") do |token| + mod.define_method("set_#{token_field}") do |token| strategy.set_token(self, token) end - define_method("ensure_#{token_field}") do + mod.define_method("ensure_#{token_field}") do strategy.ensure_token(self) end # Returns a token, but only saves when the database is in read & write mode - define_method("ensure_#{token_field}!") do + mod.define_method("ensure_#{token_field}!") do strategy.ensure_token!(self) end # Resets the token, but only saves when the database is in read & write mode - define_method("reset_#{token_field}!") do + mod.define_method("reset_#{token_field}!") do strategy.reset_token!(self) end - define_method("#{token_field}_matches?") do |other_token| + mod.define_method("#{token_field}_matches?") do |other_token| token = read_attribute(token_field) token.present? && ActiveSupport::SecurityUtils.variable_size_secure_compare(other_token, token) end end + def token_authenticatable_module + @token_authenticatable_module ||= + const_set(:TokenAuthenticatable, Module.new).tap(&method(:include)) + end + def token_authenticatable_fields @token_authenticatable_fields ||= [] end diff --git a/lib/gitlab/fake_application_settings.rb b/lib/gitlab/fake_application_settings.rb index bd806269bf0..77f7d9490f3 100644 --- a/lib/gitlab/fake_application_settings.rb +++ b/lib/gitlab/fake_application_settings.rb @@ -7,6 +7,8 @@ # column type without parsing db/schema.rb. module Gitlab class FakeApplicationSettings < OpenStruct + include ApplicationSettingImplementation + # Mimic ActiveRecord predicate methods for boolean values def self.define_predicate_methods(options) options.each do |key, value| @@ -26,20 +28,7 @@ module Gitlab FakeApplicationSettings.define_predicate_methods(options) end - def key_restriction_for(type) - 0 - end - - def allowed_key_types - ApplicationSetting::SUPPORTED_KEY_TYPES - end - - def pick_repository_storage - repository_storages.sample - end - - def commit_email_hostname - super.presence || ApplicationSetting.default_commit_email_hostname - end + alias_method :read_attribute, :[] + alias_method :has_attribute?, :[] end end diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb index fd151e8a298..c1baf88778d 100644 --- a/spec/controllers/registrations_controller_spec.rb +++ b/spec/controllers/registrations_controller_spec.rb @@ -15,7 +15,7 @@ describe RegistrationsController do context 'when send_user_confirmation_email is false' do it 'signs the user in' do - allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(false) + stub_application_setting(send_user_confirmation_email: false) expect { post(:create, params: user_params) }.not_to change { ActionMailer::Base.deliveries.size } expect(subject.current_user).not_to be_nil @@ -24,7 +24,7 @@ describe RegistrationsController do context 'when send_user_confirmation_email is true' do it 'does not authenticate user and sends confirmation email' do - allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(true) + stub_application_setting(send_user_confirmation_email: true) post(:create, params: user_params) @@ -35,7 +35,7 @@ describe RegistrationsController do context 'when signup_enabled? is false' do it 'redirects to sign_in' do - allow_any_instance_of(ApplicationSetting).to receive(:signup_enabled?).and_return(false) + stub_application_setting(signup_enabled: false) expect { post(:create, params: user_params) }.not_to change(User, :count) expect(response).to redirect_to(new_user_session_path) diff --git a/spec/lib/gitlab/fake_application_settings_spec.rb b/spec/lib/gitlab/fake_application_settings_spec.rb index af12e13d36d..c81cb83d9f4 100644 --- a/spec/lib/gitlab/fake_application_settings_spec.rb +++ b/spec/lib/gitlab/fake_application_settings_spec.rb @@ -1,32 +1,33 @@ require 'spec_helper' describe Gitlab::FakeApplicationSettings do - let(:defaults) { { password_authentication_enabled_for_web: false, foobar: 'asdf', signup_enabled: true, 'test?' => 123 } } + let(:defaults) do + described_class.defaults.merge( + foobar: 'asdf', + 'test?' => 123 + ) + end - subject { described_class.new(defaults) } + let(:setting) { described_class.new(defaults) } it 'wraps OpenStruct variables properly' do - expect(subject.password_authentication_enabled_for_web).to be_falsey - expect(subject.signup_enabled).to be_truthy - expect(subject.foobar).to eq('asdf') + expect(setting.password_authentication_enabled_for_web).to be_truthy + expect(setting.signup_enabled).to be_truthy + expect(setting.foobar).to eq('asdf') end it 'defines predicate methods' do - expect(subject.password_authentication_enabled_for_web?).to be_falsey - expect(subject.signup_enabled?).to be_truthy - end - - it 'predicate method changes when value is updated' do - subject.password_authentication_enabled_for_web = true - - expect(subject.password_authentication_enabled_for_web?).to be_truthy + expect(setting.password_authentication_enabled_for_web?).to be_truthy + expect(setting.signup_enabled?).to be_truthy end it 'does not define a predicate method' do - expect(subject.foobar?).to be_nil + expect(setting.foobar?).to be_nil end it 'does not override an existing predicate method' do - expect(subject.test?).to eq(123) + expect(setting.test?).to eq(123) end + + it_behaves_like 'application settings examples' end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 789e14e8a20..314f0728b8e 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -117,14 +117,6 @@ describe ApplicationSetting do it { expect(setting.repository_storages).to eq(['default']) } end - context '#commit_email_hostname' do - it 'returns configured gitlab hostname if commit_email_hostname is not defined' do - setting.update(commit_email_hostname: nil) - - expect(setting.commit_email_hostname).to eq("users.noreply.#{Gitlab.config.gitlab.host}") - end - end - context 'auto_devops_domain setting' do context 'when auto_devops_enabled? is true' do before do @@ -182,15 +174,6 @@ describe ApplicationSetting do it { is_expected.not_to allow_value("").for(:repository_storages) } it { is_expected.not_to allow_value(nil).for(:repository_storages) } end - - describe '.pick_repository_storage' do - it 'uses Array#sample to pick a random storage' do - array = double('array', sample: 'random') - expect(setting).to receive(:repository_storages).and_return(array) - - expect(setting.pick_repository_storage).to eq('random') - end - end end context 'housekeeping settings' do @@ -367,65 +350,6 @@ describe ApplicationSetting do end end - context 'restricted signup domains' do - it 'sets single domain' do - setting.domain_whitelist_raw = 'example.com' - expect(setting.domain_whitelist).to eq(['example.com']) - end - - it 'sets multiple domains with spaces' do - setting.domain_whitelist_raw = 'example.com *.example.com' - expect(setting.domain_whitelist).to eq(['example.com', '*.example.com']) - end - - it 'sets multiple domains with newlines and a space' do - setting.domain_whitelist_raw = "example.com\n *.example.com" - expect(setting.domain_whitelist).to eq(['example.com', '*.example.com']) - end - - it 'sets multiple domains with commas' do - setting.domain_whitelist_raw = "example.com, *.example.com" - expect(setting.domain_whitelist).to eq(['example.com', '*.example.com']) - end - end - - context 'blacklisted signup domains' do - it 'sets single domain' do - setting.domain_blacklist_raw = 'example.com' - expect(setting.domain_blacklist).to contain_exactly('example.com') - end - - it 'sets multiple domains with spaces' do - setting.domain_blacklist_raw = 'example.com *.example.com' - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') - end - - it 'sets multiple domains with newlines and a space' do - setting.domain_blacklist_raw = "example.com\n *.example.com" - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') - end - - it 'sets multiple domains with commas' do - setting.domain_blacklist_raw = "example.com, *.example.com" - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') - end - - it 'sets multiple domains with semicolon' do - setting.domain_blacklist_raw = "example.com; *.example.com" - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') - end - - it 'sets multiple domains with mixture of everything' do - setting.domain_blacklist_raw = "example.com; *.example.com\n test.com\sblock.com yes.com" - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com', 'test.com', 'block.com', 'yes.com') - end - - it 'sets multiple domain with file' do - setting.domain_blacklist_file = File.open(Rails.root.join('spec/fixtures/', 'domain_blacklist.txt')) - expect(setting.domain_blacklist).to contain_exactly('example.com', 'test.com', 'foo.bar') - end - end - describe 'performance bar settings' do describe 'performance_bar_allowed_group' do context 'with no performance_bar_allowed_group_id saved' do @@ -462,142 +386,6 @@ describe ApplicationSetting do end end - describe 'usage ping settings' do - context 'when the usage ping is disabled in gitlab.yml' do - before do - allow(Settings.gitlab).to receive(:usage_ping_enabled).and_return(false) - end - - it 'does not allow the usage ping to be configured' do - expect(setting.usage_ping_can_be_configured?).to be_falsey - end - - context 'when the usage ping is disabled in the DB' do - before do - setting.usage_ping_enabled = false - end - - it 'returns false for usage_ping_enabled' do - expect(setting.usage_ping_enabled).to be_falsey - end - end - - context 'when the usage ping is enabled in the DB' do - before do - setting.usage_ping_enabled = true - end - - it 'returns false for usage_ping_enabled' do - expect(setting.usage_ping_enabled).to be_falsey - end - end - end - - context 'when the usage ping is enabled in gitlab.yml' do - before do - allow(Settings.gitlab).to receive(:usage_ping_enabled).and_return(true) - end - - it 'allows the usage ping to be configured' do - expect(setting.usage_ping_can_be_configured?).to be_truthy - end - - context 'when the usage ping is disabled in the DB' do - before do - setting.usage_ping_enabled = false - end - - it 'returns false for usage_ping_enabled' do - expect(setting.usage_ping_enabled).to be_falsey - end - end - - context 'when the usage ping is enabled in the DB' do - before do - setting.usage_ping_enabled = true - end - - it 'returns true for usage_ping_enabled' do - expect(setting.usage_ping_enabled).to be_truthy - end - end - end - end - - describe '#allowed_key_types' do - it 'includes all key types by default' do - expect(setting.allowed_key_types).to contain_exactly(*described_class::SUPPORTED_KEY_TYPES) - end - - it 'excludes disabled key types' do - expect(setting.allowed_key_types).to include(:ed25519) - - setting.ed25519_key_restriction = described_class::FORBIDDEN_KEY_VALUE - - expect(setting.allowed_key_types).not_to include(:ed25519) - end - end - - describe '#key_restriction_for' do - it 'returns the restriction value for recognised types' do - setting.rsa_key_restriction = 1024 - - expect(setting.key_restriction_for(:rsa)).to eq(1024) - end - - it 'allows types to be passed as a string' do - setting.rsa_key_restriction = 1024 - - expect(setting.key_restriction_for('rsa')).to eq(1024) - end - - it 'returns forbidden for unrecognised type' do - expect(setting.key_restriction_for(:foo)).to eq(described_class::FORBIDDEN_KEY_VALUE) - end - end - - describe '#allow_signup?' do - it 'returns true' do - expect(setting.allow_signup?).to be_truthy - end - - it 'returns false if signup is disabled' do - allow(setting).to receive(:signup_enabled?).and_return(false) - - expect(setting.allow_signup?).to be_falsey - end - - it 'returns false if password authentication is disabled for the web interface' do - allow(setting).to receive(:password_authentication_enabled_for_web?).and_return(false) - - expect(setting.allow_signup?).to be_falsey - end - end - - describe '#user_default_internal_regex_enabled?' do - using RSpec::Parameterized::TableSyntax - - where(:user_default_external, :user_default_internal_regex, :result) do - false | nil | false - false | '' | false - false | '^(?:(?!\.ext@).)*$\r?\n?' | false - true | '' | false - true | nil | false - true | '^(?:(?!\.ext@).)*$\r?\n?' | true - end - - with_them do - before do - setting.update(user_default_external: user_default_external) - setting.update(user_default_internal_regex: user_default_internal_regex) - end - - subject { setting.user_default_internal_regex_enabled? } - - it { is_expected.to eq(result) } - end - end - context 'diff limit settings' do describe '#diff_max_patch_bytes' do context 'validations' do @@ -613,23 +401,5 @@ describe ApplicationSetting do end end - describe '#archive_builds_older_than' do - subject { setting.archive_builds_older_than } - - context 'when the archive_builds_in_seconds is set' do - before do - setting.archive_builds_in_seconds = 3600 - end - - it { is_expected.to be_within(1.minute).of(1.hour.ago) } - end - - context 'when the archive_builds_in_seconds is set' do - before do - setting.archive_builds_in_seconds = nil - end - - it { is_expected.to be_nil } - end - end + it_behaves_like 'application settings examples' end diff --git a/spec/support/shared_examples/application_setting_examples.rb b/spec/support/shared_examples/application_setting_examples.rb new file mode 100644 index 00000000000..e7ec24c5b7e --- /dev/null +++ b/spec/support/shared_examples/application_setting_examples.rb @@ -0,0 +1,252 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'application settings examples' do + context 'restricted signup domains' do + it 'sets single domain' do + setting.domain_whitelist_raw = 'example.com' + expect(setting.domain_whitelist).to eq(['example.com']) + end + + it 'sets multiple domains with spaces' do + setting.domain_whitelist_raw = 'example.com *.example.com' + expect(setting.domain_whitelist).to eq(['example.com', '*.example.com']) + end + + it 'sets multiple domains with newlines and a space' do + setting.domain_whitelist_raw = "example.com\n *.example.com" + expect(setting.domain_whitelist).to eq(['example.com', '*.example.com']) + end + + it 'sets multiple domains with commas' do + setting.domain_whitelist_raw = "example.com, *.example.com" + expect(setting.domain_whitelist).to eq(['example.com', '*.example.com']) + end + end + + context 'blacklisted signup domains' do + it 'sets single domain' do + setting.domain_blacklist_raw = 'example.com' + expect(setting.domain_blacklist).to contain_exactly('example.com') + end + + it 'sets multiple domains with spaces' do + setting.domain_blacklist_raw = 'example.com *.example.com' + expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') + end + + it 'sets multiple domains with newlines and a space' do + setting.domain_blacklist_raw = "example.com\n *.example.com" + expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') + end + + it 'sets multiple domains with commas' do + setting.domain_blacklist_raw = "example.com, *.example.com" + expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') + end + + it 'sets multiple domains with semicolon' do + setting.domain_blacklist_raw = "example.com; *.example.com" + expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') + end + + it 'sets multiple domains with mixture of everything' do + setting.domain_blacklist_raw = "example.com; *.example.com\n test.com\sblock.com yes.com" + expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com', 'test.com', 'block.com', 'yes.com') + end + + it 'sets multiple domain with file' do + setting.domain_blacklist_file = File.open(Rails.root.join('spec/fixtures/', 'domain_blacklist.txt')) + expect(setting.domain_blacklist).to contain_exactly('example.com', 'test.com', 'foo.bar') + end + end + + describe 'usage ping settings' do + context 'when the usage ping is disabled in gitlab.yml' do + before do + allow(Settings.gitlab).to receive(:usage_ping_enabled).and_return(false) + end + + it 'does not allow the usage ping to be configured' do + expect(setting.usage_ping_can_be_configured?).to be_falsey + end + + context 'when the usage ping is disabled in the DB' do + before do + setting.usage_ping_enabled = false + end + + it 'returns false for usage_ping_enabled' do + expect(setting.usage_ping_enabled).to be_falsey + end + end + + context 'when the usage ping is enabled in the DB' do + before do + setting.usage_ping_enabled = true + end + + it 'returns false for usage_ping_enabled' do + expect(setting.usage_ping_enabled).to be_falsey + end + end + end + + context 'when the usage ping is enabled in gitlab.yml' do + before do + allow(Settings.gitlab).to receive(:usage_ping_enabled).and_return(true) + end + + it 'allows the usage ping to be configured' do + expect(setting.usage_ping_can_be_configured?).to be_truthy + end + + context 'when the usage ping is disabled in the DB' do + before do + setting.usage_ping_enabled = false + end + + it 'returns false for usage_ping_enabled' do + expect(setting.usage_ping_enabled).to be_falsey + end + end + + context 'when the usage ping is enabled in the DB' do + before do + setting.usage_ping_enabled = true + end + + it 'returns true for usage_ping_enabled' do + expect(setting.usage_ping_enabled).to be_truthy + end + end + end + end + + describe '#allowed_key_types' do + it 'includes all key types by default' do + expect(setting.allowed_key_types).to contain_exactly(*described_class::SUPPORTED_KEY_TYPES) + end + + it 'excludes disabled key types' do + expect(setting.allowed_key_types).to include(:ed25519) + + setting.ed25519_key_restriction = described_class::FORBIDDEN_KEY_VALUE + + expect(setting.allowed_key_types).not_to include(:ed25519) + end + end + + describe '#key_restriction_for' do + it 'returns the restriction value for recognised types' do + setting.rsa_key_restriction = 1024 + + expect(setting.key_restriction_for(:rsa)).to eq(1024) + end + + it 'allows types to be passed as a string' do + setting.rsa_key_restriction = 1024 + + expect(setting.key_restriction_for('rsa')).to eq(1024) + end + + it 'returns forbidden for unrecognised type' do + expect(setting.key_restriction_for(:foo)).to eq(described_class::FORBIDDEN_KEY_VALUE) + end + end + + describe '#allow_signup?' do + it 'returns true' do + expect(setting.allow_signup?).to be_truthy + end + + it 'returns false if signup is disabled' do + allow(setting).to receive(:signup_enabled?).and_return(false) + + expect(setting.allow_signup?).to be_falsey + end + + it 'returns false if password authentication is disabled for the web interface' do + allow(setting).to receive(:password_authentication_enabled_for_web?).and_return(false) + + expect(setting.allow_signup?).to be_falsey + end + end + + describe '#pick_repository_storage' do + it 'uses Array#sample to pick a random storage' do + array = double('array', sample: 'random') + expect(setting).to receive(:repository_storages).and_return(array) + + expect(setting.pick_repository_storage).to eq('random') + end + end + + describe '#user_default_internal_regex_enabled?' do + using RSpec::Parameterized::TableSyntax + + where(:user_default_external, :user_default_internal_regex, :result) do + false | nil | false + false | '' | false + false | '^(?:(?!\.ext@).)*$\r?\n?' | false + true | '' | false + true | nil | false + true | '^(?:(?!\.ext@).)*$\r?\n?' | true + end + + with_them do + before do + setting.user_default_external = user_default_external + setting.user_default_internal_regex = user_default_internal_regex + end + + subject { setting.user_default_internal_regex_enabled? } + + it { is_expected.to eq(result) } + end + end + + describe '#archive_builds_older_than' do + subject { setting.archive_builds_older_than } + + context 'when the archive_builds_in_seconds is set' do + before do + setting.archive_builds_in_seconds = 3600 + end + + it { is_expected.to be_within(1.minute).of(1.hour.ago) } + end + + context 'when the archive_builds_in_seconds is set' do + before do + setting.archive_builds_in_seconds = nil + end + + it { is_expected.to be_nil } + end + end + + describe '#commit_email_hostname' do + context 'when the value is provided' do + before do + setting.commit_email_hostname = 'localhost' + end + + it 'returns the provided value' do + expect(setting.commit_email_hostname).to eq('localhost') + end + end + + context 'when the value is not provided' do + it 'returns the default from the class' do + expect(setting.commit_email_hostname) + .to eq(described_class.default_commit_email_hostname) + end + end + end + + it 'predicate method changes when value is updated' do + setting.password_authentication_enabled_for_web = false + + expect(setting.password_authentication_enabled_for_web?).to be_falsey + end +end