diff options
author | Rémy Coutable <remy@rymai.me> | 2016-02-09 15:51:06 +0100 |
---|---|---|
committer | Rémy Coutable <remy@rymai.me> | 2016-02-09 18:15:35 +0100 |
commit | b34963bc125d11af7b9c993d1233258f084e580d (patch) | |
tree | a428e07d6da540a852a1f182073e16ee317546da | |
parent | a52c5778bb9d95097cc965539731a2ef846c3ff0 (diff) | |
download | gitlab-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-- | CHANGELOG | 1 | ||||
-rw-r--r-- | app/models/application_setting.rb | 4 | ||||
-rw-r--r-- | app/models/email.rb | 2 | ||||
-rw-r--r-- | app/models/member.rb | 4 | ||||
-rw-r--r-- | app/models/user.rb | 7 | ||||
-rw-r--r-- | app/validators/email_validator.rb | 18 | ||||
-rw-r--r-- | spec/models/application_setting_spec.rb | 4 | ||||
-rw-r--r-- | spec/models/email_spec.rb | 22 | ||||
-rw-r--r-- | spec/models/member_spec.rb | 6 | ||||
-rw-r--r-- | spec/models/user_spec.rb | 36 | ||||
-rw-r--r-- | spec/support/email_format_shared_examples.rb | 44 |
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 |