| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- <?php
- namespace Illuminate\Validation\Rules;
- use ArrayIterator;
- use Illuminate\Container\Container;
- use Illuminate\Contracts\Validation\DataAwareRule;
- use Illuminate\Contracts\Validation\ImplicitRule;
- use Illuminate\Contracts\Validation\Rule;
- use Illuminate\Contracts\Validation\UncompromisedVerifier;
- use Illuminate\Contracts\Validation\ValidatorAwareRule;
- use Illuminate\Support\Arr;
- use Illuminate\Support\Facades\Validator;
- use Illuminate\Support\Traits\Conditionable;
- use InvalidArgumentException;
- use IteratorAggregate;
- use Traversable;
- class Password implements DataAwareRule, ImplicitRule, IteratorAggregate, Rule, ValidatorAwareRule
- {
- use Conditionable;
- /**
- * The validator performing the validation.
- *
- * @var \Illuminate\Contracts\Validation\Validator
- */
- protected $validator;
- /**
- * The data under validation.
- *
- * @var array
- */
- protected $data;
- /**
- * The minimum size of the password.
- *
- * @var int
- */
- protected $min = 8;
- /**
- * The maximum size of the password.
- *
- * @var int
- */
- protected $max;
- /**
- * If the password is required.
- *
- * @var bool
- */
- protected $required = false;
- /**
- * If the password should only be validated when present.
- *
- * @var bool
- */
- protected $sometimes = false;
- /**
- * If the password requires at least one uppercase and one lowercase letter.
- *
- * @var bool
- */
- protected $mixedCase = false;
- /**
- * If the password requires at least one letter.
- *
- * @var bool
- */
- protected $letters = false;
- /**
- * If the password requires at least one number.
- *
- * @var bool
- */
- protected $numbers = false;
- /**
- * If the password requires at least one symbol.
- *
- * @var bool
- */
- protected $symbols = false;
- /**
- * If the password should not have been compromised in data leaks.
- *
- * @var bool
- */
- protected $uncompromised = false;
- /**
- * The number of times a password can appear in data leaks before being considered compromised.
- *
- * @var int
- */
- protected $compromisedThreshold = 0;
- /**
- * Additional validation rules that should be merged into the default rules during validation.
- *
- * @var array
- */
- protected $customRules = [];
- /**
- * The failure messages, if any.
- *
- * @var array
- */
- protected $messages = [];
- /**
- * The callback that will generate the "default" version of the password rule.
- *
- * @var string|array|callable|null
- */
- public static $defaultCallback;
- /**
- * Create a new rule instance.
- *
- * @param int $min
- */
- public function __construct($min)
- {
- $this->min = max((int) $min, 1);
- }
- /**
- * Set the default callback to be used for determining a password's default rules.
- *
- * If no arguments are passed, the default password rule configuration will be returned.
- *
- * @param static|callable|null $callback
- * @return static|void
- *
- * @throws \InvalidArgumentException
- */
- public static function defaults($callback = null)
- {
- if (is_null($callback)) {
- return static::default();
- }
- if (! is_callable($callback) && ! $callback instanceof static) {
- throw new InvalidArgumentException('The given callback should be callable or an instance of '.static::class);
- }
- static::$defaultCallback = $callback;
- }
- /**
- * Get the default configuration of the password rule.
- *
- * @return static
- */
- public static function default()
- {
- $password = is_callable(static::$defaultCallback)
- ? call_user_func(static::$defaultCallback)
- : static::$defaultCallback;
- return $password instanceof Rule ? $password : static::min(8);
- }
- /**
- * Get the default configuration of the password rule and mark the field as required.
- *
- * @return static
- */
- public static function required()
- {
- $password = static::default();
- $password->required = true;
- return $password;
- }
- /**
- * Get the default configuration of the password rule and mark the field as sometimes being required.
- *
- * @return static
- */
- public static function sometimes()
- {
- $password = static::default();
- $password->sometimes = true;
- return $password;
- }
- /**
- * Set the performing validator.
- *
- * @param \Illuminate\Contracts\Validation\Validator $validator
- * @return $this
- */
- public function setValidator($validator)
- {
- $this->validator = $validator;
- return $this;
- }
- /**
- * Set the data under validation.
- *
- * @param array $data
- * @return $this
- */
- public function setData($data)
- {
- $this->data = $data;
- return $this;
- }
- /**
- * Set the minimum size of the password.
- *
- * @param int $size
- * @return $this
- */
- public static function min($size)
- {
- return new static($size);
- }
- /**
- * Set the maximum size of the password.
- *
- * @param int $size
- * @return $this
- */
- public function max($size)
- {
- $this->max = $size;
- return $this;
- }
- /**
- * Ensures the password has not been compromised in data leaks.
- *
- * @param int $threshold
- * @return $this
- */
- public function uncompromised($threshold = 0)
- {
- $this->uncompromised = true;
- $this->compromisedThreshold = $threshold;
- return $this;
- }
- /**
- * Makes the password require at least one uppercase and one lowercase letter.
- *
- * @return $this
- */
- public function mixedCase()
- {
- $this->mixedCase = true;
- return $this;
- }
- /**
- * Makes the password require at least one letter.
- *
- * @return $this
- */
- public function letters()
- {
- $this->letters = true;
- return $this;
- }
- /**
- * Makes the password require at least one number.
- *
- * @return $this
- */
- public function numbers()
- {
- $this->numbers = true;
- return $this;
- }
- /**
- * Makes the password require at least one symbol.
- *
- * @return $this
- */
- public function symbols()
- {
- $this->symbols = true;
- return $this;
- }
- /**
- * Specify additional validation rules that should be merged with the default rules during validation.
- *
- * @param \Closure|string|array $rules
- * @return $this
- */
- public function rules($rules)
- {
- $this->customRules = Arr::wrap($rules);
- return $this;
- }
- /**
- * Determine if the validation rule passes.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- public function passes($attribute, $value)
- {
- $this->messages = [];
- if (! $this->required && ! $this->sometimes && ! Arr::has($this->data ?? [], $attribute)) {
- return true;
- }
- if (blank($value) && ! $this->required && $this->validator?->hasRule($attribute, ['Nullable'])) {
- return true;
- }
- $validator = Validator::make(
- $this->data,
- [$attribute => [...$this]],
- $this->validator->customMessages,
- $this->validator->customAttributes
- )->after(function ($validator) use ($attribute, $value) {
- if (! is_string($value)) {
- return;
- }
- if ($this->mixedCase && ! preg_match('/(\p{Ll}+.*\p{Lu})|(\p{Lu}+.*\p{Ll})/u', $value)) {
- $validator->addFailure($attribute, 'password.mixed');
- }
- if ($this->letters && ! preg_match('/\pL/u', $value)) {
- $validator->addFailure($attribute, 'password.letters');
- }
- if ($this->symbols && ! preg_match('/\p{Z}|\p{S}|\p{P}/u', $value)) {
- $validator->addFailure($attribute, 'password.symbols');
- }
- if ($this->numbers && ! preg_match('/\pN/u', $value)) {
- $validator->addFailure($attribute, 'password.numbers');
- }
- });
- if ($validator->fails()) {
- return $this->fail($validator->messages()->all());
- }
- if ($this->uncompromised && ! Container::getInstance()->make(UncompromisedVerifier::class)->verify([
- 'value' => $value,
- 'threshold' => $this->compromisedThreshold,
- ])) {
- $validator->addFailure($attribute, 'password.uncompromised');
- return $this->fail($validator->messages()->all());
- }
- return true;
- }
- /**
- * Get the validation error message.
- *
- * @return array
- */
- public function message()
- {
- return $this->messages;
- }
- /**
- * Adds the given failures, and return false.
- *
- * @param array|string $messages
- * @return bool
- */
- protected function fail($messages)
- {
- $this->messages = array_merge($this->messages, Arr::wrap($messages));
- return false;
- }
- /**
- * Get information about the current state of the password validation rules.
- *
- * @return array
- */
- public function appliedRules()
- {
- return [
- 'min' => $this->min,
- 'max' => $this->max,
- 'mixedCase' => $this->mixedCase,
- 'letters' => $this->letters,
- 'numbers' => $this->numbers,
- 'symbols' => $this->symbols,
- 'uncompromised' => $this->uncompromised,
- 'compromisedThreshold' => $this->compromisedThreshold,
- 'customRules' => $this->customRules,
- ];
- }
- /**
- * Get an iterator for the password validation rules.
- *
- * @return \ArrayIterator<TKey, TValue>
- */
- public function getIterator(): Traversable
- {
- return new ArrayIterator([
- ...($this->required ? ['required'] : []),
- ...($this->sometimes ? ['sometimes'] : []),
- 'string',
- 'min:'.$this->min,
- ...($this->max ? ['max:'.$this->max] : []),
- ...$this->customRules,
- ]);
- }
- }
|