5 Commits

Author SHA1 Message Date
Marcel Pociot
ff232d9ef4 Merge branch '1.0' of github.com:beyondcode/phunnel into 1.0 2020-12-07 23:31:08 +01:00
Marcel Pociot
5b8cc4d985 Allow specifying server host and port in share command 2020-12-07 23:30:57 +01:00
Marcel Pociot
26a805c552 Merge pull request #164 from beyondcode/analysis-gOmLGw
Apply fixes from StyleCI
2020-12-05 00:34:31 +01:00
Marcel Pociot
28cc353c30 Apply fixes from StyleCI 2020-12-04 23:34:24 +00:00
Marcel Pociot
7bfb618d93 wip 2020-12-05 00:34:01 +01:00
17 changed files with 21 additions and 384 deletions

View File

@@ -21,3 +21,4 @@ ENV password=password
ENV exposeConfigPath=/src/config/expose.php ENV exposeConfigPath=/src/config/expose.php
CMD sed -i "s|username|${username}|g" ${exposeConfigPath} && sed -i "s|password|${password}|g" ${exposeConfigPath} && php expose serve ${domain} --port ${port} --validateAuthTokens CMD sed -i "s|username|${username}|g" ${exposeConfigPath} && sed -i "s|password|${password}|g" ${exposeConfigPath} && php expose serve ${domain} --port ${port} --validateAuthTokens
ENTRYPOINT ["/src/expose"]

View File

@@ -45,7 +45,7 @@ class Client
$sharedUrl = $this->prepareSharedUrl($sharedUrl); $sharedUrl = $this->prepareSharedUrl($sharedUrl);
foreach ($subdomains as $subdomain) { foreach ($subdomains as $subdomain) {
$this->connectToServer($sharedUrl, $subdomain, config('expose.auth_token')); $this->connectToServer($sharedUrl, $subdomain, $this->configuration->auth());
} }
} }

View File

