Skip to content

Commit

Permalink
feat: Deprecate blacklist and whitelist naming
Browse files Browse the repository at this point in the history
* change: deprecate blacklist and whitelist naming, #205

* change: per code review, #205

Co-authored-by: Burkhard Vogel-Kreykenbohm <[email protected]>

* doc: update readme with upgrading instructions, #205

---------

Co-authored-by: Burkhard Vogel-Kreykenbohm <[email protected]>
  • Loading branch information
ianbayne and bvogel authored Aug 31, 2024
1 parent 21f88fa commit 9df0bf8
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 52 deletions.
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,20 @@ To validate that the domain is not a disposable email (checks domain only, does
validates :email, 'valid_email_2/email': { disposable_domain: true }
```

To validate that the domain is not a disposable email or a disposable email (checks domain and MX server) but whitelisted (under config/whitelisted_email_domains.yml):
To validate that the domain is not a disposable email or a disposable email (checks domain and MX server) but allow-listed (under config/allow_listed_email_domains.yml):
```ruby
validates :email, 'valid_email_2/email': { disposable_with_whitelist: true }
validates :email, 'valid_email_2/email': { disposable_with_allow_list: true }
```

To validate that the domain is not a disposable email or a disposable email (checks domain only, does not check MX server) but whitelisted (under config/whitelisted_email_domains.yml):
To validate that the domain is not a disposable email or a disposable email (checks domain only, does not check MX server) but allow-listed (under config/allow_listed_email_domains.yml):

```ruby
validates :email, 'valid_email_2/email': { disposable_domain_with_whitelist: true }
validates :email, 'valid_email_2/email': { disposable_domain_with_allow_list: true }
```

To validate that the domain is not blacklisted (under config/blacklisted_email_domains.yml):
To validate that the domain is not on the deny list (under config/deny_list_email_domains.yml):
```ruby
validates :email, 'valid_email_2/email': { blacklist: true }
validates :email, 'valid_email_2/email': { deny_list: true }
```

To validate that email is not subaddressed:
Expand Down Expand Up @@ -147,6 +147,15 @@ end

This gem is tested against currently supported Ruby and Rails versions. For an up to date list, check the build matrix in the [workflow](.github/workflows/ci.yaml).

## Upgrading to v5.3.0

In version v5.3.0 the config directory files were renamed as follows:

`config/blacklisted_email_domains.yml` -> `config/deny_listed_email_domains.yml`
`config/whitelisted_email_domains.yml` -> `config/allow_listed_email_domains.yml`

You won't need to make any changes yourself if you're installing this version for the first time. For individuals updating from earlier versions, make sure to update the file namings as per the above. In future versions this will be a breaking change.

## Upgrading to v3.0.0

In version v3.0.0 I decided to move __and__ rename the config files from the
Expand Down
1 change: 0 additions & 1 deletion config/blacklisted_email_domains.yml

This file was deleted.

1 change: 1 addition & 0 deletions config/deny_listed_email_domains.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- deny-listed-test.com
12 changes: 12 additions & 0 deletions lib/helpers/deprecation_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module DeprecationHelper
def deprecate_method(old_method, new_method)
define_method(old_method) do |*args, &block|
warn "Warning: `#{old_method}` is deprecated; use `#{new_method}` instead."
send(new_method, *args, &block)
end
end

def deprecation_message(old_name, new_name)
warn "Warning: `#{old_name}` is deprecated; use `#{new_name}` instead."
end
end
18 changes: 14 additions & 4 deletions lib/valid_email2.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
# frozen_string_literal: true

require "valid_email2/email_validator"
require_relative "./helpers/deprecation_helper"

module ValidEmail2

BLACKLIST_FILE = "config/blacklisted_email_domains.yml"
DENY_LIST_FILE = "config/deny_listed_email_domains.yml"
WHITELIST_FILE = "config/whitelisted_email_domains.yml"
ALLOW_LIST_FILE = "config/allow_listed_email_domains.yml"
DISPOSABLE_FILE = File.expand_path('../config/disposable_email_domains.txt', __dir__)

class << self
extend DeprecationHelper

def disposable_emails
@disposable_emails ||= load_file(DISPOSABLE_FILE)
end

def blacklist
@blacklist ||= load_if_exists(BLACKLIST_FILE)
def deny_list
@deny_list ||= load_if_exists(DENY_LIST_FILE || BLACKLIST_FILE)
end
alias_method :blacklist, :deny_list
deprecate_method :blacklist, :deny_list

def whitelist
@whitelist ||= load_if_exists(WHITELIST_FILE)
def allow_list
@allow_list ||= load_if_exists(ALLOW_LIST_FILE || WHITELIST_FILE)
end
alias_method :whitelist, :allow_list
deprecate_method :whitelist, :allow_list

private

Expand Down
15 changes: 11 additions & 4 deletions lib/valid_email2/address.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
require "valid_email2"
require "resolv"
require "mail"
require_relative "../helpers/deprecation_helper"

module ValidEmail2
class Address
extend DeprecationHelper

attr_accessor :address

PROHIBITED_DOMAIN_CHARACTERS_REGEX = /[+!_\/\s'`]/
Expand Down Expand Up @@ -84,13 +87,17 @@ def disposable_mx_server?
valid? && mx_server_is_in?(ValidEmail2.disposable_emails)
end

def whitelisted?
domain_is_in?(ValidEmail2.whitelist)
def allow_listed?
domain_is_in?(ValidEmail2.allow_list)
end
alias_method :whitelisted?, :allow_listed?
deprecate_method :whitelisted?, :allow_listed?

def blacklisted?
valid? && domain_is_in?(ValidEmail2.blacklist)
def deny_listed?
valid? && domain_is_in?(ValidEmail2.deny_list)
end
alias_method :blacklisted?, :deny_listed?
deprecate_method :blacklisted?, :deny_listed?

def valid_mx?
return false unless valid?
Expand Down
21 changes: 18 additions & 3 deletions lib/valid_email2/email_validator.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
require "valid_email2/address"
require "active_model"
require "active_model/validations"
require_relative "../helpers/deprecation_helper"

module ValidEmail2
class EmailValidator < ActiveModel::EachValidator
include DeprecationHelper

def default_options
{ disposable: false, mx: false, strict_mx: false, disallow_subaddressing: false, multiple: false, dns_timeout: 5, dns_nameserver: nil }
end
Expand Down Expand Up @@ -33,15 +36,27 @@ def validate_each(record, attribute, value)
end

if options[:disposable_with_whitelist]
error(record, attribute) && return if addresses.any? { |address| address.disposable? && !address.whitelisted? }
deprecation_message(:disposable_with_whitelist, :disposable_with_allow_list)
end

if options[:disposable_with_allow_list] || options[:disposable_with_whitelist]
error(record, attribute) && return if addresses.any? { |address| address.disposable? && !address.allow_listed? }
end

if options[:disposable_domain_with_whitelist]
error(record, attribute) && return if addresses.any? { |address| address.disposable_domain? && !address.whitelisted? }
deprecation_message(:disposable_domain_with_whitelist, :disposable_domain_with_allow_list)
end

if options[:disposable_domain_with_allow_list] || options[:disposable_domain_with_whitelist]
error(record, attribute) && return if addresses.any? { |address| address.disposable_domain? && !address.allow_listed? }
end

if options[:blacklist]
error(record, attribute) && return if addresses.any?(&:blacklisted?)
deprecation_message(:blacklist, :deny_list)
end

if options[:deny_list] || options[:blacklist]
error(record, attribute) && return if addresses.any?(&:deny_listed?)
end

if options[:mx]
Expand Down
64 changes: 32 additions & 32 deletions spec/valid_email2_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ class TestUserDisallowDisposableDomain < TestModel
validates :email, 'valid_email_2/email': { disposable_domain: true }
end

class TestUserDisallowDisposableWithWhitelist < TestModel
validates :email, 'valid_email_2/email': { disposable_with_whitelist: true }
class TestUserDisallowDisposableWithAllowList < TestModel
validates :email, 'valid_email_2/email': { disposable_with_allow_list: true }
end

class TestUserDisallowDisposableDomainWithWhitelist < TestModel
validates :email, 'valid_email_2/email': { disposable_domain_with_whitelist: true }
class TestUserDisallowDisposableDomainWithAllowList < TestModel
validates :email, 'valid_email_2/email': { disposable_domain_with_allow_list: true }
end

class TestUserDisallowBlacklisted < TestModel
validates :email, 'valid_email_2/email': { blacklist: true }
class TestUserDisallowDenyListed < TestModel
validates :email, 'valid_email_2/email': { deny_list: true }
end

class TestUserMessage < TestModel
Expand Down Expand Up @@ -208,58 +208,58 @@ class TestUserMultiple < TestModel
end
end

describe "with whitelisted emails" do
let(:whitelist_domain) { disposable_domain }
let(:whitelist_file_path) { "config/whitelisted_email_domains.yml" }
describe "with allow-listed emails" do
let(:allow_list_domain) { disposable_domain }
let(:allow_list_file_path) { "config/allow_listed_email_domains.yml" }

# Some of the specs below need to explictly set the whitelist var or it
# Some of the specs below need to explictly set the allow list var or it
# may be cached to an empty set
def set_whitelist
def set_allow_list
ValidEmail2.instance_variable_set(
:@whitelist,
ValidEmail2.send(:load_if_exists, ValidEmail2::WHITELIST_FILE)
:@allow_list,
ValidEmail2.send(:load_if_exists, ValidEmail2::ALLOW_LIST_FILE)
)
end

after do
FileUtils.rm(whitelist_file_path, force: true)
set_whitelist
FileUtils.rm(allow_list_file_path, force: true)
set_allow_list
end

it "is invalid if the domain is disposable and not in the whitelist" do
user = TestUserDisallowDisposableWithWhitelist.new(email: "foo@#{whitelist_domain}")
it "is invalid if the domain is disposable and not in the allow list" do
user = TestUserDisallowDisposableWithAllowList.new(email: "foo@#{allow_list_domain}")
expect(user.valid?).to be_falsey
end

it "is valid if the domain is disposable but in the whitelist" do
File.open(whitelist_file_path, "w") { |f| f.write [whitelist_domain].to_yaml }
set_whitelist
user = TestUserDisallowDisposableWithWhitelist.new(email: "foo@#{whitelist_domain}")
it "is valid if the domain is disposable but in the allow list" do
File.open(allow_list_file_path, "w") { |f| f.write [allow_list_domain].to_yaml }
set_allow_list
user = TestUserDisallowDisposableWithAllowList.new(email: "foo@#{allow_list_domain}")
expect(user.valid?).to be_truthy
end

it "is invalid if the domain is a disposable_domain and not in the whitelist" do
user = TestUserDisallowDisposableDomainWithWhitelist.new(email: "foo@#{whitelist_domain}")
it "is invalid if the domain is a disposable_domain and not in the allow list" do
user = TestUserDisallowDisposableDomainWithAllowList.new(email: "foo@#{allow_list_domain}")
expect(user.valid?).to be_falsey
end

it "is valid if the domain is a disposable_domain but in the whitelist" do
File.open(whitelist_file_path, "w") { |f| f.write [whitelist_domain].to_yaml }
set_whitelist
user = TestUserDisallowDisposableDomainWithWhitelist.new(email: "foo@#{whitelist_domain}")
it "is valid if the domain is a disposable_domain but in the allow list" do
File.open(allow_list_file_path, "w") { |f| f.write [allow_list_domain].to_yaml }
set_allow_list
user = TestUserDisallowDisposableDomainWithAllowList.new(email: "foo@#{allow_list_domain}")
expect(user.valid?).to be_truthy
end
end
end

describe "with blacklist validation" do
it "is valid if the domain is not blacklisted" do
user = TestUserDisallowBlacklisted.new(email: "[email protected]")
describe "with deny list validation" do
it "is valid if the domain is not deny-listed" do
user = TestUserDisallowDenyListed.new(email: "[email protected]")
expect(user.valid?).to be_truthy
end

it "is invalid if the domain is blacklisted" do
user = TestUserDisallowBlacklisted.new(email: "foo@blacklisted-test.com")
it "is invalid if the domain is deny-listed" do
user = TestUserDisallowDenyListed.new(email: "foo@deny-listed-test.com")
expect(user.valid?).to be_falsey
end
end
Expand Down
4 changes: 2 additions & 2 deletions valid_email2.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
spec.version = ValidEmail2::VERSION
spec.authors = ["Micke Lisinge"]
spec.email = ["[email protected]"]
spec.description = %q{ActiveModel validation for email. Including MX lookup and disposable email blacklist}
spec.summary = %q{ActiveModel validation for email. Including MX lookup and disposable email blacklist}
spec.description = %q{ActiveModel validation for email. Including MX lookup and disposable email deny list}
spec.summary = %q{ActiveModel validation for email. Including MX lookup and disposable email deny list}
spec.homepage = "https:/micke/valid_email2"
spec.license = "MIT"

Expand Down

0 comments on commit 9df0bf8

Please sign in to comment.