mirror of
https://github.com/bitinflow/expose.git
synced 2026-03-13 13:35:54 +00:00
Allow users to reserve subdomains (#131)
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Server\Http\Controllers\Admin;
|
||||
|
||||
use App\Contracts\SubdomainRepository;
|
||||
use App\Contracts\UserRepository;
|
||||
use Illuminate\Http\Request;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
class DeleteSubdomainController extends AdminController
|
||||
{
|
||||
protected $keepConnectionOpen = true;
|
||||
|
||||
/** @var SubdomainRepository */
|
||||
protected $subdomainRepository;
|
||||
|
||||
/** @var UserRepository */
|
||||
protected $userRepository;
|
||||
|
||||
public function __construct(UserRepository $userRepository, SubdomainRepository $subdomainRepository)
|
||||
{
|
||||
$this->userRepository = $userRepository;
|
||||
$this->subdomainRepository = $subdomainRepository;
|
||||
}
|
||||
|
||||
public function handle(Request $request, ConnectionInterface $httpConnection)
|
||||
{
|
||||
$this->userRepository->getUserByToken($request->get('auth_token', ''))
|
||||
->then(function ($user) use ($request, $httpConnection) {
|
||||
if (is_null($user)) {
|
||||
$httpConnection->send(respond_json(['error' => 'The user does not exist'], 404));
|
||||
$httpConnection->close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->subdomainRepository->deleteSubdomainForUserId($user['id'], $request->get('subdomain'))
|
||||
->then(function ($deleted) use ($httpConnection) {
|
||||
$httpConnection->send(respond_json(['deleted' => $deleted], 200));
|
||||
$httpConnection->close();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Server\Http\Controllers\Admin;
|
||||
|
||||
use App\Contracts\SubdomainRepository;
|
||||
use App\Contracts\UserRepository;
|
||||
use Illuminate\Http\Request;
|
||||
use Ratchet\ConnectionInterface;
|
||||
@@ -13,21 +14,31 @@ class GetUserDetailsController extends AdminController
|
||||
/** @var UserRepository */
|
||||
protected $userRepository;
|
||||
|
||||
public function __construct(UserRepository $userRepository)
|
||||
/** @var SubdomainRepository */
|
||||
protected $subdomainRepository;
|
||||
|
||||
public function __construct(UserRepository $userRepository, SubdomainRepository $subdomainRepository)
|
||||
{
|
||||
$this->userRepository = $userRepository;
|
||||
$this->subdomainRepository = $subdomainRepository;
|
||||
}
|
||||
|
||||
public function handle(Request $request, ConnectionInterface $httpConnection)
|
||||
{
|
||||
$this->userRepository
|
||||
->getUserById($request->get('id'))
|
||||
->then(function ($user) use ($httpConnection) {
|
||||
$httpConnection->send(
|
||||
respond_json(['user' => $user])
|
||||
);
|
||||
->then(function ($user) use ($httpConnection, $request) {
|
||||
$this->subdomainRepository->getSubdomainsByUserId($request->get('id'))
|
||||
->then(function ($subdomains) use ($httpConnection, $user) {
|
||||
$httpConnection->send(
|
||||
respond_json([
|
||||
'user' => $user,
|
||||
'subdomains' => $subdomains,
|
||||
])
|
||||
);
|
||||
|
||||
$httpConnection->close();
|
||||
$httpConnection->close();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace App\Server\Http\Controllers\Admin;
|
||||
|
||||
use App\Contracts\SubdomainRepository;
|
||||
use App\Contracts\UserRepository;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
class StoreSubdomainController extends AdminController
|
||||
{
|
||||
protected $keepConnectionOpen = true;
|
||||
|
||||
/** @var SubdomainRepository */
|
||||
protected $subdomainRepository;
|
||||
|
||||
/** @var UserRepository */
|
||||
protected $userRepository;
|
||||
|
||||
public function __construct(UserRepository $userRepository, SubdomainRepository $subdomainRepository)
|
||||
{
|
||||
$this->userRepository = $userRepository;
|
||||
$this->subdomainRepository = $subdomainRepository;
|
||||
}
|
||||
|
||||
public function handle(Request $request, ConnectionInterface $httpConnection)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'subdomain' => 'required',
|
||||
], [
|
||||
'required' => 'The :attribute field is required.',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
$httpConnection->send(respond_json(['errors' => $validator->getMessageBag()], 401));
|
||||
$httpConnection->close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->userRepository->getUserByToken($request->get('auth_token', ''))
|
||||
->then(function ($user) use ($httpConnection, $request) {
|
||||
if (is_null($user)) {
|
||||
$httpConnection->send(respond_json(['error' => 'The user does not exist'], 404));
|
||||
$httpConnection->close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($user['can_specify_subdomains'] === 0) {
|
||||
$httpConnection->send(respond_json(['error' => 'The user is not allowed to reserve subdomains.'], 401));
|
||||
$httpConnection->close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$insertData = [
|
||||
'user_id' => $user['id'],
|
||||
'subdomain' => $request->get('subdomain'),
|
||||
];
|
||||
|
||||
$this->subdomainRepository
|
||||
->storeSubdomain($insertData)
|
||||
->then(function ($subdomain) use ($httpConnection) {
|
||||
if (is_null($subdomain)) {
|
||||
$httpConnection->send(respond_json(['error' => 'The subdomain is already taken.'], 422));
|
||||
$httpConnection->close();
|
||||
|
||||
return;
|
||||
}
|
||||
$httpConnection->send(respond_json(['subdomain' => $subdomain], 200));
|
||||
$httpConnection->close();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Server\Http\Controllers;
|
||||
|
||||
use App\Contracts\ConnectionManager;
|
||||
use App\Contracts\SubdomainRepository;
|
||||
use App\Contracts\UserRepository;
|
||||
use App\Http\QueryParameters;
|
||||
use App\Server\Exceptions\NoFreePortAvailable;
|
||||
@@ -20,10 +21,14 @@ class ControlMessageController implements MessageComponentInterface
|
||||
/** @var UserRepository */
|
||||
protected $userRepository;
|
||||
|
||||
public function __construct(ConnectionManager $connectionManager, UserRepository $userRepository)
|
||||
/** @var SubdomainRepository */
|
||||
protected $subdomainRepository;
|
||||
|
||||
public function __construct(ConnectionManager $connectionManager, UserRepository $userRepository, SubdomainRepository $subdomainRepository)
|
||||
{
|
||||
$this->connectionManager = $connectionManager;
|
||||
$this->userRepository = $userRepository;
|
||||
$this->subdomainRepository = $subdomainRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,22 +105,26 @@ class ControlMessageController implements MessageComponentInterface
|
||||
|
||||
protected function handleHttpConnection(ConnectionInterface $connection, $data, $user = null)
|
||||
{
|
||||
if (! $this->hasValidSubdomain($connection, $data->subdomain, $user)) {
|
||||
return;
|
||||
}
|
||||
$this->hasValidSubdomain($connection, $data->subdomain, $user)->then(function ($subdomain) use ($data, $connection) {
|
||||
if ($subdomain === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$connectionInfo = $this->connectionManager->storeConnection($data->host, $data->subdomain, $connection);
|
||||
$data->subdomain = $subdomain;
|
||||
|
||||
$this->connectionManager->limitConnectionLength($connectionInfo, config('expose.admin.maximum_connection_length'));
|
||||
$connectionInfo = $this->connectionManager->storeConnection($data->host, $data->subdomain, $connection);
|
||||
|
||||
$connection->send(json_encode([
|
||||
'event' => 'authenticated',
|
||||
'data' => [
|
||||
'message' => config('expose.admin.messages.message_of_the_day'),
|
||||
'subdomain' => $connectionInfo->subdomain,
|
||||
'client_id' => $connectionInfo->client_id,
|
||||
],
|
||||
]));
|
||||
$this->connectionManager->limitConnectionLength($connectionInfo, config('expose.admin.maximum_connection_length'));
|
||||
|
||||
$connection->send(json_encode([
|
||||
'event' => 'authenticated',
|
||||
'data' => [
|
||||
'message' => config('expose.admin.messages.message_of_the_day'),
|
||||
'subdomain' => $connectionInfo->subdomain,
|
||||
'client_id' => $connectionInfo->client_id,
|
||||
],
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
protected function handleTcpConnection(ConnectionInterface $connection, $data, $user = null)
|
||||
@@ -203,39 +212,65 @@ class ControlMessageController implements MessageComponentInterface
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
protected function hasValidSubdomain(ConnectionInterface $connection, ?string $subdomain, ?array $user): bool
|
||||
protected function hasValidSubdomain(ConnectionInterface $connection, ?string $subdomain, ?array $user): PromiseInterface
|
||||
{
|
||||
/**
|
||||
* Check if the user can specify a custom subdomain in the first place.
|
||||
*/
|
||||
if (! is_null($user) && $user['can_specify_subdomains'] === 0 && ! is_null($subdomain)) {
|
||||
$connection->send(json_encode([
|
||||
'event' => 'subdomainTaken',
|
||||
'event' => 'info',
|
||||
'data' => [
|
||||
'message' => config('expose.admin.messages.custom_subdomain_unauthorized'),
|
||||
'message' => config('expose.admin.messages.custom_subdomain_unauthorized').PHP_EOL,
|
||||
],
|
||||
]));
|
||||
$connection->close();
|
||||
|
||||
return false;
|
||||
return \React\Promise\resolve(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given subdomain is reserved for a different user.
|
||||
*/
|
||||
if (! is_null($subdomain)) {
|
||||
$controlConnection = $this->connectionManager->findControlConnectionForSubdomain($subdomain);
|
||||
if (! is_null($controlConnection) || $subdomain === config('expose.admin.subdomain')) {
|
||||
$message = config('expose.admin.messages.subdomain_taken');
|
||||
$message = str_replace(':subdomain', $subdomain, $message);
|
||||
return $this->subdomainRepository->getSubdomainByName($subdomain)
|
||||
->then(function ($foundSubdomain) use ($connection, $subdomain, $user) {
|
||||
if (! is_null($foundSubdomain) && ! is_null($user) && $foundSubdomain['user_id'] !== $user['id']) {
|
||||
$message = config('expose.admin.messages.subdomain_reserved');
|
||||
$message = str_replace(':subdomain', $subdomain, $message);
|
||||
|
||||
$connection->send(json_encode([
|
||||
'event' => 'subdomainTaken',
|
||||
'data' => [
|
||||
'message' => $message,
|
||||
],
|
||||
]));
|
||||
$connection->close();
|
||||
$connection->send(json_encode([
|
||||
'event' => 'subdomainTaken',
|
||||
'data' => [
|
||||
'message' => $message,
|
||||
],
|
||||
]));
|
||||
$connection->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
return \React\Promise\resolve(false);
|
||||
}
|
||||
|
||||
$controlConnection = $this->connectionManager->findControlConnectionForSubdomain($subdomain);
|
||||
|
||||
if (! is_null($controlConnection) || $subdomain === config('expose.admin.subdomain')) {
|
||||
$message = config('expose.admin.messages.subdomain_taken');
|
||||
$message = str_replace(':subdomain', $subdomain, $message);
|
||||
|
||||
$connection->send(json_encode([
|
||||
'event' => 'subdomainTaken',
|
||||
'data' => [
|
||||
'message' => $message,
|
||||
],
|
||||
]));
|
||||
$connection->close();
|
||||
|
||||
return \React\Promise\resolve(false);
|
||||
}
|
||||
|
||||
return \React\Promise\resolve($subdomain);
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
return \React\Promise\resolve($subdomain);
|
||||
}
|
||||
|
||||
protected function canShareTcpPorts(ConnectionInterface $connection, $data, $user)
|
||||
|
||||
Reference in New Issue
Block a user