diff options
author | Robert Speicher <robert@gitlab.com> | 2016-07-20 20:26:00 +0000 |
---|---|---|
committer | Robert Speicher <robert@gitlab.com> | 2016-07-20 20:26:00 +0000 |
commit | 22c8e21bf432a68f05bd81685d76acc0a3c9607f (patch) | |
tree | 3d113695853db1348f31d1fbc1fbe17590aecefa /app | |
parent | 5a77eb153669bfbac4ab1f05615d11965beb826d (diff) | |
parent | 1a057d2b4013feb151e77aa71dd0b5a83412d2d6 (diff) | |
download | gitlab-ce-22c8e21bf432a68f05bd81685d76acc0a3c9607f.tar.gz |
Merge branch 'email-domain-blacklist' into 'master'
Added the ability to block sign ups using a domain blacklist.
As part of this MR, I restructured the Application Settings form to separate **Sign up** related settings from **Sign in** related settings and make everything cleaner and easier to read.
Fixes #19749
Related to #5573
See merge request !5259
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/admin.js.coffee | 11 | ||||
-rw-r--r-- | app/controllers/admin/application_settings_controller.rb | 6 | ||||
-rw-r--r-- | app/models/application_setting.rb | 52 | ||||
-rw-r--r-- | app/models/user.rb | 61 | ||||
-rw-r--r-- | app/views/admin/application_settings/_form.html.haml | 57 |
5 files changed, 135 insertions, 52 deletions
diff --git a/app/assets/javascripts/admin.js.coffee b/app/assets/javascripts/admin.js.coffee index b2b8e1b7ffb..90c09619f8c 100644 --- a/app/assets/javascripts/admin.js.coffee +++ b/app/assets/javascripts/admin.js.coffee @@ -38,3 +38,14 @@ class @Admin $('li.group_member').bind 'ajax:success', -> Turbolinks.visit(location.href) + + showBlacklistType = -> + if $("input[name='blacklist_type']:checked").val() == 'file' + $('.blacklist-file').show() + $('.blacklist-raw').hide() + else + $('.blacklist-file').hide() + $('.blacklist-raw').show() + + $("input[name='blacklist_type']").click showBlacklistType + showBlacklistType() diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 23ba83aba0e..9e1dc15de84 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -64,6 +64,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController params[:application_setting][:disabled_oauth_sign_in_sources] = AuthHelper.button_based_providers.map(&:to_s) - Array(enabled_oauth_sign_in_sources) + params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file] params.require(:application_setting).permit( :default_projects_limit, @@ -83,7 +84,10 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :default_project_visibility, :default_snippet_visibility, :default_group_visibility, - :restricted_signup_domains_raw, + :domain_whitelist_raw, + :domain_blacklist_enabled, + :domain_blacklist_raw, + :domain_blacklist_file, :version_check_enabled, :admin_notification_email, :user_oauth_applications, diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index c6f77cc055f..8c19d9dc9c8 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -4,12 +4,20 @@ class ApplicationSetting < ActiveRecord::Base add_authentication_token_field :health_check_access_token CACHE_KEY = 'application_setting.last' + 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 serialize :restricted_visibility_levels serialize :import_sources serialize :disabled_oauth_sign_in_sources, Array - serialize :restricted_signup_domains, Array - attr_accessor :restricted_signup_domains_raw + serialize :domain_whitelist, Array + serialize :domain_blacklist, Array + + attr_accessor :domain_whitelist_raw, :domain_blacklist_raw validates :session_expire_delay, presence: true, @@ -62,6 +70,10 @@ class ApplicationSetting < ActiveRecord::Base validates :enabled_git_access_protocol, inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true } + validates :domain_blacklist, + presence: { message: 'Domain blacklist cannot be empty if Blacklist is enabled.' }, + if: :domain_blacklist_enabled? + validates_each :restricted_visibility_levels do |record, attr, value| unless value.nil? value.each do |level| @@ -129,7 +141,7 @@ class ApplicationSetting < ActiveRecord::Base session_expire_delay: Settings.gitlab['session_expire_delay'], default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], - restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], + domain_whitelist: Settings.gitlab['domain_whitelist'], import_sources: %w[github bitbucket gitlab gitorious google_code fogbugz git gitlab_project], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], max_artifacts_size: Settings.artifacts['max_size'], @@ -150,20 +162,30 @@ class ApplicationSetting < ActiveRecord::Base ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url) end - def restricted_signup_domains_raw - self.restricted_signup_domains.join("\n") unless self.restricted_signup_domains.nil? + def domain_whitelist_raw + self.domain_whitelist.join("\n") unless self.domain_whitelist.nil? + end + + def domain_blacklist_raw + self.domain_blacklist.join("\n") unless self.domain_blacklist.nil? + 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 restricted_signup_domains_raw=(values) - self.restricted_signup_domains = [] - self.restricted_signup_domains = values.split( - /\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace - | # or - \s # any whitespace character - | # or - [\r\n] # any number of newline characters - /x) - self.restricted_signup_domains.reject! { |d| d.empty? } + def domain_blacklist_file=(file) + self.domain_blacklist_raw = file.read end def runners_registration_token diff --git a/app/models/user.rb b/app/models/user.rb index 975e935fa20..7cbd97c0069 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -111,7 +111,7 @@ class User < ActiveRecord::Base validates :avatar, file_size: { maximum: 200.kilobytes.to_i } before_validation :generate_password, on: :create - before_validation :restricted_signup_domains, on: :create + before_validation :signup_domain_valid?, on: :create before_validation :sanitize_attrs before_validation :set_notification_email, if: ->(user) { user.email_changed? } before_validation :set_public_email, if: ->(user) { user.public_email_changed? } @@ -760,29 +760,6 @@ class User < ActiveRecord::Base Project.where(id: events) end - def restricted_signup_domains - email_domains = current_application_settings.restricted_signup_domains - - unless email_domains.blank? - match_found = email_domains.any? do |domain| - escaped = Regexp.escape(domain).gsub('\*', '.*?') - regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE - email_domain = Mail::Address.new(self.email).domain - email_domain =~ regexp - end - - unless match_found - self.errors.add :email, - 'is not whitelisted. ' + - 'Email domains valid for registration are: ' + - email_domains.join(', ') - return false - end - end - - true - end - def can_be_removed? !solo_owned_groups.present? end @@ -881,4 +858,40 @@ class User < ActiveRecord::Base self.can_create_group = false self.projects_limit = 0 end + + def signup_domain_valid? + valid = true + error = nil + + if current_application_settings.domain_blacklist_enabled? + blocked_domains = current_application_settings.domain_blacklist + if domain_matches?(blocked_domains, self.email) + error = 'is not from an allowed domain.' + valid = false + end + end + + allowed_domains = current_application_settings.domain_whitelist + unless allowed_domains.blank? + if domain_matches?(allowed_domains, self.email) + valid = true + else + error = "is not whitelisted. Email domains valid for registration are: #{allowed_domains.join(', ')}" + valid = false + end + end + + self.errors.add(:email, error) unless valid + + valid + end + + def domain_matches?(email_domains, email) + signup_domain = Mail::Address.new(email).domain + email_domains.any? do |domain| + escaped = Regexp.escape(domain).gsub('\*', '.*?') + regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE + signup_domain =~ regexp + end + end end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 538d8176ce7..23b52d08df7 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -109,7 +109,7 @@ Newly registered users will by default be external %fieldset - %legend Sign-in Restrictions + %legend Sign-up Restrictions .form-group .col-sm-offset-2.col-sm-10 .checkbox @@ -123,6 +123,49 @@ = f.check_box :send_user_confirmation_email Send confirmation email on sign-up .form-group + = f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'control-label col-sm-2' + .col-sm-10 + = f.text_area :domain_whitelist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8 + .help-block ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com + .form-group + = f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'control-label col-sm-2' + .col-sm-10 + .checkbox + = f.label :domain_blacklist_enabled do + = f.check_box :domain_blacklist_enabled + Enable domain blacklist for sign ups + .form-group + .col-sm-offset-2.col-sm-10 + .radio + = label_tag :blacklist_type_file do + = radio_button_tag :blacklist_type, :file + .option-title + Upload blacklist file + .radio + = label_tag :blacklist_type_raw do + = radio_button_tag :blacklist_type, :raw, @application_setting.domain_blacklist.present? || @application_setting.domain_blacklist.blank? + .option-title + Enter blacklist manually + .form-group.blacklist-file + = f.label :domain_blacklist_file, 'Blacklist file', class: 'control-label col-sm-2' + .col-sm-10 + = f.file_field :domain_blacklist_file, class: 'form-control', accept: '.txt,.conf' + .help-block Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines or commas for multiple entries. + .form-group.blacklist-raw + = f.label :domain_blacklist, 'Blacklisted domains for sign-ups', class: 'control-label col-sm-2' + .col-sm-10 + = f.text_area :domain_blacklist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8 + .help-block Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com + + .form-group + = f.label :after_sign_up_text, class: 'control-label col-sm-2' + .col-sm-10 + = f.text_area :after_sign_up_text, class: 'form-control', rows: 4 + .help-block Markdown enabled + + %fieldset + %legend Sign-in Restrictions + .form-group .col-sm-offset-2.col-sm-10 .checkbox = f.label :signin_enabled do @@ -148,11 +191,6 @@ = f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0' .help-block Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication .form-group - = f.label :restricted_signup_domains, 'Restricted domains for sign-ups', class: 'control-label col-sm-2' - .col-sm-10 - = f.text_area :restricted_signup_domains_raw, placeholder: 'domain.com', class: 'form-control' - .help-block Only users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com - .form-group = f.label :home_page_url, 'Home page URL', class: 'control-label col-sm-2' .col-sm-10 = f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'home_help_block' @@ -168,11 +206,6 @@ = f.text_area :sign_in_text, class: 'form-control', rows: 4 .help-block Markdown enabled .form-group - = f.label :after_sign_up_text, class: 'control-label col-sm-2' - .col-sm-10 - = f.text_area :after_sign_up_text, class: 'form-control', rows: 4 - .help-block Markdown enabled - .form-group = f.label :help_page_text, class: 'control-label col-sm-2' .col-sm-10 = f.text_area :help_page_text, class: 'form-control', rows: 4 @@ -352,4 +385,4 @@ .form-actions - = f.submit 'Save', class: 'btn btn-save' + = f.submit 'Save', class: 'btn btn-save'
\ No newline at end of file |