diff --git a/app/Commands/ShareCurrentWorkingDirectoryCommand.php b/app/Commands/ShareCurrentWorkingDirectoryCommand.php
index 6574e88..e6e117b 100644
--- a/app/Commands/ShareCurrentWorkingDirectoryCommand.php
+++ b/app/Commands/ShareCurrentWorkingDirectoryCommand.php
@@ -8,12 +8,12 @@ class ShareCurrentWorkingDirectoryCommand extends ShareCommand
public function handle()
{
- $host = $this->prepareSharedHost(basename(getcwd()).'.'.$this->detectTld());
+ $subdomain = $this->detectName();
+ $host = $this->prepareSharedHost($subdomain.'.'.$this->detectTld());
$this->input->setArgument('host', $host);
if (! $this->option('subdomain')) {
- $subdomain = str_replace('.', '-', basename(getcwd()));
$this->input->setOption('subdomain', $subdomain);
}
@@ -33,6 +33,32 @@ class ShareCurrentWorkingDirectoryCommand extends ShareCommand
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
{
$certificateFile = ($_SERVER['HOME'] ?? $_SERVER['USERPROFILE']).DIRECTORY_SEPARATOR.'.config'.DIRECTORY_SEPARATOR.'valet'.DIRECTORY_SEPARATOR.'Certificates'.DIRECTORY_SEPARATOR.$host.'.crt';
diff --git a/app/Contracts/ConnectionManager.php b/app/Contracts/ConnectionManager.php
index 393b97f..25ab69d 100644
--- a/app/Contracts/ConnectionManager.php
+++ b/app/Contracts/ConnectionManager.php
@@ -25,4 +25,6 @@ interface ConnectionManager
public function findControlConnectionForClientId(string $clientId): ?ControlConnection;
public function getConnections(): array;
+
+ public function getConnectionsForAuthToken(string $authToken): array;
}
diff --git a/app/Server/Connections/ConnectionManager.php b/app/Server/Connections/ConnectionManager.php
index 2544901..3b62e6e 100644
--- a/app/Server/Connections/ConnectionManager.php
+++ b/app/Server/Connections/ConnectionManager.php
@@ -4,6 +4,7 @@ namespace App\Server\Connections;
use App\Contracts\ConnectionManager as ConnectionManagerContract;
use App\Contracts\SubdomainGenerator;
+use App\Http\QueryParameters;
use Ratchet\ConnectionInterface;
use React\EventLoop\LoopInterface;
use React\Socket\Server;
@@ -47,7 +48,13 @@ class ConnectionManager implements ConnectionManagerContract
$connection->client_id = $clientId;
- $storedConnection = new ControlConnection($connection, $host, $subdomain ?? $this->subdomainGenerator->generateSubdomain(), $clientId);
+ $storedConnection = new ControlConnection(
+ $connection,
+ $host,
+ $subdomain ?? $this->subdomainGenerator->generateSubdomain(),
+ $clientId,
+ $this->getAuthTokenFromConnection($connection)
+ );
$this->connections[] = $storedConnection;
@@ -60,7 +67,13 @@ class ConnectionManager implements ConnectionManagerContract
$connection->client_id = $clientId;
- $storedConnection = new TcpControlConnection($connection, $port, $this->getSharedTcpServer(), $clientId);
+ $storedConnection = new TcpControlConnection(
+ $connection,
+ $port,
+ $this->getSharedTcpServer(),
+ $clientId,
+ $this->getAuthTokenFromConnection($connection)
+ );
$this->connections[] = $storedConnection;
@@ -118,4 +131,21 @@ class ConnectionManager implements ConnectionManagerContract
{
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();
+ }
}
diff --git a/app/Server/Connections/ControlConnection.php b/app/Server/Connections/ControlConnection.php
index cba75fc..b95c8a9 100644
--- a/app/Server/Connections/ControlConnection.php
+++ b/app/Server/Connections/ControlConnection.php
@@ -12,17 +12,19 @@ class ControlConnection
/** @var ConnectionInterface */
public $socket;
public $host;
+ public $authToken;
public $subdomain;
public $client_id;
public $proxies = [];
protected $shared_at;
- public function __construct(ConnectionInterface $socket, string $host, string $subdomain, string $clientId)
+ public function __construct(ConnectionInterface $socket, string $host, string $subdomain, string $clientId, string $authToken = '')
{
$this->socket = $socket;
$this->host = $host;
$this->subdomain = $subdomain;
$this->client_id = $clientId;
+ $this->authToken = $authToken;
$this->shared_at = now()->toDateTimeString();
}
@@ -58,6 +60,7 @@ class ControlConnection
'type' => 'http',
'host' => $this->host,
'client_id' => $this->client_id,
+ 'auth_token' => $this->authToken,
'subdomain' => $this->subdomain,
'shared_at' => $this->shared_at,
];
diff --git a/app/Server/Connections/TcpControlConnection.php b/app/Server/Connections/TcpControlConnection.php
index 93c1c2b..bf6b5dd 100644
--- a/app/Server/Connections/TcpControlConnection.php
+++ b/app/Server/Connections/TcpControlConnection.php
@@ -14,7 +14,7 @@ class TcpControlConnection extends ControlConnection
public $shared_port;
public $shared_server;
- public function __construct(ConnectionInterface $socket, int $port, Server $sharedServer, string $clientId)
+ public function __construct(ConnectionInterface $socket, int $port, Server $sharedServer, string $clientId, string $authToken = '')
{
$this->socket = $socket;
$this->client_id = $clientId;
@@ -22,6 +22,7 @@ class TcpControlConnection extends ControlConnection
$this->port = $port;
$this->shared_at = now()->toDateTimeString();
$this->shared_port = parse_url($sharedServer->getAddress(), PHP_URL_PORT);
+ $this->authToken = $authToken;
$this->configureServer($sharedServer);
}
@@ -85,7 +86,6 @@ class TcpControlConnection extends ControlConnection
$this->once('tcp_proxy_ready_'.$requestId, function (ConnectionInterface $proxy) use ($connection) {
$this->proxy = $proxy;
- dump("Proxy ready");
$connection->on('data', function($data) use ($proxy) {
$proxy->send($data);
diff --git a/app/Server/Factory.php b/app/Server/Factory.php
index 88d4088..29dbf90 100644
--- a/app/Server/Factory.php
+++ b/app/Server/Factory.php
@@ -12,6 +12,7 @@ use App\Server\Http\Controllers\Admin\DeleteUsersController;
use App\Server\Http\Controllers\Admin\DisconnectSiteController;
use App\Server\Http\Controllers\Admin\GetSettingsController;
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\ListSitesController;
use App\Server\Http\Controllers\Admin\ListUsersController;
@@ -124,6 +125,7 @@ class Factory
$this->router->post('/api/settings', StoreSettingsController::class, $adminCondition);
$this->router->get('/api/users', GetUsersController::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->get('/api/sites', GetSitesController::class, $adminCondition);
$this->router->delete('/api/sites/{id}', DisconnectSiteController::class, $adminCondition);
diff --git a/app/Server/Http/Controllers/Admin/GetUserDetailsController.php b/app/Server/Http/Controllers/Admin/GetUserDetailsController.php
new file mode 100644
index 0000000..2cb3a52
--- /dev/null
+++ b/app/Server/Http/Controllers/Admin/GetUserDetailsController.php
@@ -0,0 +1,33 @@
+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();
+ });
+ }
+}
diff --git a/app/Server/Http/Controllers/Admin/StoreUsersController.php b/app/Server/Http/Controllers/Admin/StoreUsersController.php
index 3b78fd9..14e3ff8 100644
--- a/app/Server/Http/Controllers/Admin/StoreUsersController.php
+++ b/app/Server/Http/Controllers/Admin/StoreUsersController.php
@@ -39,6 +39,7 @@ class StoreUsersController extends AdminController
$insertData = [
'name' => $request->get('name'),
'auth_token' => (string) Str::uuid(),
+ 'can_specify_subdomains' => (int) $request->get('can_specify_subdomains'),
];
$this->userRepository
diff --git a/app/Server/Http/Controllers/ControlMessageController.php b/app/Server/Http/Controllers/ControlMessageController.php
index 5d0c451..b525182 100644
--- a/app/Server/Http/Controllers/ControlMessageController.php
+++ b/app/Server/Http/Controllers/ControlMessageController.php
@@ -8,7 +8,6 @@ use App\Http\QueryParameters;
use Ratchet\ConnectionInterface;
use Ratchet\WebSocket\MessageComponentInterface;
use React\Promise\Deferred;
-use React\Promise\FulfilledPromise;
use React\Promise\PromiseInterface;
use stdClass;
@@ -81,11 +80,11 @@ class ControlMessageController implements MessageComponentInterface
protected function authenticate(ConnectionInterface $connection, $data)
{
$this->verifyAuthToken($connection)
- ->then(function () use ($connection, $data) {
+ ->then(function ($user) use ($connection, $data) {
if ($data->type === 'http') {
- $this->handleHttpConnection($connection, $data);
+ $this->handleHttpConnection($connection, $data, $user);
} elseif($data->type === 'tcp') {
- $this->handleTcpConnection($connection, $data);
+ $this->handleTcpConnection($connection, $data, $user);
}
}, function () use ($connection) {
$connection->send(json_encode([
@@ -98,9 +97,9 @@ class ControlMessageController implements MessageComponentInterface
});
}
- protected function handleHttpConnection(ConnectionInterface $connection, $data)
+ protected function handleHttpConnection(ConnectionInterface $connection, $data, $user = null)
{
- if (! $this->hasValidSubdomain($connection, $data->subdomain)) {
+ if (! $this->hasValidSubdomain($connection, $data->subdomain, $user)) {
return;
}
@@ -118,7 +117,7 @@ class ControlMessageController implements MessageComponentInterface
]));
}
- protected function handleTcpConnection(ConnectionInterface $connection, $data)
+ protected function handleTcpConnection(ConnectionInterface $connection, $data, $user = null)
{
$connectionInfo = $this->connectionManager->storeTcpConnection($data->port, $connection);
@@ -167,7 +166,7 @@ class ControlMessageController implements MessageComponentInterface
protected function verifyAuthToken(ConnectionInterface $connection): PromiseInterface
{
if (config('expose.admin.validate_auth_tokens') !== true) {
- return new FulfilledPromise();
+ return \React\Promise\resolve(null);
}
$deferred = new Deferred();
@@ -187,8 +186,20 @@ class ControlMessageController implements MessageComponentInterface
return $deferred->promise();
}
- protected function hasValidSubdomain(ConnectionInterface $connection, ?string $subdomain): bool
+ protected function hasValidSubdomain(ConnectionInterface $connection, ?string $subdomain, ?array $user): bool
{
+ if (! is_null($user) && $user['can_specify_subdomains'] === 0 && ! is_null($subdomain)) {
+ $connection->send(json_encode([
+ 'event' => 'subdomainTaken',
+ 'data' => [
+ 'message' => config('expose.admin.messages.custom_subdomain_unauthorized'),
+ ],
+ ]));
+ $connection->close();
+
+ return false;
+ }
+
if (! is_null($subdomain)) {
$controlConnection = $this->connectionManager->findControlConnectionForSubdomain($subdomain);
if (! is_null($controlConnection) || $subdomain === config('expose.admin.subdomain')) {
diff --git a/app/Server/UserRepository/DatabaseUserRepository.php b/app/Server/UserRepository/DatabaseUserRepository.php
index 973e798..43b15b1 100644
--- a/app/Server/UserRepository/DatabaseUserRepository.php
+++ b/app/Server/UserRepository/DatabaseUserRepository.php
@@ -2,6 +2,7 @@
namespace App\Server\UserRepository;
+use App\Contracts\ConnectionManager;
use App\Contracts\UserRepository;
use Clue\React\SQLite\DatabaseInterface;
use Clue\React\SQLite\Result;
@@ -13,9 +14,13 @@ class DatabaseUserRepository implements UserRepository
/** @var DatabaseInterface */
protected $database;
- public function __construct(DatabaseInterface $database)
+ /** @var ConnectionManager */
+ protected $connectionManager;
+
+ public function __construct(DatabaseInterface $database, ConnectionManager $connectionManager)
{
$this->database = $database;
+ $this->connectionManager = $connectionManager;
}
public function getUsers(): PromiseInterface
@@ -46,8 +51,12 @@ class DatabaseUserRepository implements UserRepository
$nextPage = $currentPage + 1;
}
+ $users = collect($result->rows)->map(function ($user) {
+ return $this->getUserDetails($user);
+ })->toArray();
+
$paginated = [
- 'users' => $result->rows,
+ 'users' => $users,
'current_page' => $currentPage,
'per_page' => $perPage,
'next_page' => $nextPage ?? null,
@@ -60,6 +69,13 @@ class DatabaseUserRepository implements UserRepository
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
{
$deferred = new Deferred();
@@ -67,7 +83,13 @@ class DatabaseUserRepository implements UserRepository
$this->database
->query('SELECT * FROM users WHERE id = :id', ['id' => $id])
->then(function (Result $result) use ($deferred) {
- $deferred->resolve($result->rows[0] ?? null);
+ $user = $result->rows[0] ?? null;
+
+ if (! is_null($user)) {
+ $user = $this->getUserDetails($user);
+ }
+
+ $deferred->resolve($user);
});
return $deferred->promise();
@@ -91,8 +113,8 @@ class DatabaseUserRepository implements UserRepository
$deferred = new Deferred();
$this->database->query("
- INSERT INTO users (name, auth_token, created_at)
- VALUES (:name, :auth_token, DATETIME('now'))
+ INSERT INTO users (name, auth_token, can_specify_subdomains, created_at)
+ VALUES (:name, :auth_token, :can_specify_subdomains, DATETIME('now'))
", $data)
->then(function (Result $result) use ($deferred) {
$this->database->query('SELECT * FROM users WHERE id = :id', ['id' => $result->insertId])
diff --git a/composer.json b/composer.json
index 9e656fe..8ba0004 100644
--- a/composer.json
+++ b/composer.json
@@ -17,8 +17,7 @@
],
"require": {
"php": "^7.3.0",
- "ext-json": "*",
- "padraic/phar-updater": "^1.0.6"
+ "ext-json": "*"
},
"require-dev": {
"cboden/ratchet": "^0.4.2",
diff --git a/composer.lock b/composer.lock
index 7f6a2dc..df609b9 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,196 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "29e5e7d9a7d406f7d34ef09a3b2836e9",
- "packages": [
- {
- "name": "composer/ca-bundle",
- "version": "1.2.7",
- "source": {
- "type": "git",
- "url": "https://github.com/composer/ca-bundle.git",
- "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/composer/ca-bundle/zipball/95c63ab2117a72f48f5a55da9740a3273d45b7fd",
- "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd",
- "shasum": ""
- },
- "require": {
- "ext-openssl": "*",
- "ext-pcre": "*",
- "php": "^5.3.2 || ^7.0 || ^8.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8",
- "psr/log": "^1.0",
- "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Composer\\CaBundle\\": "src"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Jordi Boggiano",
- "email": "j.boggiano@seld.be",
- "homepage": "http://seld.be"
- }
- ],
- "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
- "keywords": [
- "cabundle",
- "cacert",
- "certificate",
- "ssl",
- "tls"
- ],
- "funding": [
- {
- "url": "https://packagist.com",
- "type": "custom"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/composer/composer",
- "type": "tidelift"
- }
- ],
- "time": "2020-04-08T08:27:21+00:00"
- },
- {
- "name": "padraic/humbug_get_contents",
- "version": "1.1.2",
- "source": {
- "type": "git",
- "url": "https://github.com/humbug/file_get_contents.git",
- "reference": "dcb086060c9dd6b2f51d8f7a895500307110b7a7"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/humbug/file_get_contents/zipball/dcb086060c9dd6b2f51d8f7a895500307110b7a7",
- "reference": "dcb086060c9dd6b2f51d8f7a895500307110b7a7",
- "shasum": ""
- },
- "require": {
- "composer/ca-bundle": "^1.0",
- "ext-openssl": "*",
- "php": "^5.3 || ^7.0 || ^7.1 || ^7.2"
- },
- "require-dev": {
- "bamarni/composer-bin-plugin": "^1.1",
- "mikey179/vfsstream": "^1.6",
- "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5"
- },
- "type": "library",
- "extra": {
- "bamarni-bin": {
- "bin-links": false
- },
- "branch-alias": {
- "dev-master": "2.0-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Humbug\\": "src/"
- },
- "files": [
- "src/function.php",
- "src/functions.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Pádraic Brady",
- "email": "padraic.brady@gmail.com",
- "homepage": "http://blog.astrumfutura.com"
- },
- {
- "name": "Théo Fidry",
- "email": "theo.fidry@gmail.com"
- }
- ],
- "description": "Secure wrapper for accessing HTTPS resources with file_get_contents for PHP 5.3+",
- "homepage": "https://github.com/padraic/file_get_contents",
- "keywords": [
- "download",
- "file_get_contents",
- "http",
- "https",
- "ssl",
- "tls"
- ],
- "time": "2018-02-12T18:47:17+00:00"
- },
- {
- "name": "padraic/phar-updater",
- "version": "v1.0.6",
- "source": {
- "type": "git",
- "url": "https://github.com/humbug/phar-updater.git",
- "reference": "d01d3b8f26e541ac9b9eeba1e18d005d852f7ff1"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/humbug/phar-updater/zipball/d01d3b8f26e541ac9b9eeba1e18d005d852f7ff1",
- "reference": "d01d3b8f26e541ac9b9eeba1e18d005d852f7ff1",
- "shasum": ""
- },
- "require": {
- "padraic/humbug_get_contents": "^1.0",
- "php": ">=5.3.3"
- },
- "require-dev": {
- "phpunit/phpunit": "~4.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Humbug\\SelfUpdate\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Pádraic Brady",
- "email": "padraic.brady@gmail.com",
- "homepage": "http://blog.astrumfutura.com"
- }
- ],
- "description": "A thing to make PHAR self-updating easy and secure.",
- "keywords": [
- "humbug",
- "phar",
- "self-update",
- "update"
- ],
- "time": "2018-03-30T12:52:15+00:00"
- }
- ],
+ "content-hash": "dd987a6f4f036893204c0d006d66001b",
+ "packages": [],
"packages-dev": [
{
"name": "cboden/ratchet",
@@ -478,6 +290,72 @@
},
"time": "2020-05-10T03:16:55+00:00"
},
+ {
+ "name": "composer/ca-bundle",
+ "version": "1.2.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/ca-bundle.git",
+ "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/ca-bundle/zipball/95c63ab2117a72f48f5a55da9740a3273d45b7fd",
+ "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd",
+ "shasum": ""
+ },
+ "require": {
+ "ext-openssl": "*",
+ "ext-pcre": "*",
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8",
+ "psr/log": "^1.0",
+ "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\CaBundle\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
+ "keywords": [
+ "cabundle",
+ "cacert",
+ "certificate",
+ "ssl",
+ "tls"
+ ],
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-04-08T08:27:21+00:00"
+ },
{
"name": "container-interop/container-interop",
"version": "1.2.0",
@@ -3258,6 +3136,75 @@
],
"time": "2020-05-23T11:29:07+00:00"
},
+ {
+ "name": "padraic/humbug_get_contents",
+ "version": "1.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/humbug/file_get_contents.git",
+ "reference": "dcb086060c9dd6b2f51d8f7a895500307110b7a7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/humbug/file_get_contents/zipball/dcb086060c9dd6b2f51d8f7a895500307110b7a7",
+ "reference": "dcb086060c9dd6b2f51d8f7a895500307110b7a7",
+ "shasum": ""
+ },
+ "require": {
+ "composer/ca-bundle": "^1.0",
+ "ext-openssl": "*",
+ "php": "^5.3 || ^7.0 || ^7.1 || ^7.2"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.1",
+ "mikey179/vfsstream": "^1.6",
+ "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": false
+ },
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Humbug\\": "src/"
+ },
+ "files": [
+ "src/function.php",
+ "src/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Pádraic Brady",
+ "email": "padraic.brady@gmail.com",
+ "homepage": "http://blog.astrumfutura.com"
+ },
+ {
+ "name": "Théo Fidry",
+ "email": "theo.fidry@gmail.com"
+ }
+ ],
+ "description": "Secure wrapper for accessing HTTPS resources with file_get_contents for PHP 5.3+",
+ "homepage": "https://github.com/padraic/file_get_contents",
+ "keywords": [
+ "download",
+ "file_get_contents",
+ "http",
+ "https",
+ "ssl",
+ "tls"
+ ],
+ "time": "2018-02-12T18:47:17+00:00"
+ },
{
"name": "paragonie/random_compat",
"version": "v9.99.99",
@@ -3981,6 +3928,7 @@
"keywords": [
"tokenizer"
],
+ "abandoned": true,
"time": "2019-09-17T06:23:10+00:00"
},
{
@@ -5060,12 +5008,12 @@
"source": {
"type": "git",
"url": "https://github.com/reactphp/socket.git",
- "reference": "842dcd71df86671ee9491734035b3d2cf4a80ece"
+ "reference": "e2b96b23a13ca9b41ab343268dbce3f8ef4d524a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/reactphp/socket/zipball/842dcd71df86671ee9491734035b3d2cf4a80ece",
- "reference": "842dcd71df86671ee9491734035b3d2cf4a80ece",
+ "url": "https://api.github.com/repos/reactphp/socket/zipball/e2b96b23a13ca9b41ab343268dbce3f8ef4d524a",
+ "reference": "e2b96b23a13ca9b41ab343268dbce3f8ef4d524a",
"shasum": ""
},
"require": {
@@ -5079,7 +5027,7 @@
},
"require-dev": {
"clue/block-react": "^1.2",
- "phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.35",
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35",
"react/promise-stream": "^1.2"
},
"type": "library",
@@ -5092,6 +5040,28 @@
"license": [
"MIT"
],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
"description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
"keywords": [
"Connection",
@@ -5100,7 +5070,17 @@
"reactphp",
"stream"
],
- "time": "2020-07-01T12:50:00+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/WyriHaximus",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2020-08-28T12:49:05+00:00"
},
{
"name": "react/stream",
@@ -6134,16 +6114,16 @@
},
{
"name": "symfony/deprecation-contracts",
- "version": "v2.1.2",
+ "version": "v2.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "dd99cb3a0aff6cadd2a8d7d7ed72c2161e218337"
+ "reference": "5e20b83385a77593259c9f8beb2c43cd03b2ac14"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/dd99cb3a0aff6cadd2a8d7d7ed72c2161e218337",
- "reference": "dd99cb3a0aff6cadd2a8d7d7ed72c2161e218337",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5e20b83385a77593259c9f8beb2c43cd03b2ac14",
+ "reference": "5e20b83385a77593259c9f8beb2c43cd03b2ac14",
"shasum": ""
},
"require": {
@@ -6153,6 +6133,10 @@
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
}
},
"autoload": {
@@ -6190,20 +6174,20 @@
"type": "tidelift"
}
],
- "time": "2020-05-27T08:34:37+00:00"
+ "time": "2020-06-06T08:49:21+00:00"
},
{
"name": "symfony/error-handler",
- "version": "v5.1.2",
+ "version": "v5.1.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
- "reference": "7d0b927b9d3dc41d7d46cda38cbfcd20cdcbb896"
+ "reference": "525636d4b84e06c6ca72d96b6856b5b169416e6a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/error-handler/zipball/7d0b927b9d3dc41d7d46cda38cbfcd20cdcbb896",
- "reference": "7d0b927b9d3dc41d7d46cda38cbfcd20cdcbb896",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/525636d4b84e06c6ca72d96b6856b5b169416e6a",
+ "reference": "525636d4b84e06c6ca72d96b6856b5b169416e6a",
"shasum": ""
},
"require": {
@@ -6261,20 +6245,20 @@
"type": "tidelift"
}
],
- "time": "2020-05-30T20:35:19+00:00"
+ "time": "2020-08-17T10:01:29+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v5.1.2",
+ "version": "v5.1.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "cc0d059e2e997e79ca34125a52f3e33de4424ac7"
+ "reference": "94871fc0a69c3c5da57764187724cdce0755899c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/cc0d059e2e997e79ca34125a52f3e33de4424ac7",
- "reference": "cc0d059e2e997e79ca34125a52f3e33de4424ac7",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/94871fc0a69c3c5da57764187724cdce0755899c",
+ "reference": "94871fc0a69c3c5da57764187724cdce0755899c",
"shasum": ""
},
"require": {
@@ -6347,20 +6331,20 @@
"type": "tidelift"
}
],
- "time": "2020-05-20T17:43:50+00:00"
+ "time": "2020-08-13T14:19:42+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
- "version": "v2.1.2",
+ "version": "v2.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
- "reference": "405952c4e90941a17e52ef7489a2bd94870bb290"
+ "reference": "f6f613d74cfc5a623fc36294d3451eb7fa5a042b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/405952c4e90941a17e52ef7489a2bd94870bb290",
- "reference": "405952c4e90941a17e52ef7489a2bd94870bb290",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/f6f613d74cfc5a623fc36294d3451eb7fa5a042b",
+ "reference": "f6f613d74cfc5a623fc36294d3451eb7fa5a042b",
"shasum": ""
},
"require": {
@@ -6374,6 +6358,10 @@
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
}
},
"autoload": {
@@ -6419,7 +6407,7 @@
"type": "tidelift"
}
],
- "time": "2020-05-20T17:43:50+00:00"
+ "time": "2020-07-06T13:23:11+00:00"
},
{
"name": "symfony/expression-language",
@@ -6552,16 +6540,16 @@
},
{
"name": "symfony/http-foundation",
- "version": "v5.1.2",
+ "version": "v5.1.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "f93055171b847915225bd5b0a5792888419d8d75"
+ "reference": "41a4647f12870e9d41d9a7d72ff0614a27208558"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f93055171b847915225bd5b0a5792888419d8d75",
- "reference": "f93055171b847915225bd5b0a5792888419d8d75",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/41a4647f12870e9d41d9a7d72ff0614a27208558",
+ "reference": "41a4647f12870e9d41d9a7d72ff0614a27208558",
"shasum": ""
},
"require": {
@@ -6623,20 +6611,20 @@
"type": "tidelift"
}
],
- "time": "2020-06-15T06:52:54+00:00"
+ "time": "2020-08-17T07:48:54+00:00"
},
{
"name": "symfony/http-kernel",
- "version": "v5.1.2",
+ "version": "v5.1.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "a18c27ace1ef344ffcb129a5b089bad7643b387a"
+ "reference": "3e32676e6cb5d2081c91a56783471ff8a7f7110b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/a18c27ace1ef344ffcb129a5b089bad7643b387a",
- "reference": "a18c27ace1ef344ffcb129a5b089bad7643b387a",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/3e32676e6cb5d2081c91a56783471ff8a7f7110b",
+ "reference": "3e32676e6cb5d2081c91a56783471ff8a7f7110b",
"shasum": ""
},
"require": {
@@ -6736,7 +6724,7 @@
"type": "tidelift"
}
],
- "time": "2020-06-15T13:51:38+00:00"
+ "time": "2020-09-02T08:15:18+00:00"
},
{
"name": "symfony/mime",
@@ -6817,16 +6805,16 @@
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.17.1",
+ "version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "2edd75b8b35d62fd3eeabba73b26b8f1f60ce13d"
+ "reference": "1c302646f6efc070cd46856e600e5e0684d6b454"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/2edd75b8b35d62fd3eeabba73b26b8f1f60ce13d",
- "reference": "2edd75b8b35d62fd3eeabba73b26b8f1f60ce13d",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454",
+ "reference": "1c302646f6efc070cd46856e600e5e0684d6b454",
"shasum": ""
},
"require": {
@@ -6838,7 +6826,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.17-dev"
+ "dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -6889,7 +6877,7 @@
"type": "tidelift"
}
],
- "time": "2020-06-06T08:46:27+00:00"
+ "time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
@@ -7132,16 +7120,16 @@
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.17.1",
+ "version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "7110338d81ce1cbc3e273136e4574663627037a7"
+ "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7110338d81ce1cbc3e273136e4574663627037a7",
- "reference": "7110338d81ce1cbc3e273136e4574663627037a7",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a",
+ "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a",
"shasum": ""
},
"require": {
@@ -7153,7 +7141,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.17-dev"
+ "dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -7205,7 +7193,7 @@
"type": "tidelift"
}
],
- "time": "2020-06-06T08:46:27+00:00"
+ "time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-php72",
@@ -7278,16 +7266,16 @@
},
{
"name": "symfony/polyfill-php73",
- "version": "v1.17.1",
+ "version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
- "reference": "fa0837fe02d617d31fbb25f990655861bb27bd1a"
+ "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fa0837fe02d617d31fbb25f990655861bb27bd1a",
- "reference": "fa0837fe02d617d31fbb25f990655861bb27bd1a",
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fffa1a52a023e782cdcc221d781fe1ec8f87fcca",
+ "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca",
"shasum": ""
},
"require": {
@@ -7296,7 +7284,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.17-dev"
+ "dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -7350,20 +7338,20 @@
"type": "tidelift"
}
],
- "time": "2020-06-06T08:46:27+00:00"
+ "time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-php80",
- "version": "v1.17.1",
+ "version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "4a5b6bba3259902e386eb80dd1956181ee90b5b2"
+ "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4a5b6bba3259902e386eb80dd1956181ee90b5b2",
- "reference": "4a5b6bba3259902e386eb80dd1956181ee90b5b2",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/d87d5766cbf48d72388a9f6b85f280c8ad51f981",
+ "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981",
"shasum": ""
},
"require": {
@@ -7372,7 +7360,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.17-dev"
+ "dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -7430,7 +7418,7 @@
"type": "tidelift"
}
],
- "time": "2020-06-06T08:46:27+00:00"
+ "time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/process",
@@ -7974,16 +7962,16 @@
},
{
"name": "symfony/var-dumper",
- "version": "v5.1.2",
+ "version": "v5.1.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
- "reference": "46a942903059b0b05e601f00eb64179e05578c0f"
+ "reference": "b43a3905262bcf97b2510f0621f859ca4f5287be"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/46a942903059b0b05e601f00eb64179e05578c0f",
- "reference": "46a942903059b0b05e601f00eb64179e05578c0f",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b43a3905262bcf97b2510f0621f859ca4f5287be",
+ "reference": "b43a3905262bcf97b2510f0621f859ca4f5287be",
"shasum": ""
},
"require": {
@@ -8060,7 +8048,7 @@
"type": "tidelift"
}
],
- "time": "2020-05-30T20:35:19+00:00"
+ "time": "2020-08-17T07:42:30+00:00"
},
{
"name": "symfony/var-exporter",
diff --git a/config/expose.php b/config/expose.php
index e08b519..7835ceb 100644
--- a/config/expose.php
+++ b/config/expose.php
@@ -230,6 +230,8 @@ return [
'invalid_auth_token' => 'Authentication failed. Please check your authentication token and try again.',
'subdomain_taken' => 'The chosen subdomain :subdomain is already taken. Please choose a different subdomain.',
+
+ 'custom_subdomain_unauthorized' => 'You are not allowed to specify custom subdomains. Please upgrade to Expose Pro.',
],
],
];
diff --git a/database/migrations/02_add_feature_flags_to_users_table.sql b/database/migrations/02_add_feature_flags_to_users_table.sql
new file mode 100644
index 0000000..520d8c3
--- /dev/null
+++ b/database/migrations/02_add_feature_flags_to_users_table.sql
@@ -0,0 +1 @@
+ALTER TABLE users ADD can_specify_subdomains BOOLEAN DEFAULT 1;
diff --git a/docs/server/ssl.md b/docs/server/ssl.md
index 95571bb..9adfb00 100644
--- a/docs/server/ssl.md
+++ b/docs/server/ssl.md
@@ -7,7 +7,9 @@ order: 2
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 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, Apache2 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.
@@ -40,3 +42,47 @@ 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
+
+