summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRémy Coutable <remy@rymai.me>2016-02-09 15:51:06 +0100
committerRémy Coutable <remy@rymai.me>2016-02-09 18:15:35 +0100
commitb34963bc125d11af7b9c993d1233258f084e580d (patch)
treea428e07d6da540a852a1f182073e16ee317546da
parenta52c5778bb9d95097cc965539731a2ef846c3ff0 (diff)
downloadgitlab-ce-b34963bc125d11af7b9c993d1233258f084e580d.tar.gz
Validate email addresses using Devise.email_regexp
Also: - Get rid of legacy :strict_mode - Get rid of custom :email validator - Add some shared examples to spec emails validation
-rw-r--r--CHANGELOG1
-rw-r--r--app/models/application_setting.rb4
-rw-r--r--app/models/email.rb2
-rw-r--r--app/models/member.rb4
-rw-r--r--app/models/user.rb7
-rw-r--r--app/validators/email_validator.rb18
-rw-r--r--spec/models/application_setting_spec.rb4
-rw-r--r--spec/models/email_spec.rb22
-rw-r--r--spec/models/member_spec.rb6
-rw-r--r--spec/models/user_spec.rb36
-rw-r--r--spec/support/email_format_shared_examples.rb44
11 files changed, 90 insertions, 58 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 4694421f040..683f415432f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -19,6 +19,7 @@ v 8.5.0 (unreleased)
- Fix label links for a merge request pointing to issues list
- Don't vendor minified JS
- Increase project import timeout to 15 minutes
+ - Be more permissive with email address validation: it only has to contain a single '@'
- Display 404 error on group not found
- Track project import failure
- Support Two-factor Authentication for LDAP users
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index fa48fe5b9e4..ac6653f5d50 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -71,8 +71,8 @@ class ApplicationSetting < ActiveRecord::Base
url: true
validates :admin_notification_email,
- allow_blank: true,
- email: true
+ format: { with: Devise.email_regexp },
+ allow_blank: true
validates :two_factor_grace_period,
numericality: { greater_than_or_equal_to: 0 }
diff --git a/app/models/email.rb b/app/models/email.rb
index 935705e2ed4..daa259ee2d2 100644
--- a/app/models/email.rb
+++ b/app/models/email.rb
@@ -15,7 +15,7 @@ class Email < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
- validates :email, presence: true, email: { strict_mode: true }, uniqueness: true
+ validates :email, presence: true, uniqueness: true, format: { with: Devise.email_regexp }
validate :unique_email, if: ->(email) { email.email_changed? }
before_validation :cleanup_email
diff --git a/app/models/member.rb b/app/models/member.rb
index 34efcd0088d..0b6785ddbc5 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -38,8 +38,8 @@ class Member < ActiveRecord::Base
presence: {
if: :invite?
},
- email: {
- strict_mode: true,
+ format: {
+ with: Devise.email_regexp,
allow_nil: true
},
uniqueness: {
diff --git a/app/models/user.rb b/app/models/user.rb
index 234c1cd89f9..edda0ebef86 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -146,11 +146,8 @@ class User < ActiveRecord::Base
# Validations
#
validates :name, presence: true
- # Note that a 'uniqueness' and presence check is provided by devise :validatable for email. We do not need to
- # duplicate that here as the validation framework will have duplicate errors in the event of a failure.
- validates :email, presence: true, email: { strict_mode: true }
- validates :notification_email, presence: true, email: { strict_mode: true }
- validates :public_email, presence: true, email: { strict_mode: true }, allow_blank: true, uniqueness: true
+ validates :notification_email, presence: true, format: { with: Devise.email_regexp }
+ validates :public_email, presence: true, uniqueness: true, format: { with: Devise.email_regexp }, allow_blank: true
validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
validates :username,
diff --git a/app/validators/email_validator.rb b/app/validators/email_validator.rb
deleted file mode 100644
index b35af100803..00000000000
--- a/app/validators/email_validator.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# EmailValidator
-#
-# Based on https://github.com/balexand/email_validator
-#
-# Extended to use only strict mode with following allowed characters:
-# ' - apostrophe
-#
-# See http://www.remote.org/jochen/mail/info/chars.html
-#
-class EmailValidator < ActiveModel::EachValidator
- PATTERN = /\A\s*([-a-z0-9+._']{1,64})@((?:[-a-z0-9]+\.)+[a-z]{2,})\s*\z/i.freeze
-
- def validate_each(record, attribute, value)
- unless value =~ PATTERN
- record.errors.add(attribute, options[:message] || :invalid)
- end
- end
-end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 161a32c51e6..b1764d7ac09 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -74,6 +74,10 @@ describe ApplicationSetting, models: true do
.only_integer
.is_greater_than(0)
end
+
+ it_behaves_like 'an object with email-formated attributes', :admin_notification_email do
+ subject { setting }
+ end
end
context 'restricted signup domains' do
diff --git a/spec/models/email_spec.rb b/spec/models/email_spec.rb
new file mode 100644
index 00000000000..a20a6149649
--- /dev/null
+++ b/spec/models/email_spec.rb
@@ -0,0 +1,22 @@
+# == Schema Information
+#
+# Table name: emails
+#
+# id :integer not null, primary key
+# user_id :integer not null
+# email :string(255) not null
+# created_at :datetime
+# updated_at :datetime
+#
+
+require 'spec_helper'
+
+describe Email, models: true do
+
+ describe 'validations' do
+ it_behaves_like 'an object with email-formated attributes', :email do
+ subject { build(:email) }
+ end
+ end
+
+end
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 2aedca20df2..2d8f1cc1ad3 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -31,6 +31,10 @@ describe Member, models: true do
it { is_expected.to validate_presence_of(:source) }
it { is_expected.to validate_inclusion_of(:access_level).in_array(Gitlab::Access.values) }
+ it_behaves_like 'an object with email-formated attributes', :invite_email do
+ subject { build(:project_member) }
+ end
+
context "when an invite email is provided" do
let(:member) { build(:project_member, invite_email: "user@example.com", user: nil) }
@@ -159,7 +163,7 @@ describe Member, models: true do
describe "#generate_invite_token" do
let!(:member) { create(:project_member, invite_email: "user@example.com", user: nil) }
-
+
it "sets the invite token" do
expect { member.generate_invite_token }.to change { member.invite_token}
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index cee051f5732..47ce409fe4b 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -119,37 +119,15 @@ describe User, models: true do
it { is_expected.to validate_length_of(:bio).is_within(0..255) }
- describe 'email' do
- 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 "accepts o'reilly@example.com" do
- user = build(:user, email: "o'reilly@example.com")
- expect(user).to be_valid
- end
-
- it 'rejects test@test@example.com' do
- user = build(:user, email: 'test@test@example.com')
- expect(user).to be_invalid
- end
-
- it 'rejects mailto:test@example.com' do
- user = build(:user, email: 'mailto:test@example.com')
- expect(user).to be_invalid
- end
+ it_behaves_like 'an object with email-formated attributes', :email do
+ subject { build(:user) }
+ end
- it "rejects lol!'+=?><#$%^&*()@gmail.com" do
- user = build(:user, email: "lol!'+=?><#$%^&*()@gmail.com")
- expect(user).to be_invalid
- end
+ it_behaves_like 'an object with email-formated attributes', :public_email, :notification_email do
+ subject { build(:user).tap { |user| user.emails << build(:email, email: email_value) } }
+ end
+ describe 'email' do
context 'when no signup domains listed' do
before { allow(current_application_settings).to receive(:restricted_signup_domains).and_return([]) }
it 'accepts any email' do
diff --git a/spec/support/email_format_shared_examples.rb b/spec/support/email_format_shared_examples.rb
new file mode 100644
index 00000000000..b924a208e71
--- /dev/null
+++ b/spec/support/email_format_shared_examples.rb
@@ -0,0 +1,44 @@
+# Specifications for behavior common to all objects with an email attribute.
+# Takes a list of email-format attributes and requires:
+# - subject { "the object with a attribute= setter" }
+# Note: You have access to `email_value` which is the email address value
+# being currently tested).
+
+shared_examples 'an object with email-formated attributes' do |*attributes|
+ attributes.each do |attribute|
+ describe "specifically its :#{attribute} attribute" do
+ %w[
+ info@example.com
+ info+test@example.com
+ o'reilly@example.com
+ mailto:test@example.com
+ lol!'+=?><#$%^&*()@gmail.com
+ ].each do |valid_email|
+ context "with a value of '#{valid_email}'" do
+ let(:email_value) { valid_email }
+
+ it 'is valid' do
+ subject.send("#{attribute}=", valid_email)
+
+ expect(subject).to be_valid
+ end
+ end
+ end
+
+ %w[
+ foobar
+ test@test@example.com
+ ].each do |invalid_email|
+ context "with a value of '#{invalid_email}'" do
+ let(:email_value) { invalid_email }
+
+ it 'is invalid' do
+ subject.send("#{attribute}=", invalid_email)
+
+ expect(subject).to be_invalid
+ end
+ end
+ end
+ end
+ end
+end