2018-04-11

This commit is contained in:
Visman 2018-04-11 18:18:42 +07:00
parent 271c91b87a
commit f35e93627c
12 changed files with 274 additions and 241 deletions

View file

@ -4,6 +4,7 @@ namespace ForkBB\Core;
use ForkBB\Core\Container;
use ForkBB\Core\File;
use ForkBB\Core\Validators;
use RuntimeException;
class Validator
@ -108,12 +109,10 @@ class Validator
'absent' => [$this, 'vAbsent'],
'array' => [$this, 'vArray'],
'checkbox' => [$this, 'vCheckbox'],
'email' => [$this, 'vEmail'],
'file' => [$this, 'vFile'],
'image' => [$this, 'vImage'],
'in' => [$this, 'vIn'],
'integer' => [$this, 'vInteger'],
'login' => [$this, 'vLogin'],
'max' => [$this, 'vMax'],
'min' => [$this, 'vMin'],
'numeric' => [$this, 'vNumeric'],
@ -171,11 +170,20 @@ class Validator
$rules = [];
// перебор правил для текущего поля
foreach (\explode('|', $raw) as $rule) { //???? нужно экранирование для разделителей
$tmp = \explode(':', $rule, 2);
if (empty($this->validators[$tmp[0]])) {
throw new RuntimeException($tmp[0] . ' validator not found');
$vs = \explode(':', $rule, 2);
if (empty($this->validators[$vs[0]])) {
try {
$validator = $this->c->{'VL' . $vs[0]};
} catch (Exception $e) {
$validator = null;
}
if ($validator instanceof Validators) {
$this->validators[$vs[0]] = [$validator, $vs[0]];
} else {
throw new RuntimeException($vs[0] . ' validator not found');
}
}
$rules[$tmp[0]] = isset($tmp[1]) ? $tmp[1] : '';
$rules[$vs[0]] = isset($vs[1]) ? $vs[1] : '';
}
if (isset($suffix)) {
$this->rules[$field]['array'][$suffix] = $rules;
@ -449,7 +457,7 @@ class Validator
*
* @return mixed
*/
protected function vAbsent($v, $value, $attr)
protected function vAbsent(Validator $v, $value, $attr)
{
if (null !== $value) {
$this->addError('The :alias should be absent');
@ -461,7 +469,7 @@ class Validator
}
}
protected function vRequired($v, $value)
protected function vRequired(Validator $v, $value)
{
if (\is_string($value)) {
if (\strlen(\preg_replace('%^\s+|\s+$%u', '', $value)) > 0) {
@ -478,7 +486,7 @@ class Validator
return null;
}
protected function vRequiredWith($v, $value, $attr) //???????????????????????
protected function vRequiredWith(Validator $v, $value, $attr) //???????????????????????
{
foreach (\explode(',', $attr) as $field) {
if (null !== $this->__get($field)) { // если есть хотя бы одно поле,
@ -498,7 +506,7 @@ class Validator
# }
}
protected function vString($v, $value, $attr)
protected function vString(Validator $v, $value, $attr)
{
if (null === $value) {
return null;
@ -523,7 +531,7 @@ class Validator
}
}
protected function vNumeric($v, $value)
protected function vNumeric(Validator $v, $value)
{
if (null === $value) {
return null;
@ -535,7 +543,7 @@ class Validator
}
}
protected function vInteger($v, $value)
protected function vInteger(Validator $v, $value)
{
if (null === $value) {
return null;
@ -547,7 +555,7 @@ class Validator
}
}
protected function vArray($v, $value, $attr)
protected function vArray(Validator $v, $value, $attr)
{
if (null !== $value && ! \is_array($value)) {
$this->addError('The :alias must be array');
@ -598,9 +606,7 @@ class Validator
}
}
protected function vMin($v, $value, $attr)
protected function vMin(Validator $v, $value, $attr)
{
if (\is_string($value)) {
$isBytes = \strpos($attr, 'bytes');
@ -624,7 +630,7 @@ class Validator
return $value;
}
protected function vMax($v, $value, $attr)
protected function vMax(Validator $v, $value, $attr)
{
if (\is_string($value)) {
$isBytes = \strpos($attr, 'bytes');
@ -661,7 +667,7 @@ class Validator
return $value;
}
protected function vToken($v, $value, $attr, $args)
protected function vToken(Validator $v, $value, $attr, $args)
{
if (! \is_array($args)) {
$args = [];
@ -674,12 +680,12 @@ class Validator
}
}
protected function vCheckbox($v, $value)
protected function vCheckbox(Validator $v, $value)
{
return null === $value ? false : (string) $value;
}
protected function vReferer($v, $value, $attr, $args)
protected function vReferer(Validator $v, $value, $attr, $args)
{
if (! \is_array($args)) {
$args = [];
@ -687,21 +693,7 @@ class Validator
return $this->c->Router->validate($value, $attr, $args);
}
protected function vEmail($v, $value)
{
if (null === $value || '' === $value) { //???? перед правилом должно стоять правило `required`
return null;
}
$email = $this->c->Mail->valid($value, true);
if (false === $email) {
$this->addError('The :alias is not valid email');
return $value;
} else {
return $email;
}
}
protected function vSame($v, $value, $attr)
protected function vSame(Validator $v, $value, $attr)
{
if (! $this->getStatus($attr) || $value === $this->__get($attr)) {
return $value;
@ -711,7 +703,7 @@ class Validator
}
}
protected function vRegex($v, $value, $attr)
protected function vRegex(Validator $v, $value, $attr)
{
if (null !== $value
&& (! \is_string($value) || ! \preg_match($attr, $value))
@ -723,17 +715,12 @@ class Validator
}
}
protected function vPassword($v, $value)
protected function vPassword(Validator $v, $value)
{
return $this->vRegex($v, $value, '%[^\x20][\x20][^\x20]%');
}
protected function vLogin($v, $value)
{
return $this->vRegex($v, $value, '%^\p{L}[\p{L}\p{N}\x20\._-]+$%uD');
}
protected function vIn($v, $value, $attr)
protected function vIn(Validator $v, $value, $attr)
{
if (null !== $value && ! \in_array($value, \explode(',', $attr))) {
$this->addError('The :alias contains an invalid value');
@ -741,7 +728,7 @@ class Validator
return $value;
}
protected function vNotIn($v, $value, $attr)
protected function vNotIn(Validator $v, $value, $attr)
{
if (null !== $value && \in_array($value, \explode(',', $attr))) {
$this->addError('The :alias contains an invalid value');
@ -750,7 +737,7 @@ class Validator
}
protected function vFile($v, $value, $attr)
protected function vFile(Validator $v, $value, $attr)
{
if (null === $value) {
return null;
@ -776,7 +763,7 @@ class Validator
return $value;
}
protected function vImage($v, $value, $attr)
protected function vImage(Validator $v, $value, $attr)
{
$value = $this->vFile($v, $value, $attr);

38
app/Core/Validators.php Normal file
View file

@ -0,0 +1,38 @@
<?php
namespace ForkBB\Core;
use ForkBB\Core\Container;
use RuntimeException;
abstract class Validators
{
/**
* Контейнер
* @var Container
*/
protected $c;
/**
* Конструктор
*
* @param Container $container
*/
public function __construct(Container $container)
{
$this->c = $container;
}
/**
* Выбрасывает исключение при отсутствии метода
*
* @param string $name
* @param array $args
*
* @throws RuntimeException
*/
public function __call($name, array $args)
{
throw new RuntimeException($name . ' validator not found');
}
}

View file

@ -70,8 +70,8 @@ class Options extends Admin
'o_avatars_width' => 'required|integer|min:50|max:999',
'o_avatars_height' => 'required|integer|min:50|max:999',
'o_avatars_size' => 'required|integer|min:0|max:9999999',
'o_admin_email' => 'required|string:trim,lower|max:80|email',
'o_webmaster_email' => 'required|string:trim,lower|max:80|email',
'o_admin_email' => 'required|string:trim,lower|email',
'o_webmaster_email' => 'required|string:trim,lower|email',
'o_forum_subscriptions' => 'required|integer|in:0,1',
'o_topic_subscriptions' => 'required|integer|in:0,1',
'o_smtp_host' => 'string:trim|max:255',

View file

@ -156,15 +156,14 @@ class Auth extends Page
$v = $this->c->Validator->reset()
->addValidators([
'check_email' => [$this->c->Validators, 'vCheckEmail'],
])->addRules([
'token' => 'token:Forget',
'email' => 'required|string:trim,lower|email|check_email:exists,flood',
'email' => 'required|string:trim,lower|email:banned,exists,flood',
])->addAliases([
])->addMessages([
'email.email' => 'Invalid email',
])->addArguments([
'email.check_email' => $tmpUser, // сюда идет возрат данных по найденному пользователю
'email.email' => $tmpUser, // сюда идет возрат данных по найденному пользователю
]);
if ($v->validation($_POST)) {

View file

@ -27,8 +27,6 @@ trait PostValidatorTrait
&& ! \preg_match('%\p{Ll}%u', $subject)
) {
$v->addError('All caps subject');
} elseif (! $executive) {
$this->c->Validators->vNoURL($v, $subject, null, null);
}
return $subject;
}
@ -102,19 +100,13 @@ trait PostValidatorTrait
protected function messageValidator(Model $model, $marker, array $args, $editPost = false, $editSubject = false)
{
if ($this->user->isGuest) {
$ruleEmail = ('1' == $this->c->config->p_force_guest_email ? 'required|' : '') . 'string:trim,lower|email|check_email';
$ruleUsername = 'required|string:trim,spaces|min:2|max:25|login|check_username';
$ruleEmail = ('1' == $this->c->config->p_force_guest_email ? 'required|' : '') . 'string:trim,lower|email:banned';
$ruleUsername = 'required|string:trim,spaces|username';
} else {
$ruleEmail = 'absent';
$ruleUsername = 'absent';
}
if ($editSubject) {
$ruleSubject = 'required|string:trim,spaces|min:1|max:70|check_subject';
} else {
$ruleSubject = 'absent';
}
if ($this->user->isAdmin || $this->user->isModerator($model)) {
if ($editSubject) {
$ruleStickTopic = 'checkbox';
@ -142,6 +134,12 @@ trait PostValidatorTrait
$executive = false;
}
if ($editSubject) {
$ruleSubject = 'required|string:trim,spaces|min:1|max:70|' . ($executive ? '' : 'noURL|') . 'check_subject';
} else {
$ruleSubject = 'absent';
}
if ('1' == $this->c->config->o_smilies) {
$ruleHideSmilies = 'checkbox';
} else {
@ -150,8 +148,6 @@ trait PostValidatorTrait
$v = $this->c->Validator->reset()
->addValidators([
'check_email' => [$this->c->Validators, 'vCheckEmail'],
'check_username' => [$this->c->Validators, 'vCheckUsername'],
'check_subject' => [$this, 'vCheckSubject'],
'check_message' => [$this, 'vCheckMessage'],
'check_timeout' => [$this, 'vCheckTimeout'],
@ -176,7 +172,7 @@ trait PostValidatorTrait
'token' => $args,
'subject.check_subject' => $executive,
'message.check_message' => $executive,
'email.check_email' => $this->user,
'email.email' => $this->user,
])->addMessages([
'username.login' => 'Login format',
]);

View file

@ -56,13 +56,13 @@ class Profile extends Page
if ($isEdit && 'POST' === $method) {
if ($this->rules->rename) {
$ruleUsername = 'required|string:trim,spaces|min:2|max:25|login|check_username';
$ruleUsername = 'required|string:trim,spaces|username';
} else {
$ruleUsername = 'absent';
}
if ($this->rules->setTitle) {
$ruleTitle = 'string:trim|max:50|no_url';
$ruleTitle = 'string:trim|max:50|noURL';
} else {
$ruleTitle = 'absent';
}
@ -95,8 +95,6 @@ class Profile extends Page
$v = $this->c->Validator->reset()
->addValidators([
'no_url' => [$this->c->Validators, 'vNoURL'],
'check_username' => [$this->c->Validators, 'vCheckUsername'],
'check_signature' => [$this, 'vCheckSignature'],
])->addRules([
'token' => 'token:EditUserProfile',
@ -105,9 +103,9 @@ class Profile extends Page
'upload_avatar' => $ruleAvatar,
'delete_avatar' => $ruleDelAvatar,
'admin_note' => $ruleAdminNote,
'realname' => 'string:trim|max:40|no_url',
'realname' => 'string:trim|max:40|noURL:1',
'gender' => 'required|integer|in:0,1,2',
'location' => 'string:trim|max:30|no_url',
'location' => 'string:trim|max:30|noURL:1',
'email_setting' => 'required|integer|in:0,1,2',
'url' => $ruleWebsite,
'signature' => $ruleSignature,
@ -124,8 +122,8 @@ class Profile extends Page
'url' => 'Website',
'signature' => 'Signature',
])->addArguments([
'token' => ['id' => $this->curUser->id],
'username.check_username' => $this->curUser,
'token' => ['id' => $this->curUser->id],
'username.username' => $this->curUser,
])->addMessages([
]);
@ -210,17 +208,16 @@ class Profile extends Page
$v = $this->c->Validator->reset()
->addValidators([
'check_password' => [$this, 'vCheckPassword'],
'check_email' => [$this->c->Validators, 'vCheckEmail'],
])->addRules([
'token' => 'token:ChangeUserEmail',
'password' => 'required|string:trim|check_password',
'new_email' => 'required|string:trim,lower|email|check_email:unique,flood',
'new_email' => 'required|string:trim,lower|email:banned,unique,flood',
])->addAliases([
'new_email' => 'New email',
'password' => 'Your password',
])->addArguments([
'token' => ['id' => $this->curUser->id],
'new_email.check_email' => $this->curUser,
'token' => ['id' => $this->curUser->id],
'new_email.email' => $this->curUser,
])->addMessages([
]);

View file

@ -20,14 +20,12 @@ class Register extends Page
$v = $this->c->Validator->reset()
->addValidators([
'check_email' => [$this->c->Validators, 'vCheckEmail'],
'check_username' => [$this->c->Validators, 'vCheckUsername'],
])->addRules([
'token' => 'token:RegisterForm',
'agree' => 'required|token:Register',
'on' => 'integer',
'email' => 'required_with:on|string:trim,lower|email|check_email:unique',
'username' => 'required_with:on|string:trim,spaces|min:2|max:25|login|check_username',
'email' => 'required_with:on|string:trim,lower|email:banned,unique',
'username' => 'required_with:on|string:trim,spaces|username',
'password' => 'required_with:on|string|min:16|password',
])->addAliases([
'email' => 'Email',

View file

@ -1,160 +0,0 @@
<?php
namespace ForkBB\Models;
use ForkBB\Core\Container;
use ForkBB\Core\Validator;
use ForkBB\Models\User\Model as User;
class Validators
{
/**
* Контейнер
* @var Container
*/
protected $c;
/**
* Конструктор
*
* @param Container $container
*/
public function __construct(Container $container)
{
$this->c = $container;
}
/**
* Дополнительная проверка username
*
* @param Validator $v
* @param string $username
* @param string $z
* @param mixed $originalUser
*
* @return string
*/
public function vCheckUsername(Validator $v, $username, $z, $originalUser)
{
if ($originalUser instanceof User) {
$id = $originalUser->id;
$old = $originalUser->username;
} else {
$id = null;
$old = null;
}
if ($old !== $username) {
$user = $this->c->users->create(['id' => $id, 'username' => $username]);
// username = Гость
if (\preg_match('%^(guest|' . \preg_quote(\ForkBB\__('Guest'), '%') . ')$%iu', $username)) { // ???? а зачем?
$v->addError('Username guest');
// цензура
} elseif ($this->c->censorship->censor($username) !== $username) {
$v->addError('Username censor');
// username забанен
} elseif ($this->c->bans->isBanned($user) > 0) {
$v->addError('Banned username');
// есть пользователь с похожим именем
} elseif (empty($v->getErrors()) && ! $this->c->users->isUniqueName($user)) {
$v->addError('Username not unique');
}
}
return $username;
}
/**
* Дополнительная проверка email
* WARNING!!!
* Если передан гость 4-ым параметром, то проверка уникальности email не проводится
*
* @param Validator $v
* @param string $email
* @param string $attrs
* @param mixed $originalUser
*
* @return string
*/
public function vCheckEmail(Validator $v, $email, $attrs, $originalUser)
{
// email забанен
if ($this->c->bans->isBanned($this->c->users->create(['email' => $email])) > 0) {
$v->addError('Banned email');
// остальные проверки
} elseif (empty($v->getErrors())) {
$attrs = \array_flip(\explode(',', $attrs));
$ok = true;
$user = true;
// наличие
if (isset($attrs['exists'])) {
$user = $this->c->users->load($email, 'email');
if (! $user instanceof User) {
$v->addError('Invalid email');
$ok = false;
}
}
// уникальность
if ($ok && isset($attrs['unique']) && (! $originalUser instanceof User || ! $originalUser->isGuest)) {
if (true === $user) {
$user = $this->c->users->load($email, 'email');
}
$id = $originalUser instanceof User ? $originalUser->id : true;
if (($user instanceof User && $id !== $user->id) || (! $user instanceof User && 0 !== $user)) {
$v->addError('Dupe email');
$ok = false;
}
}
// флуд
if ($ok && isset($attrs['flood'])) {
$min = 3600;
if ($originalUser instanceof User && ! $originalUser->isGuest) {
$flood = \time() - $originalUser->last_email_sent;
} elseif ($user instanceof User) {
$flood = \time() - $user->last_email_sent;
} else {
$flood = $min;
}
if ($flood < $min) {
$v->addError(\ForkBB\__('Email flood', (int) (($min - $flood) / 60)), 'e');
$ok = false;
}
}
// возврат данных пользователя через 4-ый параметр
if ($ok && $originalUser instanceof User && $originalUser->id < 1 && $user instanceof User) {
$originalUser->setAttrs($user->getAttrs());
}
}
return $email;
}
/**
* Дополнительная проверка на отсутствие url в значении
*
* @param Validator $v
* @param mixed $value
* @param string $flag
*
* @return mixed
*/
public function vNoURL(Validator $v, $value, $flag)
{
$flag = empty($flag) || '1' != $this->c->user->g_post_links;
if ($flag && \preg_match('%https?://|www\.%i', $value)) {
$v->addError('The :alias contains a link');
}
return $value;
}
}

View file

@ -0,0 +1,92 @@
<?php
namespace ForkBB\Models\Validators;
use ForkBB\Core\Validator;
use ForkBB\Core\Validators;
use ForkBB\Models\User\Model as User;
class Email extends Validators
{
const FLOOD = 3600;
/**
* Проверяет email
* WARNING!!!
* Если передан гость 4-ым параметром, то проверка уникальности email не проводится
*
* @param Validator $v
* @param string $email
* @param string $attrs
* @param mixed $originalUser
*
* @return string
*/
public function email(Validator $v, $email, $attrs, $originalUser)
{
// поле отсутствует
if (null === $email) {
return null;
// это не email
} elseif (false === ($result = $this->c->Mail->valid($email, true))) {
$v->addError('The :alias is not valid email');
return $email;
// есть другие ошибки
} elseif (! empty($v->getErrors())) {
return $result;
}
$email = $result;
$attrs = \array_flip(\explode(',', $attrs));
$ok = true;
$user = true;
// email забанен
if ($ok && isset($attrs['banned']) && $this->c->bans->isBanned($this->c->users->create(['email' => $email])) > 0) {
$v->addError('Banned email');
$ok = false;
}
// отсутствует пользователь с таким email (или их больше одного O_o)
if (isset($attrs['exists'])) {
$user = $this->c->users->load($email, 'email');
if (! $user instanceof User) {
$v->addError('Invalid email');
$ok = false;
}
}
// email не уникален
if ($ok && isset($attrs['unique']) && (! $originalUser instanceof User || ! $originalUser->isGuest)) {
if (true === $user) {
$user = $this->c->users->load($email, 'email');
}
$id = $originalUser instanceof User ? $originalUser->id : true;
if (($user instanceof User && $id !== $user->id) || (! $user instanceof User && 0 !== $user)) {
$v->addError('Dupe email');
$ok = false;
}
}
// проверка на флуд интервал
if ($ok && isset($attrs['flood'])) {
if ($originalUser instanceof User && ! $originalUser->isGuest) {
$flood = \time() - $originalUser->last_email_sent;
} elseif ($user instanceof User) {
$flood = \time() - $user->last_email_sent;
} else {
$flood = self::FLOOD;
}
if ($flood < self::FLOOD) {
$v->addError(\ForkBB\__('Email flood', (int) ((self::FLOOD - $flood) / 60)), 'e');
$ok = false;
}
}
// возврат данных пользователя через 4-ый параметр
if ($ok && $originalUser instanceof User && $originalUser->id < 1 && $user instanceof User) {
$originalUser->setAttrs($user->getAttrs());
}
return $email;
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace ForkBB\Models\Validators;
use ForkBB\Core\Validator;
use ForkBB\Core\Validators;
class NoURL extends Validators
{
/**
* Проверяет значение на отсутствие ссылки, если пользователю запрещено использовать ссылки или включен флаг принудительной проверки
*
* @param Validator $v
* @param mixed $value
* @param string $flag
*
* @return mixed
*/
public function noURL(Validator $v, $value, $flag)
{
if ((! empty($flag) || '1' != $this->c->user->g_post_links)
&& \preg_match('%https?://|www\.%i', $value)
) {
$v->addError('The :alias contains a link');
}
return $value;
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace ForkBB\Models\Validators;
use ForkBB\Core\Validator;
use ForkBB\Core\Validators;
use ForkBB\Models\User\Model as User;
class Username extends Validators
{
/**
* Проверяет имя пользователя
*
* @param Validator $v
* @param string $username
* @param string $z
* @param mixed $originalUser
*
* @return string
*/
public function username(Validator $v, $username, $z, $originalUser)
{
if ($originalUser instanceof User) {
$id = $originalUser->id;
$old = $originalUser->username;
} else {
$id = null;
$old = null;
}
if ($old !== $username) {
$user = $this->c->users->create(['id' => $id, 'username' => $username]);
// 2-25 символов, буквы, цифры, пробел, подчеркивание, точка и тире
if (! \preg_match('%^(?=.{2,25}$)\p{L}[\p{L}\p{N}\x20\._-]+$%uD', $username)) {
$v->addError('The :alias is not valid format'); // ???? выводить отдельное сообщение для username
// username = Гость
} elseif (\preg_match('%^(guest|' . \preg_quote(\ForkBB\__('Guest'), '%') . ')$%iu', $username)) { // ???? а зачем?
$v->addError('Username guest');
// цензура
} elseif ($this->c->censorship->censor($username) !== $username) {
$v->addError('Username censor');
// username забанен
} elseif ($this->c->bans->isBanned($user) > 0) {
$v->addError('Banned username');
// есть пользователь с похожим именем
} elseif (empty($v->getErrors()) && ! $this->c->users->isUniqueName($user)) {
$v->addError('Username not unique');
}
}
return $username;
}
}

View file

@ -117,7 +117,10 @@ return [
'file' => '%MAX_FILE_SIZE%',
'img' => '%MAX_IMG_SIZE%',
],
'Validators' => \ForkBB\Models\Validators::class,
'VLnoURL' => \ForkBB\Models\Validators\NoURL::class,
'VLusername' => \ForkBB\Models\Validators\Username::class,
'VLemail' => \ForkBB\Models\Validators\Email::class,
],
'multiple' => [