mirror of
				https://github.com/elyby/accounts.git
				synced 2025-05-31 14:11:46 +05:30 
			
		
		
		
	Resolves #9. Implemented host-based blacklist for users emails
This commit is contained in:
		| @@ -13,6 +13,7 @@ final class Error { | ||||
|     public const EMAIL_TOO_LONG = 'error.email_too_long'; | ||||
|     public const EMAIL_INVALID = 'error.email_invalid'; | ||||
|     public const EMAIL_IS_TEMPMAIL = 'error.email_is_tempmail'; | ||||
|     public const EMAIL_HOST_IS_NOT_ALLOWED = 'error.email_host_is_not_allowed'; | ||||
|     public const EMAIL_NOT_AVAILABLE = 'error.email_not_available'; | ||||
|     public const EMAIL_NOT_FOUND = 'error.email_not_found'; | ||||
|  | ||||
|   | ||||
| @@ -11,10 +11,7 @@ use yii\validators\EmailValidator as YiiEmailValidator; | ||||
|  | ||||
| class EmailValidatorTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @var EmailValidator | ||||
|      */ | ||||
|     private $validator; | ||||
|     private EmailValidator $validator; | ||||
|  | ||||
|     public function _before() { | ||||
|         parent::_before(); | ||||
| @@ -102,6 +99,28 @@ class EmailValidatorTest extends TestCase { | ||||
|         $this->assertNotSame(['error.email_is_tempmail'], $model->getErrors('field')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @dataProvider getValidateAttributeBlacklistedHostTestCases | ||||
|      */ | ||||
|     public function testValidateAttributeBlacklistedHost(string $email, bool $expectValid) { | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'checkdnsrr')->expects($this->any())->willReturn(true); | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'dns_get_record')->expects($this->any())->willReturn(['127.0.0.1']); | ||||
|  | ||||
|         $model = $this->createModel($email); | ||||
|         $this->validator->validateAttribute($model, 'field'); | ||||
|         $errors = $model->getErrors('field'); | ||||
|         if ($expectValid) { | ||||
|             $this->assertEmpty($errors); | ||||
|         } else { | ||||
|             $this->assertSame(['error.email_host_is_not_allowed'], $errors); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function getValidateAttributeBlacklistedHostTestCases() { | ||||
|         yield 'seznam.cz' => ['user@seznam.cz', false]; | ||||
|         yield 'valid' => ['valid@google.com', true]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @dataProvider getValidateAttributeIdnaTestCases | ||||
|      */ | ||||
|   | ||||
| @@ -1,4 +1,6 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace common\validators; | ||||
|  | ||||
| use common\helpers\Error as E; | ||||
| @@ -13,15 +15,15 @@ use yii\validators\Validator; | ||||
| class EmailValidator extends Validator { | ||||
|  | ||||
|     /** | ||||
|      * @var \Closure the function must return the account id for which the current validation is being performed. | ||||
|      * Allows you to skip the email check for the current account. | ||||
|      * @var callable(): int the function must return the account id for which the current validation is being performed. | ||||
|      * Allows you to skip the email uniqueness check for the current account. | ||||
|      */ | ||||
|     public $accountCallback; | ||||
|  | ||||
|     public $skipOnEmpty = false; | ||||
|  | ||||
|     public function validateAttribute($model, $attribute) { | ||||
|         $filter = new validators\FilterValidator(['filter' => [StringHelper::class, 'trim']]); | ||||
|     public function validateAttribute($model, $attribute): void { | ||||
|         $trim = new validators\FilterValidator(['filter' => [StringHelper::class, 'trim']]); | ||||
|  | ||||
|         $required = new validators\RequiredValidator(); | ||||
|         $required->message = E::EMAIL_REQUIRED; | ||||
| @@ -38,6 +40,21 @@ class EmailValidator extends Validator { | ||||
|         $tempmail = new TempmailValidator(); | ||||
|         $tempmail->message = E::EMAIL_IS_TEMPMAIL; | ||||
|  | ||||
|         $blacklist = new class extends Validator { | ||||
|             public $hosts = [ | ||||
|                 'seznam.cz', | ||||
|             ]; | ||||
|  | ||||
|             protected function validateValue($value): ?array { | ||||
|                 $host = explode('@', $value)[1]; | ||||
|                 if (in_array($host, $this->hosts, true)) { | ||||
|                     return [E::EMAIL_HOST_IS_NOT_ALLOWED, []]; | ||||
|                 } | ||||
|  | ||||
|                 return null; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         $idnaDomain = new validators\FilterValidator(['filter' => function(string $value): string { | ||||
|             [$name, $domain] = explode('@', $value); | ||||
|             return idn_to_ascii($name, 0, INTL_IDNA_VARIANT_UTS46) . '@' . idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46); | ||||
| @@ -48,16 +65,17 @@ class EmailValidator extends Validator { | ||||
|         $unique->targetClass = Account::class; | ||||
|         $unique->targetAttribute = 'email'; | ||||
|         if ($this->accountCallback !== null) { | ||||
|             $unique->filter = function(QueryInterface $query) { | ||||
|             $unique->filter = function(QueryInterface $query): void { | ||||
|                 $query->andWhere(['NOT', ['id' => ($this->accountCallback)()]]); | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         $this->executeValidation($filter, $model, $attribute) && | ||||
|         $this->executeValidation($trim, $model, $attribute) && | ||||
|         $this->executeValidation($required, $model, $attribute) && | ||||
|         $this->executeValidation($length, $model, $attribute) && | ||||
|         $this->executeValidation($email, $model, $attribute) && | ||||
|         $this->executeValidation($tempmail, $model, $attribute) && | ||||
|         $this->executeValidation($blacklist, $model, $attribute) && | ||||
|         $this->executeValidation($idnaDomain, $model, $attribute) && | ||||
|         $this->executeValidation($unique, $model, $attribute); | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user