Skip to content

Commit be37fd3

Browse files
authored
Merge pull request #9 from xtrime-ru/access_control
Independent media rmp control.
2 parents 8e10558 + d8102af commit be37fd3

7 files changed

Lines changed: 85 additions & 51 deletions

File tree

.env.docker.example

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,28 @@ MAX_MEDIA_SIZE=31457280
2525
# -1 - disable RPM check.
2626
# 0 - deny all request
2727
# 15 - 15 requests per last 60 seconds.
28-
DEFAULT_RPM=15
28+
RPM=15
29+
MEDIA_RPM=45
2930

3031
# Allowed number of errors per minute
3132
# Examples:
3233
# -1 - allow any number of errors
3334
# 0 - ban after first error
3435
# 2 - allow 2 errors per minute: ban on 3rd error.
35-
DEFAULT_ERRORS_LIMIT=0;
36+
ERRORS_LIMIT=0;
37+
MEDIA_ERRORS_LIMIT=2;
3638

3739
# Json list of addresses with custom RPM and errors limits.
3840
# Its a WHITELIST and a BLACKLIST at same time.
3941
# Override DEDAULT_RPM and DEFAULT_ERRORS_LIMIT for individual IPs.
4042
# Example:
4143
# {
42-
# \"127.0.0.1\": {\"rpm\":-1, \"errorsLimit\":-1},
43-
# \"1.1.1.1\": {\"rpm\":0, \"errorsLimit\":0},
44-
# \"8.8.8.8\": {\"rpm\":0, \"errorsLimit\":0}
44+
# \"127.0.0.1\": {\"rpm\":-1, \"errors_limit\":-1},
45+
# \"1.1.1.1\": {\"rpm\":0, \"media_rpm\":1, \"errors_limit\":0, \"media_errors_limit\":2,},
46+
# \"8.8.8.8\": {\"rpm\":0, \"errors_limit\":0}
4547
# }
4648
CLIENTS_SETTINGS="{
47-
\"127.0.0.1\": {\"rpm\":-1, \"errorsLimit\":-1}
49+
\"127.0.0.1\": {\"rpm\":-1, \"errors_limit\":-1}
4850
}"
4951

5052
# LOGS

.env.example

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,28 @@ MAX_MEDIA_SIZE=31457280
2525
# -1 - disable RPM check.
2626
# 0 - deny all request
2727
# 15 - 15 requests per last 60 seconds.
28-
DEFAULT_RPM=15
28+
RPM=15
29+
MEDIA_RPM=45
2930

3031
# Allowed number of errors per minute
3132
# Examples:
3233
# -1 - allow any number of errors
3334
# 0 - ban after first error
3435
# 2 - allow 2 errors per minute: ban on 3rd error.
35-
DEFAULT_ERRORS_LIMIT=0;
36+
ERRORS_LIMIT=0;
37+
MEDIA_ERRORS_LIMIT=2;
3638

3739
# Json list of addresses with custom RPM and errors limits.
3840
# Its a WHITELIST and a BLACKLIST at same time.
3941
# Override DEDAULT_RPM and DEFAULT_ERRORS_LIMIT for individual IPs.
4042
# Example:
4143
# {
42-
# \"127.0.0.1\": {\"rpm\":-1, \"errorsLimit\":-1},
43-
# \"1.1.1.1\": {\"rpm\":0, \"errorsLimit\":0},
44-
# \"8.8.8.8\": {\"rpm\":0, \"errorsLimit\":0}
44+
# \"127.0.0.1\": {\"rpm\":-1, \"errors_limit\":-1},
45+
# \"1.1.1.1\": {\"rpm\":0, \"media_rpm\":1, \"errors_limit\":0, \"media_errors_limit\":2,},
46+
# \"8.8.8.8\": {\"rpm\":0, \"errors_limit\":0}
4547
# }
4648
CLIENTS_SETTINGS="{
47-
\"127.0.0.1\": {\"rpm\":-1, \"errorsLimit\":-1}
49+
\"127.0.0.1\": {\"rpm\":-1, \"errors_limit\":-1}
4850
}"
4951

5052
# Set to 0 if you need to output any channels, chats, etc.

