diff --git a/CHANGES.md b/CHANGES.md index 8d23b9fdb6..b9429c94e4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -19,6 +19,7 @@ - Added container names to docker-compose.yml (@drandell) - Upgraded ClamAV container image to version 1.0.9 - Optimised function execution by using fully-qualified function calls +- Added support for boolean 'true' and 'false' in query strings alongside 1 and 0 ## Bug Fixes @@ -34,6 +35,9 @@ - Fixed wrong JSON validation when creating and updating database documnets - Fixed bug where max file size was limited to max of 10MB - Fixed bug preventing the deletion of the project logo +- Fixed Bug when trying to overwrite OAuth cookie in the Flutter SDK +- Fixed OAuth redirect when using the self-hosted instance default success URL ([#454](https://github.com/appwrite/appwrite/issues/454)) +- Fixed bug denying authentication with Github OAuth provider ## Breaking Changes - **Deprecated** `first` and `last` query params for documents list route in the database API diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index ffad22b325..8ba5784412 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -29,8 +29,8 @@ use DeviceDetector\DeviceDetector; use GeoIp2\Database\Reader; use Utopia\Validator\ArrayList; -$oauthDefaultSuccess = $request->getServer('_APP_HOME').'/auth/oauth2/success'; -$oauthDefaultFailure = $request->getServer('_APP_HOME').'/auth/oauth2/failure'; +$oauthDefaultSuccess = '/auth/oauth2/success'; +$oauthDefaultFailure = '/auth/oauth2/failure'; $oauth2Keys = []; @@ -504,8 +504,9 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect') ->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])) ; } - - if ($state['success'] === $oauthDefaultSuccess) { // Add keys for non-web platforms + + // Add keys for non-web platforms - TODO - add verification phase to aviod session sniffing + if (parse_url($state['success'], PHP_URL_PATH) === $oauthDefaultSuccess) { $state['success'] = URLParser::parse($state['success']); $query = URLParser::parseQuery($state['success']['query']); $query['project'] = $project->getId(); diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 90f8ac34c8..9e888f7f7d 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -3,6 +3,7 @@ global $utopia, $request, $response; use Utopia\Exception; +use Utopia\Validator\Boolean; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; use Utopia\Validator\Range; @@ -364,7 +365,7 @@ $utopia->get('/v1/avatars/qr') ->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('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) + ->param('download', false, function () { return new Boolean(true); }, '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]) ->label('sdk.namespace', 'avatars') @@ -373,6 +374,8 @@ $utopia->get('/v1/avatars/qr') ->label('sdk.description', '/docs/references/avatars/get-qr.md') ->action( function ($text, $size, $margin, $download) use ($response) { + $download = ($download === '1' || $download === 'true' || $download === 1 || $download === true); + $renderer = new ImageRenderer( new RendererStyle($size, $margin), new ImagickImageBackEnd('png', 100) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index e7c27c109c..af0e358a64 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -5,14 +5,15 @@ global $utopia, $register, $request, $response, $webhook, $audit, $projectDB; use Utopia\App; use Utopia\Exception; use Utopia\Response; +use Utopia\Validator\Boolean; use Utopia\Validator\Range; use Utopia\Validator\WhiteList; use Utopia\Validator\Text; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; -use Utopia\Locale\Locale; -use Utopia\Audit\Audit; -use Utopia\Audit\Adapters\MySQL as AuditAdapter; +// use Utopia\Locale\Locale; +// use Utopia\Audit\Audit; +// use Utopia\Audit\Adapters\MySQL as AuditAdapter; use Appwrite\Database\Database; use Appwrite\Database\Document; use Appwrite\Database\Validator\UID; @@ -22,8 +23,9 @@ use Appwrite\Database\Validator\Collection; use Appwrite\Database\Validator\Authorization; use Appwrite\Database\Exception\Authorization as AuthorizationException; use Appwrite\Database\Exception\Structure as StructureException; -use DeviceDetector\DeviceDetector; -use GeoIp2\Database\Reader; + +// use DeviceDetector\DeviceDetector; +// use GeoIp2\Database\Reader; $utopia->post('/v1/database/collections') ->desc('Create Collection') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 3311abbd0b..bd49422398 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -5,10 +5,10 @@ global $utopia, $request, $response, $register, $user, $consoleDB, $projectDB, $ use Utopia\Exception; use Utopia\Response; use Utopia\Validator\ArrayList; +use Utopia\Validator\Boolean; use Utopia\Validator\Domain as DomainValidator; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; -use Utopia\Validator\Range; use Utopia\Validator\URL; use Utopia\Config\Config; use Utopia\Domains\Domain; @@ -454,7 +454,7 @@ $utopia->post('/v1/projects/:projectId/webhooks') ->param('name', null, function () { return new Text(256); }, 'Webhook name.') ->param('events', null, function () { return new ArrayList(new Text(256)); }, 'Webhook events list.') ->param('url', null, function () { return new Text(2000); }, 'Webhook URL.') - ->param('security', null, function () { return new Range(0, 1); }, 'Certificate verification, 0 for disabled or 1 for enabled.') + ->param('security', false, function () { return new Boolean(true); }, 'Certificate verification, false for disabled or true for enabled.') ->param('httpUser', '', function () { return new Text(256); }, 'Webhook HTTP user.', true) ->param('httpPass', '', function () { return new Text(256); }, 'Webhook HTTP password.', true) ->action( @@ -465,6 +465,7 @@ $utopia->post('/v1/projects/:projectId/webhooks') throw new Exception('Project not found', 404); } + $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); $key = $request->getServer('_APP_OPENSSL_KEY_V1'); $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); $tag = null; @@ -587,8 +588,7 @@ $utopia->put('/v1/projects/:projectId/webhooks/:webhookId') ->param('name', null, function () { return new Text(256); }, 'Webhook name.') ->param('events', null, function () { return new ArrayList(new Text(256)); }, 'Webhook events list.') ->param('url', null, function () { return new Text(2000); }, 'Webhook URL.') - ->param('security', null, function () { return new Range(0, 1); }, 'Certificate verification, 0 for disabled or 1 for enabled.') - ->param('httpUser', '', function () { return new Text(256); }, 'Webhook HTTP user.', true) + ->param('security', false, function () { return new Boolean(true); }, 'Certificate verification, false for disabled or true for enabled.') ->param('httpUser', '', function () { return new Text(256); }, 'Webhook HTTP user.', true) ->param('httpPass', '', function () { return new Text(256); }, 'Webhook HTTP password.', true) ->action( function ($projectId, $webhookId, $name, $events, $url, $security, $httpUser, $httpPass) use ($request, $response, $consoleDB) { @@ -598,6 +598,7 @@ $utopia->put('/v1/projects/:projectId/webhooks/:webhookId') throw new Exception('Project not found', 404); } + $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); $key = $request->getServer('_APP_OPENSSL_KEY_V1'); $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); $tag = null; @@ -835,8 +836,7 @@ $utopia->post('/v1/projects/:projectId/tasks') ->param('name', null, function () { return new Text(256); }, 'Task name.') ->param('status', null, function () { return new WhiteList(['play', 'pause']); }, 'Task status.') ->param('schedule', null, function () { return new Cron(); }, 'Task schedule CRON syntax.') - ->param('security', null, function () { return new Range(0, 1); }, 'Certificate verification, 0 for disabled or 1 for enabled.') - ->param('httpMethod', '', function () { return new WhiteList(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE', 'CONNECT']); }, 'Task HTTP method.') + ->param('security', false, function () { return new Boolean(true); }, 'Certificate verification, false for disabled or true for enabled.') ->param('httpMethod', '', function () { return new WhiteList(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE', 'CONNECT']); }, 'Task HTTP method.') ->param('httpUrl', '', function () { return new URL(); }, 'Task HTTP URL') ->param('httpHeaders', null, function () { return new ArrayList(new Text(256)); }, 'Task HTTP headers list.', true) ->param('httpUser', '', function () { return new Text(256); }, 'Task HTTP user.', true) @@ -852,6 +852,7 @@ $utopia->post('/v1/projects/:projectId/tasks') $cron = CronExpression::factory($schedule); $next = ($status == 'play') ? $cron->getNextRunDate()->format('U') : null; + $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); $key = $request->getServer('_APP_OPENSSL_KEY_V1'); $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); $tag = null; @@ -985,7 +986,7 @@ $utopia->put('/v1/projects/:projectId/tasks/:taskId') ->param('name', null, function () { return new Text(256); }, 'Task name.') ->param('status', null, function () { return new WhiteList(['play', 'pause']); }, 'Task status.') ->param('schedule', null, function () { return new Cron(); }, 'Task schedule CRON syntax.') - ->param('security', null, function () { return new Range(0, 1); }, 'Certificate verification, 0 for disabled or 1 for enabled.') + ->param('security', false, function () { return new Boolean(true); }, 'Certificate verification, false for disabled or true for enabled.') ->param('httpMethod', '', function () { return new WhiteList(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE', 'CONNECT']); }, 'Task HTTP method.') ->param('httpUrl', '', function () { return new URL(); }, 'Task HTTP URL.') ->param('httpHeaders', null, function () { return new ArrayList(new Text(256)); }, 'Task HTTP headers list.', true) @@ -1008,6 +1009,7 @@ $utopia->put('/v1/projects/:projectId/tasks/:taskId') $cron = CronExpression::factory($schedule); $next = ($status == 'play') ? $cron->getNextRunDate()->format('U') : null; + $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); $key = $request->getServer('_APP_OPENSSL_KEY_V1'); $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); $tag = null; diff --git a/app/controllers/web/home.php b/app/controllers/web/home.php index 65097d36c3..5d8a7aefc2 100644 --- a/app/controllers/web/home.php +++ b/app/controllers/web/home.php @@ -457,6 +457,10 @@ $utopia->get('/open-api-2.json') $node['type'] = 'string'; $node['x-example'] = '['.\strtoupper(fromCamelCase($node['name'])).']'; break; + case 'Utopia\Validator\Boolean': + $node['type'] = 'boolean'; + $node['x-example'] = false; + break; case 'Appwrite\Database\Validator\UID': $node['type'] = 'string'; $node['x-example'] = '['.\strtoupper(fromCamelCase($node['name'])).']'; diff --git a/composer.lock b/composer.lock index 595045b9b5..a7d030f06a 100644 --- a/composer.lock +++ b/composer.lock @@ -2141,24 +2141,23 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "790426f28bfcbfc1a6f1d59ee8c986edfa45395c" + "reference": "664187301bfbc87e686df212094e6817805c3ab8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/790426f28bfcbfc1a6f1d59ee8c986edfa45395c", - "reference": "790426f28bfcbfc1a6f1d59ee8c986edfa45395c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/664187301bfbc87e686df212094e6817805c3ab8", + "reference": "664187301bfbc87e686df212094e6817805c3ab8", "shasum": "" }, "require": { - "ext-filter": "^7.1", - "php": "^7.2", + "ext-filter": "*", + "php": "^7.2 || ^8.0", "phpdocumentor/reflection-common": "^2.0", "phpdocumentor/type-resolver": "^1.0", "webmozart/assert": "^1" }, "require-dev": { - "doctrine/instantiator": "^1", - "mockery/mockery": "^1" + "mockery/mockery": "1.3.*" }, "type": "library", "extra": { @@ -2186,7 +2185,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2020-06-19T18:58:43+00:00" + "time": "2020-06-27T17:33:53+00:00" }, { "name": "phpdocumentor/type-resolver", diff --git a/docker-compose.yml b/docker-compose.yml index 26a2ed016f..0ec0701b70 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -50,7 +50,7 @@ services: - ./phpunit.xml:/usr/share/nginx/html/phpunit.xml - ./tests:/usr/share/nginx/html/tests - ./app:/usr/share/nginx/html/app - # - ./vendor:/usr/share/nginx/html/vendor + # - ./vendor:/usr/share/nginx/html/vendor - ./docs:/usr/share/nginx/html/docs - ./public:/usr/share/nginx/html/public - ./src:/usr/share/nginx/html/src diff --git a/src/Appwrite/Auth/OAuth2.php b/src/Appwrite/Auth/OAuth2.php index 2882746608..5a92049fce 100644 --- a/src/Appwrite/Auth/OAuth2.php +++ b/src/Appwrite/Auth/OAuth2.php @@ -138,7 +138,7 @@ abstract class OAuth2 \curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); \curl_setopt($ch, CURLOPT_HEADER, 0); \curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - \curl_setopt($ch, CURLOPT_USERAGENT, ''); + \curl_setopt($ch, CURLOPT_USERAGENT, 'Appwrite OAuth2'); if (!empty($payload)) { \curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); diff --git a/src/Appwrite/Auth/OAuth2/GitHub.php b/src/Appwrite/Auth/OAuth2/Github.php similarity index 97% rename from src/Appwrite/Auth/OAuth2/GitHub.php rename to src/Appwrite/Auth/OAuth2/Github.php index 882f2d3f2a..cd02252092 100644 --- a/src/Appwrite/Auth/OAuth2/GitHub.php +++ b/src/Appwrite/Auth/OAuth2/Github.php @@ -15,7 +15,7 @@ class Github extends OAuth2 * @var array */ protected $scopes = [ - 'user:email' + 'user:email', ]; /** @@ -78,6 +78,7 @@ class Github extends OAuth2 { $user = $this->getUser($accessToken); + var_dump($user); if (isset($user['id'])) { return $user['id']; } @@ -125,7 +126,7 @@ class Github extends OAuth2 * @return array */ protected function getUser(string $accessToken) - { + { if (empty($this->user)) { $this->user = \json_decode($this->request('GET', 'https://api.github.com/user', ['Authorization: token '.\urlencode($accessToken)]), true); }