Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions app/DomainRepository/DatabaseDomainRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ public function storeDomain(array $data): PromiseInterface
$this->getDomainByName($data['domain'])
->then(function ($registeredDomain) use ($data, $deferred) {
$this->database->query("
INSERT INTO domains (user_id, domain, created_at)
VALUES (:user_id, :domain, DATETIME('now'))
INSERT INTO domains (user_id, domain, error_page, created_at)
VALUES (:user_id, :domain, :error_page, DATETIME('now'))
", $data)
->then(function (Result $result) use ($deferred) {
$this->database->query('SELECT * FROM domains WHERE id = :id', ['id' => $result->insertId])
Expand Down Expand Up @@ -128,7 +128,15 @@ public function updateDomain($id, array $data): PromiseInterface
{
$deferred = new Deferred();

// TODO
$this->database->query('UPDATE domains SET error_page = :error_page WHERE (id = :id OR domain = :id)', array_merge($data, [
'id' => $id,
]))
->then(function (Result $result) use ($deferred, $id) {
$this->getDomainById($id)
->then(function ($domain) use ($deferred) {
$deferred->resolve($domain);
});
});

return $deferred->promise();
}
Expand Down
2 changes: 2 additions & 0 deletions app/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
use Expose\Server\Http\Controllers\Admin\StoreSettingsController;
use Expose\Server\Http\Controllers\Admin\StoreSubdomainController;
use Expose\Server\Http\Controllers\Admin\StoreUsersController;
use Expose\Server\Http\Controllers\Admin\UpdateDomainController;
use Expose\Server\Http\Controllers\ControlMessageController;
use Expose\Server\Http\Controllers\HealthController;
use Expose\Server\Http\Controllers\TunnelMessageController;
Expand Down Expand Up @@ -160,6 +161,7 @@ protected function addAdminRoutes()
$this->router->get('/api/logs/{subdomain}', GetLogsForSubdomainController::class, $adminCondition);

$this->router->post('/api/domains', StoreDomainController::class, $adminCondition);
$this->router->put('/api/domains/{id}', UpdateDomainController::class, $adminCondition);
$this->router->delete('/api/domains/{domain}', DeleteSubdomainController::class, $adminCondition);

$this->router->post('/api/subdomains', StoreSubdomainController::class, $adminCondition);
Expand Down
2 changes: 2 additions & 0 deletions app/Http/Controllers/Admin/StoreDomainController.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public function handle(Request $request, ConnectionInterface $httpConnection)
{
$validator = Validator::make($request->all(), [
'domain' => 'required',
'error_page' => 'nullable|string'
], [
'required' => 'The :attribute field is required.',
]);
Expand Down Expand Up @@ -59,6 +60,7 @@ public function handle(Request $request, ConnectionInterface $httpConnection)
$insertData = [
'user_id' => $user['id'],
'domain' => $request->get('domain'),
'error_page' => $request->get('error_page'),
];

$this->domainRepository
Expand Down
71 changes: 71 additions & 0 deletions app/Http/Controllers/Admin/UpdateDomainController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

namespace Expose\Server\Http\Controllers\Admin;

use Expose\Server\Contracts\DomainRepository;
use Expose\Server\Contracts\UserRepository;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Ratchet\ConnectionInterface;

class UpdateDomainController extends AdminController
{
protected $keepConnectionOpen = true;

/** @var DomainRepository */
protected $domainRepository;

/** @var UserRepository */
protected $userRepository;

public function __construct(UserRepository $userRepository, DomainRepository $domainRepository)
{
$this->userRepository = $userRepository;
$this->domainRepository = $domainRepository;
}

public function handle(Request $request, ConnectionInterface $httpConnection)
{
$id = $request->get('id');

$validator = Validator::make($request->all(), [
'error_page' => 'nullable|string'
]);

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, $id) {
if (is_null($user)) {
$httpConnection->send(respond_json(['error' => 'The user does not exist'], 404));
$httpConnection->close();

return;
}

if ($user['can_specify_domains'] === 0) {
$httpConnection->send(respond_json(['error' => 'The user is not allowed to reserve custom domains.'], 401));
$httpConnection->close();

return;
}

$updateData = [
'error_page' => $request->get('error_page'),
];

$this->domainRepository
->updateDomain($id, $updateData)
->then(function ($domain) use ($httpConnection) {
$httpConnection->send(respond_json(['domain' => $domain], 200));
$httpConnection->close();
});
});
}
}
30 changes: 24 additions & 6 deletions app/Http/Controllers/TunnelMessageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Expose\Server\Connections\ControlConnection;
use Expose\Server\Connections\HttpConnection;
use Expose\Server\Contracts\ConnectionManager;
use Expose\Server\Contracts\DomainRepository;
use Expose\Server\Contracts\StatisticsCollector;
use Expose\Common\Http\Controllers\Controller;
use GuzzleHttp\Psr7\Message;
Expand All @@ -31,11 +32,15 @@ class TunnelMessageController extends Controller
/** @var StatisticsCollector */
protected $statisticsCollector;

public function __construct(ConnectionManager $connectionManager, StatisticsCollector $statisticsCollector, Configuration $configuration)
/** @var DomainRepository */
protected $domainRepository;

public function __construct(ConnectionManager $connectionManager, StatisticsCollector $statisticsCollector, Configuration $configuration, DomainRepository $domainRepository)
{
$this->connectionManager = $connectionManager;
$this->configuration = $configuration;
$this->statisticsCollector = $statisticsCollector;
$this->domainRepository = $domainRepository;
}

public function handle(Request $request, ConnectionInterface $httpConnection)
Expand All @@ -55,11 +60,24 @@ public function handle(Request $request, ConnectionInterface $httpConnection)
$controlConnection = $this->connectionManager->findControlConnectionForSubdomainAndServerHost($subdomain, $serverHost);

if (is_null($controlConnection)) {
$httpConnection->send(
respond_html($this->getBlade($httpConnection, 'server.errors.404', ['subdomain' => $subdomain]), 404)
);
$httpConnection->close();

$this->domainRepository
->getDomainByName(strtolower($serverHost))
->then(function ($domain) use ($subdomain, $httpConnection) {
if (is_null($domain) || is_null($domain['error_page'])) {
$errorPageContent = $this->getBlade($httpConnection, 'server.errors.404', ['subdomain' => $subdomain]);
} else {
$errorPageContent = str_replace(
['%%subdomain%%'],
[$subdomain],
$domain['error_page']
);
}

$httpConnection->send(
respond_html($errorPageContent, 404)
);
$httpConnection->close();
});
return;
}

Expand Down
1 change: 1 addition & 0 deletions database/migrations/12_add_404_to_domains_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE domains ADD COLUMN error_page TEXT DEFAULT NULL;
103 changes: 103 additions & 0 deletions tests/Feature/Server/TunnelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,109 @@ public function it_returns_404_for_non_existing_clients_on_custom_hosts()
]));
}

