diff --git a/app/Contracts/SubdomainRepository.php b/app/Contracts/SubdomainRepository.php index e1c46a4..f55339f 100644 --- a/app/Contracts/SubdomainRepository.php +++ b/app/Contracts/SubdomainRepository.php @@ -14,6 +14,8 @@ interface SubdomainRepository public function getSubdomainByNameAndDomain(string $name, string $domain): PromiseInterface; + public function getSubdomainsByNameAndDomain(string $name, string $domain): PromiseInterface; + public function getSubdomainsByUserId($id): PromiseInterface; public function getSubdomainsByUserIdAndName($id, $name): PromiseInterface; diff --git a/app/Server/Http/Controllers/ControlMessageController.php b/app/Server/Http/Controllers/ControlMessageController.php index dd09635..caf11ab 100644 --- a/app/Server/Http/Controllers/ControlMessageController.php +++ b/app/Server/Http/Controllers/ControlMessageController.php @@ -323,9 +323,13 @@ class ControlMessageController implements MessageComponentInterface * Check if the given subdomain is reserved for a different user. */ if (! is_null($subdomain)) { - return $this->subdomainRepository->getSubdomainByNameAndDomain($subdomain, $serverHost) - ->then(function ($foundSubdomain) use ($connection, $subdomain, $user, $serverHost) { - if (! is_null($foundSubdomain) && ! is_null($user) && $foundSubdomain['user_id'] !== $user['id']) { + return $this->subdomainRepository->getSubdomainsByNameAndDomain($subdomain, $serverHost) + ->then(function ($foundSubdomains) use ($connection, $subdomain, $user, $serverHost) { + $ownSubdomain = collect($foundSubdomains)->first(function ($subdomain) use ($user) { + return $subdomain['user_id'] === $user['id']; + }); + + if (count($foundSubdomains) > 0 && ! is_null($user) && is_null($ownSubdomain)) { $message = config('expose.admin.messages.subdomain_reserved'); $message = str_replace(':subdomain', $subdomain, $message); diff --git a/app/Server/SubdomainRepository/DatabaseSubdomainRepository.php b/app/Server/SubdomainRepository/DatabaseSubdomainRepository.php index 9f0cba1..f599204 100644 --- a/app/Server/SubdomainRepository/DatabaseSubdomainRepository.php +++ b/app/Server/SubdomainRepository/DatabaseSubdomainRepository.php @@ -73,6 +73,22 @@ class DatabaseSubdomainRepository implements SubdomainRepository return $deferred->promise(); } + public function getSubdomainsByNameAndDomain(string $name, string $domain): PromiseInterface + { + $deferred = new Deferred(); + + $this->database + ->query('SELECT * FROM subdomains WHERE subdomain = :name AND domain = :domain', [ + 'name' => $name, + 'domain' => $domain, + ]) + ->then(function (Result $result) use ($deferred) { + $deferred->resolve($result->rows); + }); + + return $deferred->promise(); + } + public function getSubdomainsByUserId($id): PromiseInterface { $deferred = new Deferred(); diff --git a/tests/Feature/Server/TunnelTest.php b/tests/Feature/Server/TunnelTest.php index 20b2fdc..9e5faa1 100644 --- a/tests/Feature/Server/TunnelTest.php +++ b/tests/Feature/Server/TunnelTest.php @@ -315,6 +315,50 @@ class TunnelTest extends TestCase $this->assertSame('reserved', $response->subdomain); } + /** @test */ + public function it_allows_users_that_both_have_the_same_reserved_subdomain() + { + $this->app['config']['expose.admin.validate_auth_tokens'] = true; + + $user = $this->createUser([ + 'name' => 'Marcel', + 'can_specify_subdomains' => 1, + ]); + + $response = $this->await($this->browser->post('http://127.0.0.1:8080/api/subdomains', [ + 'Host' => 'expose.localhost', + 'Authorization' => base64_encode('username:secret'), + 'Content-Type' => 'application/json', + ], json_encode([ + 'subdomain' => 'reserved', + 'auth_token' => $user->auth_token, + ]))); + + $user = $this->createUser([ + 'name' => 'Test-User', + 'can_specify_subdomains' => 1, + ]); + + $response = $this->await($this->browser->post('http://127.0.0.1:8080/api/subdomains', [ + 'Host' => 'expose.localhost', + 'Authorization' => base64_encode('username:secret'), + 'Content-Type' => 'application/json', + ], json_encode([ + 'subdomain' => 'reserved', + 'auth_token' => $user->auth_token, + ]))); + + $this->createTestHttpServer(); + /** + * We create an expose client that connects to our server and shares + * the created test HTTP server. + */ + $client = $this->createClient(); + $response = $this->await($client->connectToServer('127.0.0.1:8085', 'reserved', null, $user->auth_token)); + + $this->assertSame('reserved', $response->subdomain); + } + /** @test */ public function it_rejects_users_that_want_to_use_a_reserved_subdomain_on_a_custom_domain() {