diff --git a/app/app.php b/app/app.php index fd9188ac8..d31cc9192 100644 --- a/app/app.php +++ b/app/app.php @@ -15,86 +15,33 @@ use Appwrite\Database\Document; use Appwrite\Database\Validator\Authorization; use Appwrite\Network\Validator\Origin; +Config::setParam('domain', $_SERVER['HTTP_HOST']); +Config::setParam('domainVerification', false); // Config::setParam('domain', $request->getServer('HTTP_HOST', '')); // Config::setParam('domainVerification', false); -// Config::setParam('protocol', $request->getServer('HTTP_X_FORWARDED_PROTO', $request->getServer('REQUEST_SCHEME', 'https'))); -// Config::setParam('port', (string) \parse_url(Config::getParam('protocol').'://'.$request->getServer('HTTP_HOST', ''), PHP_URL_PORT)); -// Config::setParam('hostname', \parse_url(Config::getParam('protocol').'://'.$request->getServer('HTTP_HOST', null), PHP_URL_HOST)); + +\define('COOKIE_DOMAIN', + ( + $_SERVER['HTTP_HOST'] === 'localhost' || + $_SERVER['HTTP_HOST'] === 'localhost:'.$request->getPort() || + (\filter_var($request->getHostname(), FILTER_VALIDATE_IP) !== false) + ) + ? null + : '.'.$request->getHostname() + ); +\define('COOKIE_SAMESITE', Response::COOKIE_SAMESITE_NONE); // \define('COOKIE_DOMAIN', // ( // $request->getServer('HTTP_HOST', null) === 'localhost' || -// $request->getServer('HTTP_HOST', null) === 'localhost:'.Config::getParam('port') || -// (\filter_var(Config::getParam('hostname'), FILTER_VALIDATE_IP) !== false) +// $request->getServer('HTTP_HOST', null) === 'localhost:'.$request->getPort() || +// (\filter_var($request->getHostname(), FILTER_VALIDATE_IP) !== false) // ) // ? null -// : '.'.Config::getParam('hostname') +// : '.'.$request->getHostname() // ); // \define('COOKIE_SAMESITE', Response::COOKIE_SAMESITE_NONE); -// Authorization::disable(); - -// $project = $consoleDB->getDocument($request->getParam('project', $request->getHeader('X-Appwrite-Project', ''))); - -// Authorization::enable(); - -// $console = $consoleDB->getDocument('console'); - -// $mode = $request->getParam('mode', $request->getHeader('X-Appwrite-Mode', 'default')); - -// Auth::setCookieName('a_session_'.$project->getId()); - -// if (APP_MODE_ADMIN === $mode) { -// Auth::setCookieName('a_session_'.$console->getId()); -// } - -// $session = Auth::decodeSession( -// $request->getCookie(Auth::$cookieName, // Get sessions -// $request->getCookie(Auth::$cookieName.'_legacy', // Get fallback session from old clients (no SameSite support) -// $request->getHeader('X-Appwrite-Key', '')))); // Get API Key - -// // Get fallback session from clients who block 3rd-party cookies -// $response->addHeader('X-Debug-Fallback', 'false'); - -// if(empty($session['id']) && empty($session['secret'])) { -// $response->addHeader('X-Debug-Fallback', 'true'); -// $fallback = $request->getHeader('X-Fallback-Cookies', ''); -// $fallback = \json_decode($fallback, true); -// $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); -// } - -// Auth::$unique = $session['id']; -// Auth::$secret = $session['secret']; - -// $projectDB = new Database(); -// $projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register)); -// $projectDB->setNamespace('app_'.$project->getId()); -// $projectDB->setMocks(Config::getParam('collections', [])); - -// if (APP_MODE_ADMIN !== $mode) { -// $user = $projectDB->getDocument(Auth::$unique); -// } -// else { -// $user = $consoleDB->getDocument(Auth::$unique); - -// $user -// ->setAttribute('$id', 'admin-'.$user->getAttribute('$id')) -// ; -// } - -// if (empty($user->getId()) // Check a document has been found in the DB -// || Database::SYSTEM_COLLECTION_USERS !== $user->getCollection() // Validate returned document is really a user document -// || !Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret)) { // Validate user has valid login token -// $user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]); -// } - -// if (APP_MODE_ADMIN === $mode) { -// if (!empty($user->search('teamId', $project->getAttribute('teamId'), $user->getAttribute('memberships')))) { -// Authorization::disable(); -// } else { -// $user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]); -// } -// } // // Set project mail // $register->get('smtp') @@ -106,29 +53,6 @@ use Appwrite\Network\Validator\Origin; // ) // ); -/** - * Get All verified client URLs for both console and current projects - * + Filter for duplicated entries - */ -// $clientsConsole = \array_map(function ($node) { -// return $node['hostname']; -// }, \array_filter($console->getAttribute('platforms', []), function ($node) { -// if (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) { -// return true; -// } - -// return false; -// })); - -// $clients = \array_unique(\array_merge($clientsConsole, \array_map(function ($node) { -// return $node['hostname']; -// }, \array_filter($project->getAttribute('platforms', []), function ($node) { -// if (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) { -// return true; -// } - -// return false; -// })))); App::init(function ($utopia, $request, $response, $console, $project, $user, $locale, $webhooks, $audits, $usage, $clients) { /** @var Utopia\Request $request */ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 6596bc753..309277981 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -565,7 +565,7 @@ App::get('/v1/account') ], $oauth2Keys )), ['roles' => Authorization::getRoles()])); - }, ['response', ['user']]); + }, ['response', 'user']); App::get('/v1/account/prefs') ->desc('Get Account Preferences') diff --git a/app/controllers/web/home.php b/app/controllers/web/home.php index 9cbfef4e4..6f8c0d9f0 100644 --- a/app/controllers/web/home.php +++ b/app/controllers/web/home.php @@ -188,8 +188,9 @@ App::get('/open-api-2.json') ->param('platform', APP_PLATFORM_CLIENT, function () {return new WhiteList([APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER, APP_PLATFORM_CONSOLE]);}, 'Choose target platform.', true) ->param('extensions', 0, function () {return new Range(0, 1);}, 'Show extra data.', true) ->param('tests', 0, function () {return new Range(0, 1);}, 'Include only test services.', true) - ->action(function ($platform, $extensions, $tests, $utopia, $response) { + ->action(function ($platform, $extensions, $tests, $utopia, $request, $response) { /** @var Utopia\App $utopia */ + /** @var Utopia\Request $request */ /** @var Utopia\Response $response */ $services = Config::getParam('services', []); @@ -370,7 +371,7 @@ App::get('/open-api-2.json') ], 'externalDocs' => [ 'description' => 'Full API docs, specs and tutorials', - 'url' => Config::getParam('protocol').'://'.Config::getParam('domain').'/docs', + 'url' => $request->getProtocol().'://'.Config::getParam('domain').'/docs', ], ]; @@ -586,4 +587,4 @@ App::get('/open-api-2.json') $response ->json($output); - }, ['utopia', 'response']); \ No newline at end of file + }, ['utopia', 'request', 'response']); \ No newline at end of file diff --git a/app/init.php b/app/init.php index d02ded195..ea69d1a90 100644 --- a/app/init.php +++ b/app/init.php @@ -11,6 +11,7 @@ if (\file_exists(__DIR__.'/../vendor/autoload.php')) { require_once __DIR__.'/../vendor/autoload.php'; } +use Appwrite\Auth\Auth; use Utopia\App; use Utopia\Config\Config; use Utopia\Locale\Locale; @@ -19,6 +20,7 @@ use Appwrite\Database\Database; use Appwrite\Database\Adapter\MySQL as MySQLAdapter; use Appwrite\Database\Adapter\Redis as RedisAdapter; use Appwrite\Database\Document; +use Appwrite\Database\Validator\Authorization; use Appwrite\Event\Event; use PHPMailer\PHPMailer\PHPMailer; use Utopia\View; @@ -268,13 +270,101 @@ App::setResource('deletes', function($register) { }, ['register']); // Test Mock -App::setResource('clients', function() { return []; }); +App::setResource('clients', function($console, $project) { + /** + * Get All verified client URLs for both console and current projects + * + Filter for duplicated entries + */ + $clientsConsole = \array_map(function ($node) { + return $node['hostname']; + }, \array_filter($console->getAttribute('platforms', []), function ($node) { + if (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) { + return true; + } -App::setResource('user', function() { return new Document([]); }); + return false; + })); -App::setResource('project', function() { return new Document([]); }); + $clients = \array_unique(\array_merge($clientsConsole, \array_map(function ($node) { + return $node['hostname']; + }, \array_filter($project->getAttribute('platforms', []), function ($node) { + if (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) { + return true; + } -App::setResource('console', function() { return new Document([]); }); + return false; + })))); + + return $clients; +}, ['console', 'project']); + +App::setResource('user', function($mode, $project, $console, $request, $response, $projectDB, $consoleDB) { + + Auth::setCookieName('a_session_'.$project->getId()); + + if (APP_MODE_ADMIN === $mode) { + Auth::setCookieName('a_session_'.$console->getId()); + } + + $session = Auth::decodeSession( + $request->getCookie(Auth::$cookieName, // Get sessions + $request->getCookie(Auth::$cookieName.'_legacy', // Get fallback session from old clients (no SameSite support) + $request->getHeader('X-Appwrite-Key', '')))); // Get API Key + + // Get fallback session from clients who block 3rd-party cookies + $response->addHeader('X-Debug-Fallback', 'false'); + + if(empty($session['id']) && empty($session['secret'])) { + $response->addHeader('X-Debug-Fallback', 'true'); + $fallback = $request->getHeader('X-Fallback-Cookies', ''); + $fallback = \json_decode($fallback, true); + $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); + } + Auth::$unique = $session['id']; + Auth::$secret = $session['secret']; + + if (APP_MODE_ADMIN !== $mode) { + $user = $projectDB->getDocument(Auth::$unique); + } + else { + $user = $consoleDB->getDocument(Auth::$unique); + + $user + ->setAttribute('$id', 'admin-'.$user->getAttribute('$id')) + ; + } + + if (empty($user->getId()) // Check a document has been found in the DB + || Database::SYSTEM_COLLECTION_USERS !== $user->getCollection() // Validate returned document is really a user document + || !Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret)) { // Validate user has valid login token + $user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]); + } + + if (APP_MODE_ADMIN === $mode) { + if (!empty($user->search('teamId', $project->getAttribute('teamId'), $user->getAttribute('memberships')))) { + Authorization::disable(); + } else { + $user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]); + } + } + + return $user; +}, ['mode', 'project', 'console', 'request', 'response', 'projectDB', 'consoleDB']); + +App::setResource('project', function($consoleDB, $request) { + Authorization::disable(); + + $project = $consoleDB->getDocument($request->getParam('project', + $request->getHeader('X-Appwrite-Project', ''))); + + Authorization::enable(); + + return $project; +}, ['consoleDB', 'request']); + +App::setResource('console', function($consoleDB) { + return $consoleDB->getDocument('console'); +}, ['consoleDB']); App::setResource('consoleDB', function($register) { $consoleDB = new Database(); @@ -282,8 +372,19 @@ App::setResource('consoleDB', function($register) { $consoleDB->setNamespace('app_console'); // Should be replaced with param if we want to have parent projects $consoleDB->setMocks(Config::getParam('collections', [])); + + return $consoleDB; }, ['register']); -App::setResource('projectDB', function() { return new Database([]); }); +App::setResource('projectDB', function($register, $project) { + $projectDB = new Database(); + $projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register)); + $projectDB->setNamespace('app_'.$project->getId()); + $projectDB->setMocks(Config::getParam('collections', [])); -App::setResource('mode', function() { return false; }); + return $projectDB; +}, ['register', 'project']); + +App::setResource('mode', function($request) { + return $request->getParam('mode', $request->getHeader('X-Appwrite-Mode', 'default')); +}, ['request']);