diff --git a/app/app.php b/app/app.php index 613707bb5b..7ab147225b 100644 --- a/app/app.php +++ b/app/app.php @@ -16,6 +16,7 @@ use Appwrite\Database\Database; use Appwrite\Database\Document; use Appwrite\Database\Validator\Authorization; use Appwrite\Event\Event; +use Appwrite\Network\Validators\Origin; /* * Configuration files @@ -96,16 +97,15 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $ * Adding Appwrite API domains to allow XDOMAIN communication * Skip this check for non-web platforms which are not requiredto send an origin header */ - $origin = parse_url($request->getServer('HTTP_ORIGIN', $request->getServer('HTTP_REFERER', '')), PHP_URL_HOST); - - if (!empty($origin) - && !in_array($origin, $clients) - && in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE]) - && empty($request->getHeader('X-Appwrite-Key', '')) - ) { - throw new Exception('Access from this client host is forbidden', 403); - } + $origin = $request->getServer('HTTP_ORIGIN', $request->getServer('HTTP_REFERER', '')); + $originValidator = new Origin($project->getAttribute('platforms', [])); + if(!$originValidator->isValid($origin) + && in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE]) + && empty($request->getHeader('X-Appwrite-Key', ''))) { + throw new Exception($originValidator->getDescription(), 403); + } + /* * ACL Check */ diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 84689814d8..81df073668 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -356,7 +356,7 @@ $utopia->get('/v1/avatars/qr') ->desc('Get QR Code') ->param('text', '', function () { return new Text(512); }, 'Plain text to be converted to QR code image.') ->param('size', 400, function () { return new Range(0, 1000); }, 'QR code size. Pass an integer between 0 to 1000. Defaults to 400.', true) - ->param('margin', 1, function () { return new Range(0, 10); }, 'Margin From Edge. Pass an integer between 0 to 10. Defaults to 1.', true) + ->param('margin', 1, function () { return new Range(0, 10); }, 'Margin from edge. Pass an integer between 0 to 10. Defaults to 1.', true) ->param('download', 0, function () { return new Range(0, 1); }, 'Return resulting image with \'Content-Disposition: attachment \' headers for the browser to start downloading it. Pass 0 for no header, or 1 for otherwise. Default value is set to 0.', true) ->label('scope', 'avatars.read') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) diff --git a/public/images/clients/flutter-android.png b/public/images/clients/flutter-android.png index be3c163d80..f7a3714f75 100644 Binary files a/public/images/clients/flutter-android.png and b/public/images/clients/flutter-android.png differ diff --git a/public/images/clients/flutter-ios.png b/public/images/clients/flutter-ios.png index be3c163d80..f7a3714f75 100644 Binary files a/public/images/clients/flutter-ios.png and b/public/images/clients/flutter-ios.png differ diff --git a/public/images/clients/flutter.png b/public/images/clients/flutter.png index be3c163d80..f7a3714f75 100644 Binary files a/public/images/clients/flutter.png and b/public/images/clients/flutter.png differ diff --git a/src/Appwrite/Network/Validators/Origin.php b/src/Appwrite/Network/Validators/Origin.php new file mode 100644 index 0000000000..b3ba4100cd --- /dev/null +++ b/src/Appwrite/Network/Validators/Origin.php @@ -0,0 +1,121 @@ + 'Web', + self::SCHEME_TYPE_HTTPS => 'Web', + self::SCHEME_TYPE_IOS => 'iOS', + self::SCHEME_TYPE_ANDROID => 'Android', + self::SCHEME_TYPE_MACOS => 'macOS', + self::SCHEME_TYPE_WINDOWS => 'Windows', + self::SCHEME_TYPE_LINUX => 'Linux', + ]; + + /** + * @var array + */ + protected $clients = [ + APP_DOMAIN, + 'localhost', + 'appwrite.test', + ]; + + /** + * @var string + */ + protected $client = self::CLIENT_TYPE_UNKNOWN; + + /** + * @var string + */ + protected $host = ''; + + /** + * @param string $target + */ + public function __construct($platforms) + { + foreach($platforms as $platform) { + $type = (isset($platform['type'])) ? $platform['type'] : ''; + + switch ($type) { + case self::CLIENT_TYPE_WEB: + $this->clients[] = (isset($platform['hostname'])) ? $platform['hostname'] : ''; + break; + + case self::CLIENT_TYPE_FLUTTER_IOS: + case self::CLIENT_TYPE_FLUTTER_ANDROID: + case self::CLIENT_TYPE_FLUTTER_MACOS: + case self::CLIENT_TYPE_FLUTTER_WINDOWS: + case self::CLIENT_TYPE_FLUTTER_LINUX: + $this->clients[] = (isset($platform['key'])) ? $platform['key'] : ''; + break; + + default: + # code... + break; + } + } + } + + public function getDescription() + { + if(!array_key_exists($this->client, $this->platforms)) { + return 'Unsupported platform'; + } + + return 'Inavlid Origin. Register your new client ('.$this->host.') as a new ' + .$this->platforms[$this->client].' platform on your '.APP_NAME.' console'; + } + + /** + * Check if Origin has been whiltlisted + * for access to the API + * + * @param string $origin + * + * @return bool + */ + public function isValid($origin) + { + $scheme = parse_url($origin, PHP_URL_SCHEME); + $host = parse_url($origin, PHP_URL_HOST); + + $this->host = $host; + $this->client = $scheme; + + if(empty($host)) { + return true; + } + + if(in_array($host, $this->clients)) { + return true; + } + + return false; + } +}