app/AccessControl/AccessControl.php

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,28 @@ class AccessControl
99
{
1010
/** @var User[] */
1111
private array $users = [];
12+
/** @var User[] */
13+
private array $mediaUsers = [];
1214

1315
/** @var int Interval to remove old clients: 60 seconds */
1416
private const CLEANUP_INTERVAL_MS = 60*1000;
1517
private int $rpmLimit;
1618
private int $errorsLimit;
19+
20+
private int $mediaRpmLimit;
21+
private int $mediaErrorsLimit;
22+
1723
/** @var int[] */
1824
private array $clientsSettings;
1925

2026
public function __construct()
2127
{
22-
$this->rpmLimit = (int) Config::getInstance()->get('access.default_rpm');
23-
$this->errorsLimit = (int) Config::getInstance()->get('access.default_errors_limit');
28+
$this->rpmLimit = (int) Config::getInstance()->get('access.rpm');
29+
$this->errorsLimit = (int) Config::getInstance()->get('access.errors_limit');
30+
31+
$this->mediaRpmLimit = (int) Config::getInstance()->get('access.media_rpm');
32+
$this->mediaErrorsLimit = (int) Config::getInstance()->get('access.media_errors_limit');
33+
2434
$this->clientsSettings = (array) Config::getInstance()->get('access.clients_settings');
2535

2636
Timer::tick(static::CLEANUP_INTERVAL_MS, function () {
@@ -33,26 +43,38 @@ private function removeOldUsers(): void
3343
$now = time();
3444
foreach ($this->users as $ip => $user) {
3545
if ($user->isOld($now)) {
36-
$this->removeUser($ip);
46+
unset($this->users[$ip]);
47+
}
48+
}
49+
foreach ($this->mediaUsers as $ip => $user) {
50+
if ($user->isOld($now)) {
51+
unset($this->mediaUsers[$ip]);
3752
}
3853
}
3954
}
4055

41-
private function removeUser(string $ip): void
56+
public function getOrCreateUser(string $ip, string $type = 'default'): User
4257
{
43-
unset($this->users[$ip]);
44-
}
58+
if ($type === 'media') {
59+
if (!isset($this->mediaUsers[$ip])) {
60+
$this->mediaUsers[$ip] = new User(
61+
$this->clientsSettings[$ip]['media_rpm'] ?? $this->clientsSettings[$ip]['rpm'] ?? $this->mediaRpmLimit,
62+
$this->clientsSettings[$ip]['media_errors_limit'] ?? $this->clientsSettings[$ip]['errors_limit'] ?? $this->mediaErrorsLimit
63+
);
64+
}
4565

46-
public function getOrCreateUser($ip)
47-
{
48-
if (!isset($this->users[$ip])) {
49-
$user = $this->users[$ip] = new User(
50-
$this->clientsSettings[$ip]['rpm'] ?? $this->rpmLimit,
51-
$this->clientsSettings[$ip]['errorsLimit'] ?? $this->errorsLimit
52-
);
66+
return $this->mediaUsers[$ip];
67+
} else {
68+
if (!isset($this->users[$ip])) {
69+
$this->users[$ip] = new User(
70+
$this->clientsSettings[$ip]['rpm'] ?? $this->rpmLimit,
71+
$this->clientsSettings[$ip]['errors_limit'] ?? $this->errorsLimit
72+
);
73+
}
74+
75+
return $this->users[$ip];
5376
}
5477

55-
return $this->users[$ip];
5678
}
5779

5880
}

app/Controller.php

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,21 @@ class Controller {
7272
/**
7373
* Controller constructor.
7474
*
75-
* @param Request $request
76-
* @param Response $response
77-
* @param Client $client
7875
* @param AccessControl $accessControl
7976
*/
80-
public function __construct(
81-
Request $request,
82-
Response $response,
83-
Client $client,
84-
AccessControl $accessControl
85-
) {
86-
//Parse request and generate response
77+
public function __construct(AccessControl $accessControl)
78+
{
8779
$this->accessControl = $accessControl;
80+
}
8881

82+
/**
83+
* Parse request and generate response
84+
*
85+
* @param Request $request
86+
* @param Response $response
87+
* @param Client $client
88+
*/
89+
public function process(Request $request, Response $response, Client $client) {
8990
$this
9091
->route($request)
9192
->validate()
@@ -105,7 +106,6 @@ public function __construct(
105106
}
106107

107108
$response->close();
108-
109109
}
110110

111111
/**
@@ -123,7 +123,16 @@ private function route(Request $request): self {
123123
$request->server['remote_addr']
124124
;
125125
$this->request['url'] = $request->server['request_uri'] ?? $request->server['path_info'] ?? '';
126-
$this->user = $this->accessControl->getOrCreateUser($this->request['ip']);
126+
$path = array_values(array_filter(explode('/', $request->server['request_uri'])));
127+
$type = $path[0] ?? '';
128+
129+
if ($type === 'media') {
130+
$accessType = 'media';
131+
} else {
132+
$accessType = 'default';
133+
}
134+
135+
$this->user = $this->accessControl->getOrCreateUser($this->request['ip'], $accessType);
127136

128137
Log::getInstance()->add([
129138
'remote_addr' => $this->request['ip'],
@@ -137,9 +146,6 @@ private function route(Request $request): self {
137146
'errors_limit' => $this->user->errorsLimit,
138147
]);
139148

140-
$path = array_values(array_filter(explode('/', $request->server['request_uri'])));
141-
$type = $path[0] ?? '';
142-
143149
switch (true) {
144150
case $type === 'favicon.ico':
145151
$this->response['type'] = $type;

app/Server.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ static function (Request $request, Response $response) use ($client, $accessCont
3939
//иначе их данные будут в области видимости всех запросов.
4040

4141
//Телеграм клиент инициализируется 1 раз и используется во всех запросах.
42-
new Controller($request, $response, $client, $accessControl);
42+
(new Controller($accessControl))->process($request, $response, $client);
4343
if (++$counter % 100 === 0) {
4444
gc_collect_cycles();
4545
$counter = 0;

composer.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?php
22
global $options;
33
if (
4-
getenv('DEFAULT_RPM') === false
5-
|| getenv('DEFAULT_ERRORS_LIMIT') === false
4+
getenv('RPM') === false
5+
|| getenv('ERRORS_LIMIT') === false
66
|| getenv('CLIENTS_SETTINGS') === false
77
) {
88
throw new RuntimeException(
@@ -40,8 +40,10 @@
4040
'max_size' => (int) getenv('MAX_MEDIA_SIZE'),
4141
],
4242
'access' => [
43-
'default_rpm' => (int) getenv('DEFAULT_RPM'),
44-
'default_errors_limit' => (int) getenv('DEFAULT_ERRORS_LIMIT'),
43+
'rpm' => (int) getenv('RPM'),
44+
'errors_limit' => (int) getenv('ERRORS_LIMIT'),
45+
'media_rpm' => (int) getenv('MEDIA_RPM'),
46+
'media_errors_limit' => (int) getenv('MEDIA_ERRORS_LIMIT'),
4547
'clients_settings' => $clientsSettings,
4648
'only_public_channels' => (bool) getenv('ONLY_PUBLIC_CHANNELS'),
4749
],

0 commit comments

Comments
 (0)