diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5ddad42 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +# Changelog \ No newline at end of file diff --git a/README.md b/README.md index b604411..73c99bf 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,154 @@ # laravel-active-email -Disposable email address validator rule for Laravel apps + +## Table of Contents + +- [Overview](#overview) + - [Key Features](#key-features) +- [Installation](#installation) + - [Requirements](#requirements) + - [Install the Package](#install-the-package) + - [Publish the Config](#publish-the-config) +- [Usage](#usage) + - [Validator Approach](#validator-approach) + - [Class Approach](#class-approach) + - [Customization](#customization) + - [Strict Mode](#strict-mode) + - [Black List](#black-list) + - [Grey List](#grey-list) + - [White List](#white-list) +- [Testing](#testing) +- [To do](#to-do) +- [Security](#security) +- [Contribution](#contribution) +- [Changelog](#changelog) +- [Upgrading](#upgrading) +- [License](#license) + +## Overview + +This package provides a library of disposable domains and adds a validator to Laravel apps to check that a given email address isn't coming from a disposable email service such as `Mailinator`, `Guerillamail`, `Fastmail` considering all their possible wildcards. + +### Key Features + +- You can add your own preferred domains to the [black list](#black-list). +- You can [white list](#white-list) a domain to bye pass the blacklist. This can be useful in development environment. +- With [strict mode](#strict-mode), you can control the strictness of the validator, thereby allowing or preventing domains that are not necessarily disposable, but have been classified as disposable. +- Case-aware. +- Wildcard-aware. + +## Installation + + +### Requirements + +The package has been developed and tested to work with the following minimum requirements: + +- PHP 8.x +- Laravel 10.x, 11.x + +### Install the Package + +You can install the package via Composer. The service provider is discovered automatically. + +```bash +composer require veeqtoh/laravel-active-email +``` + +### Publish the Config + +You can then publish the package's config file and update it as you'd prefer: +```bash +php artisan vendor:publish --provider="Veeqtoh\ActiveEmail\Providers\ActiveEmailProvider" +``` + +## Usage + +### Validator Approach + +Add the `notblacklisted` validator to your email validation rules array (or string) to ensure that the domain for a given email address is not blacklisted. I'd recommend you add it after the email validator to make sure a valid email is passed through: +```php +'emailField' => 'email|notblacklisted', +``` + +or + +```php +'emailField' => ['email', 'notblacklisted'], +``` + +### Class Approach + +Instantiate the `NotBlackListedEmail` Class as part of your email validation rules array to ensure that the domain for a given email address is not blacklisted. Again, I'd recommend you add it after the email validator to make sure a valid email is passed through: + +```php +use Veeqtoh\ActiveEmail\Rules\NotBlackListedEmail; + +'emailField' => ['email', new NotBlackListedEmail()], +``` + +### Customization + +The package is highly customizable from the config file with the following features: + +#### Strict Mode + +This value determines the strictness level of this feature. when set to `true`, domains in the [grey list](#grey-list) are also blacklisted. + +It is turned on by default, but can be set in your .env file as follows: + +```php +DISPOSABLE_EMAIL_STRICT_MODE=true, +``` + +#### Black List + +This is a list of base domains with or without the TLD that are blacklisted by default. Add a domain to this list to blacklist it. + +#### Grey List + +This is a list of base domains with or without the TLD that aren't blacklisted by default except when in strict mode. Add a domain to this list to whitelist it when the feature is not set to strict mode. Ensure that the domain is not on the [black list](#black-list). + +#### White List + +This is a list of base domains with or without the TLD that are blacklisted by default but you want them to be bye passed. + +## To Do + +There's always something that can be done to improve this package. I'd keep updating this list as I think of them. + +- Crawl the web to grab an updated list of disposable domains. +- Maybe setup a schedule for it.. + +## Testing + +To run the package's unit tests, run the following command: + +``` bash +vendor/bin/pest +``` + +## Security + +If you find any security related issues, please contact me directly at [victorjohnukam@gmail.com](mailto:victorjohnukam@gmail.com) to report it. + +## Contribution + +If you wish to make any changes or improvements to the package, feel free to make a pull request. + +Note: A contribution guide will be added soon. + +## Changelog + +Check the [CHANGELOG](CHANGELOG.md) to get more information about the latest changes. + +## Upgrading + +Check the [UPGRADE](UPGRADE.md) guide to get more information on how to update this library to newer versions. + +## License + +The MIT License (MIT). Please see [License File](LICENSE) for more information. + +## Support Me + +If you've found this package useful, please consider sponsoring this project. It will encourage me to keep maintaining it. \ No newline at end of file diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 0000000..275c547 --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1 @@ +# Upgrade \ No newline at end of file diff --git a/config/active-email.php b/config/active-email.php index efceb32..46d3480 100644 --- a/config/active-email.php +++ b/config/active-email.php @@ -15,32 +15,46 @@ /* |-------------------------------------------------------------------------- - | Blacklist + | Black List |-------------------------------------------------------------------------- | - | This is a list of base domains without the TLD that are blacklisted by - | default. Add a domain to this list to blacklist it. + | This is a list of base domains with or without the TLD that are + | blacklisted by default. Add a domain to this list to blacklist it. | */ 'blacklist' => [ - // + // ], /* |-------------------------------------------------------------------------- - | Greylist + | Grey List |-------------------------------------------------------------------------- | - | This is a list of base domains without the TLD that aren't blacklisted by - | default except when in strict mode. Add a domain to this list to whitelist - | it when the feature is not set to strict mode. Ensure that the domain is not - | on the blacklist above. + | This is a list of base domains with or without the TLD that aren't + | blacklisted by default except when in strict mode. Add a domain to this + | list to whitelist it when the feature is not set to strict mode. + | Ensure that the domain is not on the blacklist above. | */ 'greylist' => [ - // + // + ], + + /* + |-------------------------------------------------------------------------- + | White List + |-------------------------------------------------------------------------- + | + | This is a list of base domains with or without the TLD that are + | blacklisted by default but you want them to be bye passed. + | + */ + + 'whitelist' => [ + // ], ]; diff --git a/src/DisposableEmail.php b/src/DisposableEmail.php index 47478ce..ae160d4 100644 --- a/src/DisposableEmail.php +++ b/src/DisposableEmail.php @@ -12,8 +12,10 @@ */ class DisposableEmail { - /** + /** * Retrieve a list of base domains without the TLDs that are blacklisted by default. + * + * @return array */ public function getBlacklist() : array { diff --git a/src/Providers/ActiveEmailProvider.php b/src/Providers/ActiveEmailProvider.php index 69fce1c..3ab739f 100644 --- a/src/Providers/ActiveEmailProvider.php +++ b/src/Providers/ActiveEmailProvider.php @@ -37,11 +37,10 @@ public function boot() : void __DIR__ . '/../../config/active-email.php' => config_path('active-email.php'), ], 'config'); - // Register the custom validation rule + // Register the custom validation rule. Validator::extend('notblacklisted', function ($attribute, $value, $parameters, $validator) { $rule = new NotBlacklistedEmail(); - // Create a Closure for the fail method $fail = function($message) use ($attribute, $validator) { $validator->errors()->add($attribute, $message); }; diff --git a/src/Rules/NotBlacklistedEmail.php b/src/Rules/NotBlacklistedEmail.php index fa414a5..07c29fa 100644 --- a/src/Rules/NotBlacklistedEmail.php +++ b/src/Rules/NotBlacklistedEmail.php @@ -26,12 +26,12 @@ public function __construct() /** * Run the validation rule. - * + * * @param string $attribute The name of the attribute being validated. * @param mixed $value The value of the attribute being validated. - * @param \Closure(mixed): \Illuminate\Translation\PotentiallyTranslatedString $fail + * @param \Closure(mixed): \Illuminate\Translation\PotentiallyTranslatedString $fail * The callback that should be used to report validation failures. - * + * * @return void */ public function validate(string $attribute, mixed $value, Closure $fail) : void @@ -41,11 +41,17 @@ public function validate(string $attribute, mixed $value, Closure $fail) : void $domain = $this->getDomainName('@', $value, 1); $domainName = $this->getDomainName('.', $domain, 0); - $mergedBlackLists = array_merge(config('active-email.blacklist'), $disposableEmail->getBlacklist()); - $blackListedDomainNames = array_unique($mergedBlackLists); + $blackListedDomainNames = array_unique(array_merge(config('active-email.blacklist'), $disposableEmail->getBlacklist())); + $greyListedDomainNames = array_unique(array_merge(config('active-email.greylist'), $disposableEmail->getGreyList())); + $whiteListedDomainNames = array_unique(config('active-email.whitelist')); + + foreach ($whiteListedDomainNames as $whiteListedDomainName) { + $byePass = $this->getDomainName('.', $whiteListedDomainName, 0); - $mergedGreyLists = array_merge(config('active-email.greylist'), $disposableEmail->getGreyList()); - $greyListedDomainNames = array_unique($mergedGreyLists); + if ($domainName === $byePass) { + return; + } + } if (config('active-email.strict_mode')) { $blackListedDomainNames = array_merge($blackListedDomainNames, $greyListedDomainNames); diff --git a/tests/Unit/ActiveEmailTest.php b/tests/Unit/ActiveEmailTest.php index 43ae5a4..fcf3d63 100644 --- a/tests/Unit/ActiveEmailTest.php +++ b/tests/Unit/ActiveEmailTest.php @@ -64,3 +64,67 @@ expect($validator->fails())->toBeTrue(); } }); + +it('validates that domains can be whitelisted - class instantiation', function () { + $rule = ['email' => [new NotBlacklistedEmail]]; + + // Valid email addresses (not blacklisted). + $validEmails = [ + 'user@gmail.com', + 'user@yahoo.com', + 'user@customdomain.org', + 'user@10minutemail.com', + ]; + + foreach ($validEmails as $email) { + $validator = Validator::make(['email' => $email], $rule); + expect($validator->passes())->toBeTrue(); + } + + // Invalid email addresses (blacklisted domains). + $invalidEmails = [ + 'user@mailinator.com', + 'user@tempmail.com', + 'user@example.ltd', + 'user@example.co', + 'user@example.com.nh', + 'user@example.co.uk', + ]; + + foreach ($invalidEmails as $email) { + $validator = Validator::make(['email' => $email], $rule); + expect($validator->fails())->toBeTrue(); + } +}); + +it('validates that domains can be whitelisted - alias', function () { + $rule = ['email' => 'notblacklisted']; + + // Valid email addresses (not blacklisted). + $validEmails = [ + 'user@gmail.com', + 'user@yahoo.com', + 'user@customdomain.org', + 'user@10minutemail.com', + ]; + + foreach ($validEmails as $email) { + $validator = Validator::make(['email' => $email], $rule); + expect($validator->passes())->toBeTrue(); + } + + // Invalid email addresses (blacklisted domains). + $invalidEmails = [ + 'user@mailinator.com', + 'user@tempmail.com', + 'user@example.ltd', + 'user@example.co', + 'user@example.com.nh', + 'user@example.co.uk', + ]; + + foreach ($invalidEmails as $email) { + $validator = Validator::make(['email' => $email], $rule); + expect($validator->fails())->toBeTrue(); + } +}); \ No newline at end of file diff --git a/tests/Unit/TestCase.php b/tests/Unit/TestCase.php index 13e209d..7b65ac6 100644 --- a/tests/Unit/TestCase.php +++ b/tests/Unit/TestCase.php @@ -46,8 +46,9 @@ protected function getPackageAliases($app) protected function getEnvironmentSetUp($app) { // Set up any necessary configurations here - $app['config']->set('active-email.blacklist', ['mailinator.com', 'tempmail.com', 'example.ltd', 'example.co', 'example.com.nh', 'example.co.uk']); + $app['config']->set('active-email.blacklist', ['mailinator.com', 'tempmail.com', 'example.ltd', 'example.co', 'example.com.nh', 'example.co.uk', '10minutemail']); $app['config']->set('active-email.greylist', []); + $app['config']->set('active-email.whitelist', ['10minutemail']); $app['config']->set('active-email.strict_mode', false); } }