This commit is contained in:
mrilyew 2025-06-25 23:02:27 +03:00
parent bdac64a4ae
commit 5898b9a455
5 changed files with 110 additions and 56 deletions

View file

@ -37,14 +37,6 @@ trait TSubscribable
if (!($sub->fetch())) { if (!($sub->fetch())) {
$ctx->table("subscriptions")->insert($data); $ctx->table("subscriptions")->insert($data);
# todo change
if (str_contains(static::class, "Club")) {
\openvk\Web\Util\EventRateLimiter::i()->writeEvent("groups.sub", $user, $this);
} else {
\openvk\Web\Util\EventRateLimiter::i()->writeEvent("friends.outgoing_sub", $user, $this);
}
return true; return true;
} }

View file

@ -1044,8 +1044,6 @@ class User extends RowModel
"anonymous" => $anonymous, "anonymous" => $anonymous,
"sent" => time(), "sent" => time(),
]); ]);
\openvk\Web\Util\EventRateLimiter::i()->writeEvent("gifts.send", $sender, $this);
} }
public function ban(string $reason, bool $deleteSubscriptions = true, $unban_time = null, ?int $initiator = null): void public function ban(string $reason, bool $deleteSubscriptions = true, $unban_time = null, ?int $initiator = null): void
@ -1741,4 +1739,42 @@ class User extends RowModel
{ {
return DatabaseConnection::i()->getContext()->table("blacklist_relations")->where("author", $this->getId())->count(); return DatabaseConnection::i()->getContext()->table("blacklist_relations")->where("author", $this->getId())->count();
} }
public function recieveEventsData(array $list): array
{
$ev_str = $this->getRecord()->events_counters;
$values = [];
if (!$ev_str) {
for ($i = 0; $i < sizeof(array_keys($list)); $i++) {
$values[] = 0;
}
} else {
$keys = array_keys($list);
$values = unpack("S*", base64_decode($ev_str));
}
return [
'counters' => $values,
'refresh_time' => $this->getRecord()->events_refresh_time,
];
}
public function stateEvents(array $list): void
{
$this->stateChanges("events_counters", base64_encode(pack("S*", array_values($list))));
}
public function resetEvents(array $list, int $restriction_length)
{
$values = [];
for ($i = 0; $i < sizeof(array_keys($list)); $i++) {
$values[] = 0;
}
$this->stateEvents($values);
$this->stateChanges("events_refresh_time", $restriction_length + time());
$this->save();
}
} }

View file

@ -83,8 +83,6 @@ final class GroupPresenter extends OpenVKPresenter
} }
$club->toggleSubscription($this->user->identity); $club->toggleSubscription($this->user->identity);
\openvk\Web\Util\EventRateLimiter::i()->writeEvent("groups.create", $this->user->identity, $club);
$this->redirect("/club" . $club->getId()); $this->redirect("/club" . $club->getId());
} else { } else {

View file

@ -432,8 +432,6 @@ final class WallPresenter extends OpenVKPresenter
} }
} }
\openvk\Web\Util\EventRateLimiter::i()->writeEvent("wall.post", $this->user->identity, $wallOwner);
if ($should_be_suggested) { if ($should_be_suggested) {
$this->redirect("/club" . $wallOwner->getId() . "/suggested"); $this->redirect("/club" . $wallOwner->getId() . "/suggested");
} else { } else {

View file

@ -8,23 +8,6 @@ use openvk\Web\Models\Entities\User;
use openvk\Web\Models\RowModel; use openvk\Web\Models\RowModel;
use Chandler\Patterns\TSimpleSingleton; use Chandler\Patterns\TSimpleSingleton;
class UserEvent
{
private $data;
public function __construct($data)
{
$this->data = $data;
}
public function write($edb): bool
{
$edb->getConnection()->query("INSERT INTO `user-events` VALUES (?, ?, ?, ?, ?)", ...array_values($this->data));
return true;
}
}
class EventRateLimiter class EventRateLimiter
{ {
use TSimpleSingleton; use TSimpleSingleton;
@ -35,58 +18,105 @@ class EventRateLimiter
public function __construct() public function __construct()
{ {
$this->config = OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]; $this->config = OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"];
$this->availableFields = array_keys($this->config['list']); $this->availableFields = array_keys($this->config['list']);
} }
/* /*
Checks count of actions for last x seconds, returns true if limit has exceed Checks count of actions for last hours
x is OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]["restrictionTime"] Uses config path OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]
Returns:
true limit has exceed and the action must be restricted
false the action can be performed
*/ */
public function tryToLimit(?User $user, string $event_type, bool $distinct = true): bool public function tryToLimit(?User $user, string $event_type, bool $distinct = true): bool
{ {
if (!$this->config['enable']) { $isEnabled = $this->config['enable'];
$isIgnoreForAdmins = $this->config['ignoreForAdmins'];
$restrictionTime = $this->config['restrictionTime'];
$eventsList = $this->config['list'];
if (!$isEnabled) {
return false; return false;
} }
if ($this->config['ignoreForAdmins'] && $user->isAdmin()) { if ($isIgnoreForAdmins && $user->isAdmin()) {
return false; return false;
} }
$limitForThisEvent = $this->config['list'][$event_type]; $limitForThatEvent = $eventsList[$event_type];
$compareTime = time() - $this->config['restrictionTime']; $stat = $this->getEvent($event_type, $user);
bdump($stat);
$query = "SELECT COUNT(".($distinct ? "DISTINCT(`receiverId`)" : "*").") as `cnt` FROM `user-events` WHERE `initiatorId` = ? AND `eventType` = ? AND `eventTime` > ?"; $is_restrict_over = $stat["refresh_time"] < time() - $restrictionTime;
$count = $result->fetch()->cnt;
#bdump($count); exit();
return $count > $limitForThisEvent; if ($is_restrict_over) {
$user->resetEvents($eventsList, $restrictionTime);
return false;
}
$is = $stat["compared"] > $limitForThatEvent;
if ($is === false) {
$this->incrementEvent($event_type, $user);
}
return $is;
}
public function getEvent(string $event_type, User $by_user): array
{
$ev_data = $by_user->recieveEventsData($this->config['list']);
$values = $ev_data['counters'];
$i = 0;
$compared = [];
bdump($values);
foreach ($this->config['list'] as $name => $value) {
bdump($value);
$compared[$name] = $values[$i];
$i += 1;
}
return [
"compared" => $compared,
"refresh_time" => $ev_data["refresh_time"]
];
} }
/* /*
Writes new event to `openvk-eventdb`.`user-events` Updates counter for user
*/ */
public function writeEvent(string $event_type, User $initiator, ?RowModel $reciever = null): bool public function incrementEvent(string $event_type, User $initiator): bool
{ {
if (!$this->config['enable']) { $isEnabled = $this->config['enable'];
$eventsList = OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"];
if (!$isEnabled) {
return false; return false;
} }
$data = [ $ev_data = $initiator->recieveEventsData($eventsList);
'initiatorId' => $initiator->getId(), $values = $ev_data['counters'];
'initiatorIp' => null, $i = 0;
'receiverId' => null,
'eventType' => $event_type,
'eventTime' => time()
];
if ($reciever) { $compared = [];
$data['receiverId'] = $reciever->getRealId();
foreach ($eventsList as $name => $value) {
$compared[$name] = $values[$i];
$i += 1;
} }
$newEvent = new UserEvent($data); $compared[$event_type] += 1;
$newEvent->write($e);
bdump($compared);
$initiator->stateEvents($compared);
return true; return true;
} }