Allow specifying maximum connection counts per user

This commit is contained in:
Marcel Pociot
2021-05-28 16:51:48 +02:00
parent 717e8cf05c
commit a3d1735b6e
10 changed files with 164 additions and 73 deletions

View File

@@ -29,4 +29,8 @@ interface ConnectionManager
public function getConnectionsForAuthToken(string $authToken): array;
public function getTcpConnectionsForAuthToken(string $authToken): array;
public function findControlConnectionsForIp(string $ip): array;
public function findControlConnectionsForAuthToken(string $token): array;
}

View File

@@ -6,6 +6,7 @@ use App\Contracts\ConnectionManager as ConnectionManagerContract;
use App\Contracts\SubdomainGenerator;
use App\Http\QueryParameters;
use App\Server\Exceptions\NoFreePortAvailable;
use Illuminate\Support\Collection;
use Ratchet\ConnectionInterface;
use React\EventLoop\LoopInterface;
use React\Socket\Server;
@@ -157,6 +158,20 @@ class ConnectionManager implements ConnectionManagerContract
});
}
public function findControlConnectionsForIp(string $ip): array
{
return collect($this->connections)->filter(function (ControlConnection $connection) use ($ip) {
return $connection->socket->remoteAddress == $ip;
})->toArray();
}
public function findControlConnectionsForAuthToken(string $token): array
{
return collect($this->connections)->filter(function (ControlConnection $connection) use ($token) {
return $connection->authToken === $token;
})->toArray();
}
public function getConnections(): array
{
return $this->connections;

View File

@@ -41,6 +41,7 @@ class StoreUsersController extends AdminController
'auth_token' => (string) Str::uuid(),
'can_specify_subdomains' => (int) $request->get('can_specify_subdomains'),
'can_share_tcp_ports' => (int) $request->get('can_share_tcp_ports'),
'max_connections' => (int) $request->get('max_connections'),
];
$this->userRepository

View File

@@ -7,11 +7,13 @@ use App\Contracts\SubdomainRepository;
use App\Contracts\UserRepository;
use App\Http\QueryParameters;
use App\Server\Exceptions\NoFreePortAvailable;
use Illuminate\Support\Arr;
use Ratchet\ConnectionInterface;
use Ratchet\WebSocket\MessageComponentInterface;
use React\Promise\Deferred;
use React\Promise\PromiseInterface;
use stdClass;
use function React\Promise\reject;
class ControlMessageController implements MessageComponentInterface
{
@@ -85,7 +87,35 @@ class ControlMessageController implements MessageComponentInterface
protected function authenticate(ConnectionInterface $connection, $data)
{
if (! isset($data->subdomain)) {
$data->subdomain = null;
}
$this->verifyAuthToken($connection)
->then(function ($user) use ($connection) {
$maximumConnectionCount = config('expose.admin.maximum_open_connections_per_user', 0);
if (is_null($user)) {
$connectionCount = count($this->connectionManager->findControlConnectionsForIp($connection->remoteAddress));
} else {
$maximumConnectionCount = Arr::get($user, 'max_connections', $maximumConnectionCount);
$connectionCount = count($this->connectionManager->findControlConnectionsForAuthToken($user['auth_token']));
}
if ($maximumConnectionCount > 0 && $connectionCount + 1 > $maximumConnectionCount) {
$connection->send(json_encode([
'event' => 'authenticationFailed',
'data' => [
'message' => config('expose.admin.messages.maximum_connection_count'),
],
]));
$connection->close();
reject(null);
}
return $user;
})
->then(function ($user) use ($connection, $data) {
if ($data->type === 'http') {
$this->handleHttpConnection($connection, $data, $user);

View File

@@ -132,8 +132,8 @@ class DatabaseUserRepository implements UserRepository
$deferred = new Deferred();
$this->database->query("
INSERT INTO users (name, auth_token, can_specify_subdomains, can_share_tcp_ports, created_at)
VALUES (:name, :auth_token, :can_specify_subdomains, :can_share_tcp_ports, DATETIME('now'))
INSERT INTO users (name, auth_token, can_specify_subdomains, can_share_tcp_ports, max_connections, created_at)
VALUES (:name, :auth_token, :can_specify_subdomains, :can_share_tcp_ports, :max_connections, DATETIME('now'))
", $data)
->then(function (Result $result) use ($deferred) {
$this->database->query('SELECT * FROM users WHERE id = :id', ['id' => $result->insertId])