/** @test */
public function it_returns_default_404_pages_for_custom_domains_when_no_custom_error_page_is_specified()
{
$this->app['config']['expose-server.validate_auth_tokens'] = true;

$user = $this->createUser([
'name' => 'Marcel',
'can_specify_domains' => 1,
]);

$this->await($this->browser->post('http://127.0.0.1:8080/api/domains', [
'Host' => 'expose.localhost',
'Authorization' => base64_encode('username:secret'),
'Content-Type' => 'application/json',
], json_encode([
'domain' => 'share.beyondco.de',
'auth_token' => $user->auth_token,
])));

try {
$this->await($this->browser->get('http://127.0.0.1:8080/', [
'Host' => 'tunnel.share.beyondco.de',
]));
} catch (ResponseException $e) {
$response = $e->getResponse();

$this->assertStringContainsString('<title>Expose</title>', $response->getBody()->getContents());
}
}

/** @test */
public function it_returns_custom_404_pages_for_custom_domains_when_specified()
{
$this->app['config']['expose-server.validate_auth_tokens'] = true;

$user = $this->createUser([
'name' => 'Marcel',
'can_specify_domains' => 1,
]);

$this->await($this->browser->post('http://127.0.0.1:8080/api/domains', [
'Host' => 'expose.localhost',
'Authorization' => base64_encode('username:secret'),
'Content-Type' => 'application/json',
], json_encode([
'domain' => 'share.beyondco.de',
'error_page' => '<h1>Custom 404 for %%subdomain%%</h1>',
'auth_token' => $user->auth_token,
])));

try {
$this->await($this->browser->get('http://127.0.0.1:8080/', [
'Host' => 'tunnel.share.beyondco.de',
]));
} catch (ResponseException $e) {
$response = $e->getResponse();

$this->assertStringContainsString('<h1>Custom 404 for tunnel</h1>', $response->getBody()->getContents());
}
}

/** @test */
public function it_can_update_404_pages_for_custom_domains()
{
$this->app['config']['expose-server.validate_auth_tokens'] = true;

$user = $this->createUser([
'name' => 'Marcel',
'can_specify_domains' => 1,
]);

$domainResponse = $this->await($this->browser->post('http://127.0.0.1:8080/api/domains', [
'Host' => 'expose.localhost',
'Authorization' => base64_encode('username:secret'),
'Content-Type' => 'application/json',
], json_encode([
'domain' => 'share.beyondco.de',
'auth_token' => $user->auth_token,
])));

$domain = json_decode($domainResponse->getBody()->getContents())->domain;

$this->await($this->browser->put('http://127.0.0.1:8080/api/domains/'.$domain->id, [
'Host' => 'expose.localhost',
'Authorization' => base64_encode('username:secret'),
'Content-Type' => 'application/json',
], json_encode([
'domain' => 'share.beyondco.de',
'error_page' => '<h1>Custom 404 for %%subdomain%%</h1>',
'auth_token' => $user->auth_token,
])));

try {
$this->await($this->browser->get('http://127.0.0.1:8080/', [
'Host' => 'tunnel.share.beyondco.de',
]));
} catch (ResponseException $e) {
$response = $e->getResponse();

$this->assertStringContainsString('<h1>Custom 404 for tunnel</h1>', $response->getBody()->getContents());
}
}

/** @test */
public function it_sends_incoming_requests_to_the_connected_client()
{
Expand Down