@@ -66,7 +66,6 @@ class HttpClient
protected function createConnector(): Connector protected function createConnector(): Connector
{ {
return new Connector($this->loop, [ return new Connector($this->loop, [
'dns' => '127.0.0.1',
'tls' => [ 'tls' => [
'verify_peer' => false, 'verify_peer' => false,
'verify_peer_name' => false, 'verify_peer_name' => false,

View File

@@ -10,7 +10,7 @@ use Symfony\Component\Console\Output\ConsoleOutput;
class ShareCommand extends Command class ShareCommand extends Command
{ {
protected $signature = 'share {host} {--subdomain=} {--auth=}'; protected $signature = 'share {host} {--subdomain=} {--auth=} {--server-host=} {--server-port=}';
protected $description = 'Share a local url with a remote expose server'; protected $description = 'Share a local url with a remote expose server';
@@ -27,11 +27,15 @@ class ShareCommand extends Command
{ {
$this->configureConnectionLogger(); $this->configureConnectionLogger();
$serverHost = $this->option('server-host') ?? config('expose.host', 'localhost');
$serverPort = $this->option('server-port') ?? config('expose.port', 8080);
$auth = $this->option('auth') ?? config('expose.auth_token', '');
(new Factory()) (new Factory())
->setLoop(app(LoopInterface::class)) ->setLoop(app(LoopInterface::class))
->setHost(config('expose.host', 'localhost')) ->setHost($serverHost)
->setPort(config('expose.port', 8080)) ->setPort($serverPort)
->setAuth($this->option('auth')) ->setAuth($auth)
->createClient() ->createClient()
->share($this->argument('host'), explode(',', $this->option('subdomain'))) ->share($this->argument('host'), explode(',', $this->option('subdomain')))
->createHttpServer() ->createHttpServer()

View File

@@ -8,12 +8,12 @@ class ShareCurrentWorkingDirectoryCommand extends ShareCommand
public function handle() public function handle()
{ {
$subdomain = $this->detectName(); $host = $this->prepareSharedHost(basename(getcwd()).'.'.$this->detectTld());
$host = $this->prepareSharedHost($subdomain.'.'.$this->detectTld());
$this->input->setArgument('host', $host); $this->input->setArgument('host', $host);
if (! $this->option('subdomain')) { if (! $this->option('subdomain')) {
$subdomain = str_replace('.', '-', basename(getcwd()));
$this->input->setOption('subdomain', $subdomain); $this->input->setOption('subdomain', $subdomain);
} }
@@ -33,32 +33,6 @@ class ShareCurrentWorkingDirectoryCommand extends ShareCommand
return config('expose.default_tld', 'test'); return config('expose.default_tld', 'test');
} }
protected function detectName(): string
{
$projectPath = getcwd();
$valetSitesPath = ($_SERVER['HOME'] ?? $_SERVER['USERPROFILE']).DIRECTORY_SEPARATOR.'.config'.DIRECTORY_SEPARATOR.'valet'.DIRECTORY_SEPARATOR.'Sites';
if (is_dir($valetSitesPath)) {
$site = collect(scandir($valetSitesPath))
->skip(2)
->map(function ($site) use ($valetSitesPath) {
return $valetSitesPath.DIRECTORY_SEPARATOR.$site;
})->mapWithKeys(function ($site) {
return [$site => readlink($site)];
})->filter(function ($sourcePath) use ($projectPath) {
return $sourcePath === $projectPath;
})
->keys()
->first();
if ($site) {
$projectPath = $site;
}
}
return str_replace('.', '-', basename($projectPath));
}
protected function prepareSharedHost($host): string protected function prepareSharedHost($host): string
{ {
$certificateFile = ($_SERVER['HOME'] ?? $_SERVER['USERPROFILE']).DIRECTORY_SEPARATOR.'.config'.DIRECTORY_SEPARATOR.'valet'.DIRECTORY_SEPARATOR.'Certificates'.DIRECTORY_SEPARATOR.$host.'.crt'; $certificateFile = ($_SERVER['HOME'] ?? $_SERVER['USERPROFILE']).DIRECTORY_SEPARATOR.'.config'.DIRECTORY_SEPARATOR.'valet'.DIRECTORY_SEPARATOR.'Certificates'.DIRECTORY_SEPARATOR.$host.'.crt';

View File

@@ -23,6 +23,4 @@ interface ConnectionManager
public function findControlConnectionForClientId(string $clientId): ?ControlConnection; public function findControlConnectionForClientId(string $clientId): ?ControlConnection;
public function getConnections(): array; public function getConnections(): array;
public function getConnectionsForAuthToken(string $authToken): array;
} }

View File

@@ -308,12 +308,6 @@ class LoggedRequest implements \JsonSerializable
protected function getRequestAsCurl(): string protected function getRequestAsCurl(): string
{ {
$maxRequestLength = 256000;
if (strlen($this->rawRequest) > $maxRequestLength) {
return '';
}
try { try {
return (new CurlFormatter())->format(parse_request($this->rawRequest)); return (new CurlFormatter())->format(parse_request($this->rawRequest));
} catch (\Throwable $e) { } catch (\Throwable $e) {

View File

@@ -4,7 +4,6 @@ namespace App\Server\Connections;
use App\Contracts\ConnectionManager as ConnectionManagerContract; use App\Contracts\ConnectionManager as ConnectionManagerContract;
use App\Contracts\SubdomainGenerator; use App\Contracts\SubdomainGenerator;
use App\Http\QueryParameters;
use Ratchet\ConnectionInterface; use Ratchet\ConnectionInterface;
use React\EventLoop\LoopInterface; use React\EventLoop\LoopInterface;
@@ -47,13 +46,7 @@ class ConnectionManager implements ConnectionManagerContract
$connection->client_id = $clientId; $connection->client_id = $clientId;
$storedConnection = new ControlConnection( $storedConnection = new ControlConnection($connection, $host, $subdomain ?? $this->subdomainGenerator->generateSubdomain(), $clientId);
$connection,
$host,
$subdomain ?? $this->subdomainGenerator->generateSubdomain(),
$clientId,
$this->getAuthTokenFromConnection($connection)
);
$this->connections[] = $storedConnection; $this->connections[] = $storedConnection;
@@ -106,21 +99,4 @@ class ConnectionManager implements ConnectionManagerContract
{ {
return $this->connections; return $this->connections;
} }
protected function getAuthTokenFromConnection(ConnectionInterface $connection): string
{
return QueryParameters::create($connection->httpRequest)->get('authToken');
}
public function getConnectionsForAuthToken(string $authToken): array
{
return collect($this->connections)
->filter(function ($connection) use ($authToken) {
return $connection->authToken === $authToken;
})
->map(function ($connection) {
return $connection->toArray();
})
->toArray();
}
} }

View File

@@ -12,19 +12,17 @@ class ControlConnection
/** @var ConnectionInterface */ /** @var ConnectionInterface */
public $socket; public $socket;
public $host; public $host;
public $authToken;
public $subdomain; public $subdomain;
public $client_id; public $client_id;
public $proxies = []; public $proxies = [];
protected $shared_at; protected $shared_at;
public function __construct(ConnectionInterface $socket, string $host, string $subdomain, string $clientId, string $authToken = '') public function __construct(ConnectionInterface $socket, string $host, string $subdomain, string $clientId)
{ {
$this->socket = $socket; $this->socket = $socket;
$this->host = $host; $this->host = $host;
$this->subdomain = $subdomain; $this->subdomain = $subdomain;
$this->client_id = $clientId; $this->client_id = $clientId;
$this->authToken = $authToken;
$this->shared_at = now()->toDateTimeString(); $this->shared_at = now()->toDateTimeString();
} }
@@ -59,7 +57,6 @@ class ControlConnection
return [ return [
'host' => $this->host, 'host' => $this->host,
'client_id' => $this->client_id, 'client_id' => $this->client_id,
'auth_token' => $this->authToken,
'subdomain' => $this->subdomain, 'subdomain' => $this->subdomain,
'shared_at' => $this->shared_at, 'shared_at' => $this->shared_at,
]; ];

View File

@@ -12,7 +12,6 @@ use App\Server\Http\Controllers\Admin\DeleteUsersController;
use App\Server\Http\Controllers\Admin\DisconnectSiteController; use App\Server\Http\Controllers\Admin\DisconnectSiteController;
use App\Server\Http\Controllers\Admin\GetSettingsController; use App\Server\Http\Controllers\Admin\GetSettingsController;
use App\Server\Http\Controllers\Admin\GetSitesController; use App\Server\Http\Controllers\Admin\GetSitesController;
use App\Server\Http\Controllers\Admin\GetUserDetailsController;
use App\Server\Http\Controllers\Admin\GetUsersController; use App\Server\Http\Controllers\Admin\GetUsersController;
use App\Server\Http\Controllers\Admin\ListSitesController; use App\Server\Http\Controllers\Admin\ListSitesController;
use App\Server\Http\Controllers\Admin\ListUsersController; use App\Server\Http\Controllers\Admin\ListUsersController;
@@ -125,7 +124,6 @@ class Factory
$this->router->post('/api/settings', StoreSettingsController::class, $adminCondition); $this->router->post('/api/settings', StoreSettingsController::class, $adminCondition);
$this->router->get('/api/users', GetUsersController::class, $adminCondition); $this->router->get('/api/users', GetUsersController::class, $adminCondition);
$this->router->post('/api/users', StoreUsersController::class, $adminCondition); $this->router->post('/api/users', StoreUsersController::class, $adminCondition);
$this->router->get('/api/users/{id}', GetUserDetailsController::class, $adminCondition);
$this->router->delete('/api/users/{id}', DeleteUsersController::class, $adminCondition); $this->router->delete('/api/users/{id}', DeleteUsersController::class, $adminCondition);
$this->router->get('/api/sites', GetSitesController::class, $adminCondition); $this->router->get('/api/sites', GetSitesController::class, $adminCondition);
$this->router->delete('/api/sites/{id}', DisconnectSiteController::class, $adminCondition); $this->router->delete('/api/sites/{id}', DisconnectSiteController::class, $adminCondition);

View File

@@ -1,33 +0,0 @@
<?php
namespace App\Server\Http\Controllers\Admin;
use App\Contracts\UserRepository;
use Illuminate\Http\Request;
use Ratchet\ConnectionInterface;
class GetUserDetailsController extends AdminController
{
protected $keepConnectionOpen = true;
/** @var UserRepository */
protected $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function handle(Request $request, ConnectionInterface $httpConnection)
{
$this->userRepository
->getUserById($request->get('id'))
->then(function ($user) use ($httpConnection) {
$httpConnection->send(
respond_json(['user' => $user])
);
$httpConnection->close();
});
}
}

View File

@@ -8,6 +8,7 @@ use App\Http\QueryParameters;
use Ratchet\ConnectionInterface; use Ratchet\ConnectionInterface;
use Ratchet\WebSocket\MessageComponentInterface; use Ratchet\WebSocket\MessageComponentInterface;
use React\Promise\Deferred; use React\Promise\Deferred;
use React\Promise\FulfilledPromise;
use React\Promise\PromiseInterface; use React\Promise\PromiseInterface;
use stdClass; use stdClass;
@@ -126,7 +127,7 @@ class ControlMessageController implements MessageComponentInterface
protected function verifyAuthToken(ConnectionInterface $connection): PromiseInterface protected function verifyAuthToken(ConnectionInterface $connection): PromiseInterface
{ {
if (config('expose.admin.validate_auth_tokens') !== true) { if (config('expose.admin.validate_auth_tokens') !== true) {
return \React\Promise\resolve(null); return new FulfilledPromise();
} }
$deferred = new Deferred(); $deferred = new Deferred();

View File

@@ -75,7 +75,7 @@ class TunnelMessageController extends Controller
$httpConnection = $this->connectionManager->storeHttpConnection($httpConnection, $requestId); $httpConnection = $this->connectionManager->storeHttpConnection($httpConnection, $requestId);
transform($this->passRequestThroughModifiers($request, $httpConnection), function (Request $request) use ($controlConnection , $requestId) { transform($this->passRequestThroughModifiers($request, $httpConnection), function (Request $request) use ($controlConnection, $requestId) {
$controlConnection->once('proxy_ready_'.$requestId, function (ConnectionInterface $proxy) use ($request) { $controlConnection->once('proxy_ready_'.$requestId, function (ConnectionInterface $proxy) use ($request) {
// Convert the Laravel request into a PSR7 request // Convert the Laravel request into a PSR7 request
$psr17Factory = new Psr17Factory(); $psr17Factory = new Psr17Factory();

View File

@@ -2,7 +2,6 @@
namespace App\Server\UserRepository; namespace App\Server\UserRepository;
use App\Contracts\ConnectionManager;
use App\Contracts\UserRepository; use App\Contracts\UserRepository;
use Clue\React\SQLite\DatabaseInterface; use Clue\React\SQLite\DatabaseInterface;
use Clue\React\SQLite\Result; use Clue\React\SQLite\Result;
@@ -14,13 +13,9 @@ class DatabaseUserRepository implements UserRepository
/** @var DatabaseInterface */ /** @var DatabaseInterface */
protected $database; protected $database;
/** @var ConnectionManager */ public function __construct(DatabaseInterface $database)
protected $connectionManager;
public function __construct(DatabaseInterface $database, ConnectionManager $connectionManager)
{ {
$this->database = $database; $this->database = $database;
$this->connectionManager = $connectionManager;
} }
public function getUsers(): PromiseInterface public function getUsers(): PromiseInterface
@@ -51,12 +46,8 @@ class DatabaseUserRepository implements UserRepository
$nextPage = $currentPage + 1; $nextPage = $currentPage + 1;
} }
$users = collect($result->rows)->map(function ($user) {
return $this->getUserDetails($user);
})->toArray();
$paginated = [ $paginated = [
'users' => $users, 'users' => $result->rows,
'current_page' => $currentPage, 'current_page' => $currentPage,
'per_page' => $perPage, 'per_page' => $perPage,
'next_page' => $nextPage ?? null, 'next_page' => $nextPage ?? null,
@@ -69,13 +60,6 @@ class DatabaseUserRepository implements UserRepository
return $deferred->promise(); return $deferred->promise();
} }
protected function getUserDetails(array $user)
{
$user['sites'] = $user['auth_token'] !== '' ? $this->connectionManager->getConnectionsForAuthToken($user['auth_token']) : [];
return $user;
}
public function getUserById($id): PromiseInterface public function getUserById($id): PromiseInterface
{ {
$deferred = new Deferred(); $deferred = new Deferred();
@@ -83,13 +67,7 @@ class DatabaseUserRepository implements UserRepository
$this->database $this->database
->query('SELECT * FROM users WHERE id = :id', ['id' => $id]) ->query('SELECT * FROM users WHERE id = :id', ['id' => $id])
->then(function (Result $result) use ($deferred) { ->then(function (Result $result) use ($deferred) {
$user = $result->rows[0] ?? null; $deferred->resolve($result->rows[0] ?? null);
if (! is_null($user)) {
$user = $this->getUserDetails($user);
}
$deferred->resolve($user);
}); });
return $deferred->promise(); return $deferred->promise();

View File

@@ -7,9 +7,7 @@ order: 2
Once your Expose server is running, you can only access it over the port that you configure when the server gets started. Once your Expose server is running, you can only access it over the port that you configure when the server gets started.
If you want to enable SSL support, you will need to use a proxy service - like Nginx, HAProxy, Apache2 or Caddy - to handle the SSL configurations and proxy all non-SSL requests to your expose server. If you want to enable SSL support, you will need to use a proxy service - like Nginx, HAProxy or Caddy - to handle the SSL configurations and proxy all non-SSL requests to your expose server.
## Nginx configuration
A basic Nginx configuration would look like this, but you might want to tweak the SSL parameters to your liking. A basic Nginx configuration would look like this, but you might want to tweak the SSL parameters to your liking.
@@ -42,47 +40,3 @@ server {
} }
} }
``` ```
## Apache2 configuration
A basic Apache configuration would look like this, but you might want to tweak the SSL parameters to your liking.
```
Listen 80
Listen 443
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName expose.domain.tld
ServerAlias *.expose.domain.tld
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
ServerAdmin admin@domain.tld
ProxyPass "/" "http://localhost:8080/"
ProxyPassReverse "/" "http://localhost:8080/"
ProxyPreserveHost On
# Needed for websocket support
RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC,OR]
RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
RewriteRule .* ws://127.0.0.1:8080%{REQUEST_URI} [P,QSA,L]
<Proxy http://localhost:8080>
Require all granted
Options none
</Proxy>
ErrorLog ${APACHE_LOG_DIR}/expose.domain.tld-error.log
CustomLog ${APACHE_LOG_DIR}/expose.domain.tld-access.log combined
SSLCertificateFile /etc/letsencrypt/live/expose.domain.tld-0001/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/expose.domain.tld-0001/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>
```

View File

@@ -8,7 +8,6 @@ use Clue\React\Buzz\Browser;
use Clue\React\Buzz\Message\ResponseException; use Clue\React\Buzz\Message\ResponseException;
use GuzzleHttp\Psr7\Response; use GuzzleHttp\Psr7\Response;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Nyholm\Psr7\Request;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Ratchet\Server\IoConnection; use Ratchet\Server\IoConnection;
use Tests\Feature\TestCase; use Tests\Feature\TestCase;
@@ -150,8 +149,6 @@ class AdminTest extends TestCase
$connectionManager = app(ConnectionManager::class); $connectionManager = app(ConnectionManager::class);
$connection = \Mockery::mock(IoConnection::class); $connection = \Mockery::mock(IoConnection::class);
$connection->httpRequest = new Request('GET', '/?authToken=some-token');
$connectionManager->storeConnection('some-host.text', 'fixed-subdomain', $connection); $connectionManager->storeConnection('some-host.text', 'fixed-subdomain', $connection);
/** @var Response $response */ /** @var Response $response */

View File

@@ -1,201 +0,0 @@
<?php
namespace Tests\Feature\Server;
use App\Contracts\ConnectionManager;
use App\Server\Factory;
use Clue\React\Buzz\Browser;
use GuzzleHttp\Psr7\Response;
use Nyholm\Psr7\Request;
use Ratchet\Server\IoConnection;
use Tests\Feature\TestCase;
class ApiTest extends TestCase
{
/** @var Browser */
protected $browser;
/** @var Factory */
protected $serverFactory;
public function setUp(): void
{
parent::setUp();
$this->browser = new Browser($this->loop);
$this->browser = $this->browser->withOptions([
'followRedirects' => false,
]);
$this->startServer();
}
public function tearDown(): void
{
$this->serverFactory->getSocket()->close();
parent::tearDown();
}
/** @test */
public function it_can_list_all_registered_users()
{
/** @var Response $response */
$this->await($this->browser->post('http://127.0.0.1:8080/api/users', [
'Host' => 'expose.localhost',
'Authorization' => base64_encode('username:secret'),
'Content-Type' => 'application/json',
], json_encode([
'name' => 'Marcel',
])));
/** @var Response $response */
$response = $this->await($this->browser->get('http://127.0.0.1:8080/api/users', [
'Host' => 'expose.localhost',
'Authorization' => base64_encode('username:secret'),
'Content-Type' => 'application/json',
]));
$body = json_decode($response->getBody()->getContents());
$users = $body->paginated->users;
$this->assertCount(1, $users);
$this->assertSame('Marcel', $users[0]->name);
$this->assertSame([], $users[0]->sites);
}
/** @test */
public function it_can_get_user_details()
{
/** @var Response $response */
$this->await($this->browser->post('http://127.0.0.1:8080/api/users', [
'Host' => 'expose.localhost',
'Authorization' => base64_encode('username:secret'),
'Content-Type' => 'application/json',
], json_encode([
'name' => 'Marcel',
])));
/** @var Response $response */
$response = $this->await($this->browser->get('http://127.0.0.1:8080/api/users/1', [
'Host' => 'expose.localhost',
'Authorization' => base64_encode('username:secret'),
'Content-Type' => 'application/json',
]));
$body = json_decode($response->getBody()->getContents());
$user = $body->user;
$this->assertSame('Marcel', $user->name);
$this->assertSame([], $user->sites);
}
/** @test */
public function it_can_list_all_currently_connected_sites_from_all_users()
{
/** @var Response $response */
$response = $this->await($this->browser->post('http://127.0.0.1:8080/api/users', [
'Host' => 'expose.localhost',
'Authorization' => base64_encode('username:secret'),
'Content-Type' => 'application/json',
], json_encode([
'name' => 'Marcel',
])));
$createdUser = json_decode($response->getBody()->getContents())->user;
/** @var ConnectionManager $connectionManager */
$connectionManager = app(ConnectionManager::class);
$connection = \Mockery::mock(IoConnection::class);
$connection->httpRequest = new Request('GET', '/?authToken='.$createdUser->auth_token);
$connectionManager->storeConnection('some-host.test', 'fixed-subdomain', $connection);
$connection = \Mockery::mock(IoConnection::class);
$connection->httpRequest = new Request('GET', '/?authToken=some-other-token');
$connectionManager->storeConnection('some-different-host.test', 'different-subdomain', $connection);
/** @var Response $response */
$response = $this->await($this->browser->get('http://127.0.0.1:8080/api/users', [
'Host' => 'expose.localhost',
'Authorization' => base64_encode('username:secret'),
'Content-Type' => 'application/json',
]));
$body = json_decode($response->getBody()->getContents());
$users = $body->paginated->users;
$this->assertCount(1, $users[0]->sites);
$this->assertSame('some-host.test', $users[0]->sites[0]->host);
$this->assertSame('fixed-subdomain', $users[0]->sites[0]->subdomain);
}
/** @test */
public function it_can_list_all_currently_connected_sites()
{
/** @var ConnectionManager $connectionManager */
$connectionManager = app(ConnectionManager::class);
$connection = \Mockery::mock(IoConnection::class);
$connection->httpRequest = new Request('GET', '/?authToken=some-token');
$connectionManager->storeConnection('some-host.test', 'fixed-subdomain', $connection);
/** @var Response $response */
$response = $this->await($this->browser->get('http://127.0.0.1:8080/api/sites', [
'Host' => 'expose.localhost',
'Authorization' => base64_encode('username:secret'),
'Content-Type' => 'application/json',
]));
$body = json_decode($response->getBody()->getContents());
$sites = $body->sites;
$this->assertCount(1, $sites);
$this->assertSame('some-host.test', $sites[0]->host);
$this->assertSame('some-token', $sites[0]->auth_token);
$this->assertSame('fixed-subdomain', $sites[0]->subdomain);
}
/** @test */
public function it_can_list_all_currently_connected_sites_without_auth_tokens()
{
/** @var ConnectionManager $connectionManager */
$connectionManager = app(ConnectionManager::class);
$connection = \Mockery::mock(IoConnection::class);
$connection->httpRequest = new Request('GET', '/');
$connectionManager->storeConnection('some-host.test', 'fixed-subdomain', $connection);
/** @var Response $response */
$response = $this->await($this->browser->get('http://127.0.0.1:8080/api/sites', [
'Host' => 'expose.localhost',
'Authorization' => base64_encode('username:secret'),
'Content-Type' => 'application/json',
]));
$body = json_decode($response->getBody()->getContents());
$sites = $body->sites;
$this->assertCount(1, $sites);
$this->assertSame('some-host.test', $sites[0]->host);
$this->assertSame('', $sites[0]->auth_token);
$this->assertSame('fixed-subdomain', $sites[0]->subdomain);
}
protected function startServer()
{
$this->app['config']['expose.admin.subdomain'] = 'expose';
$this->app['config']['expose.admin.database'] = ':memory:';
$this->app['config']['expose.admin.users'] = [
'username' => 'secret',
];
$this->serverFactory = new Factory();
$this->serverFactory->setLoop($this->loop)
->createServer();
}
}