Commit 0df317f7 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'restrict-signups-to-domains' into 'master'

Add application setting to restrict user signups to e-mail domains

This feature was requested long ago:

http://feedback.gitlab.com/forums/176466-general/suggestions/4118466-ability-to-register-only-from-ceratain-domains

This MR is based off !253 but changed to use application settings and use wildcard strings
to give more flexibility in pattern matching. Regexps seemed overkill and prone to mistakes.

Also note that validation is ONLY done on creation to prevent breaking existing users who do not have a whitelisted domain. However, this allows a user to sign-up and change his/her email to a non-whitelisted domain.

Screenshots:

![image](https://gitlab.com/gitlab-org/gitlab-ce/uploads/b312046aae03971f37f4247382971fc6/image.png)

![image](https://gitlab.com/gitlab-org/gitlab-ce/uploads/94bdf3ffaf37c2e8324eff83308f81f0/image.png)

See merge request !598
parents 0d0c539c eb4f1eb5
......@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 7.11.0 (unreleased)
- Make Reply-To config apply to change e-mail confirmation and other Devise notifications (Stan Hu)
- Add application setting to restrict user signups to e-mail domains (Stan Hu)
- Don't allow a merge request to be merged when its title starts with "WIP".
- Add a page title to every page.
- Get Gitorious importer to work again.
......
......@@ -41,7 +41,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:max_attachment_size,
:default_project_visibility,
:default_snippet_visibility,
restricted_visibility_levels: []
:restricted_signup_domains_raw,
restricted_visibility_levels: [],
)
end
end
......@@ -18,11 +18,13 @@
# restricted_visibility_levels :text
# max_attachment_size :integer default(10)
# default_project_visibility :integer
# default_snippet_visibility :integer
# restricted_signup_domains :text
#
class ApplicationSetting < ActiveRecord::Base
serialize :restricted_visibility_levels
serialize :restricted_signup_domains, Array
attr_accessor :restricted_signup_domains_raw
validates :home_page_url,
allow_blank: true,
......@@ -55,11 +57,29 @@ class ApplicationSetting < ActiveRecord::Base
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_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']
)
end
def home_page_url_column_exist
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?
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? }
end
end
......@@ -142,6 +142,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 :sanitize_attrs
before_validation :set_notification_email, if: ->(user) { user.email_changed? }
before_validation :set_public_email, if: ->(user) { user.public_email_changed? }
......@@ -611,4 +612,27 @@ class User < ActiveRecord::Base
select(:project_id).
uniq.map(&:project_id)
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
end
......@@ -72,6 +72,11 @@
= f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :max_attachment_size, class: 'form-control'
.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 Ex: domain.com, *.domain.com. Wildcards allowed. Use separate lines for multiple entries.
.form-actions
= f.submit 'Save', class: 'btn btn-primary'
......@@ -132,6 +132,7 @@ Settings.gitlab.default_projects_features['wiki'] = true if Settings.g
Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil?
Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
Settings.gitlab['repository_downloads_path'] = File.absolute_path(Settings.gitlab['repository_downloads_path'] || 'tmp/repositories', Rails.root)
Settings.gitlab['restricted_signup_domains'] ||= []
#
# Gravatar
......
class AddRestrictedSignupDomainsToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :restricted_signup_domains, :text
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150429002313) do
ActiveRecord::Schema.define(version: 20150502064022) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -31,6 +31,7 @@ ActiveRecord::Schema.define(version: 20150429002313) do
t.integer "max_attachment_size", default: 10, null: false
t.integer "default_project_visibility"
t.integer "default_snippet_visibility"
t.text "restricted_signup_domains"
end
create_table "broadcast_messages", force: true do |t|
......
......@@ -21,4 +21,28 @@ require 'spec_helper'
describe ApplicationSetting, models: true do
it { expect(ApplicationSetting.create_from_defaults).to be_valid }
context 'restricted signup domains' do
let(:setting) { ApplicationSetting.create_from_defaults }
it 'set single domain' do
setting.restricted_signup_domains_raw = 'example.com'
expect(setting.restricted_signup_domains).to eq(['example.com'])
end
it 'set multiple domains with spaces' do
setting.restricted_signup_domains_raw = 'example.com *.example.com'
expect(setting.restricted_signup_domains).to eq(['example.com', '*.example.com'])
end
it 'set multiple domains with newlines and a space' do
setting.restricted_signup_domains_raw = "example.com\n *.example.com"
expect(setting.restricted_signup_domains).to eq(['example.com', '*.example.com'])
end
it 'set multiple domains with commas' do
setting.restricted_signup_domains_raw = "example.com, *.example.com"
expect(setting.restricted_signup_domains).to eq(['example.com', '*.example.com'])
end
end
end
......@@ -54,6 +54,8 @@
require 'spec_helper'
describe User do
include Gitlab::CurrentSettings
describe "Associations" do
it { is_expected.to have_one(:namespace) }
it { is_expected.to have_many(:snippets).class_name('Snippet').dependent(:destroy) }
......@@ -112,6 +114,51 @@ describe User do
user = build(:user, email: "lol!'+=?><#$%^&*()@gmail.com")
expect(user).to be_invalid
end
context 'when no signup domains listed' do
before { allow(current_application_settings).to receive(:restricted_signup_domains).and_return([]) }
it 'accepts any email' do
user = build(:user, email: "info@example.com")
expect(user).to be_valid
end
end
context 'when a signup domain is listed and subdomains are allowed' do
before { allow(current_application_settings).to receive(:restricted_signup_domains).and_return(['example.com', '*.example.com']) }
it 'accepts info@example.com' do
user = build(:user, email: "info@example.com")
expect(user).to be_valid
end
it 'accepts info@test.example.com' do
user = build(:user, email: "info@test.example.com")
expect(user).to be_valid
end
it 'rejects example@test.com' do
user = build(:user, email: "example@test.com")
expect(user).to be_invalid
end
end
context 'when a signup domain is listed and subdomains are not allowed' do
before { allow(current_application_settings).to receive(:restricted_signup_domains).and_return(['example.com']) }
it 'accepts info@example.com' do
user = build(:user, email: "info@example.com")
expect(user).to be_valid
end
it 'rejects info@test.example.com' do
user = build(:user, email: "info@test.example.com")
expect(user).to be_invalid
end
it 'rejects example@test.com' do
user = build(:user, email: "example@test.com")
expect(user).to be_invalid
end
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment