From 45e9040fa5a0c3a9e8650a27c2d6bae88e34dccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 21 Mar 2022 14:23:56 +0000 Subject: [PATCH 01/87] Disallow HTTP communication if required --- app/config/errors.php | 5 +++++ app/controllers/general.php | 4 ++++ composer.lock | 28 ++++++++++++++-------------- src/Appwrite/Extend/Exception.php | 1 + 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index b7b5c1ca31..ca305ed0b5 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -78,6 +78,11 @@ return [ 'description' => 'An internal server error occurred.', 'code' => 500, ], + Exception::GENERAL_FORCED_HTTPS_IGNORED => [ + 'name' => Exception::GENERAL_FORCED_HTTPS_IGNORED, + 'description' => 'Appwrite instance forces secure communication. Switch to HTTPS protocol in your endpoint.', + 'code' => 500, + ], /** User Errors */ Exception::USER_COUNT_EXCEEDED => [ diff --git a/app/controllers/general.php b/app/controllers/general.php index c08c244b31..4cf77bb4bb 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -173,6 +173,10 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons */ if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS if ($request->getProtocol() !== 'https') { + if($request->getMethod() !== Request::METHOD_GET) { + throw new Exception('HTTPS communication required.', 500, Exception::GENERAL_FORCED_HTTPS_IGNORED); + } + return $response->redirect('https://'.$request->getHostname().$request->getURI()); } diff --git a/composer.lock b/composer.lock index 291b391b3e..4a9b786b33 100644 --- a/composer.lock +++ b/composer.lock @@ -478,16 +478,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.4.1", + "version": "7.4.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79" + "reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee0a041b1760e6a53d2a39c8c34115adc2af2c79", - "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ac1ec1cd9b5624694c3a40be801d94137afb12b4", + "reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4", "shasum": "" }, "require": { @@ -582,7 +582,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.4.1" + "source": "https://github.com/guzzle/guzzle/tree/7.4.2" }, "funding": [ { @@ -598,7 +598,7 @@ "type": "tidelift" } ], - "time": "2021-12-06T18:43:05+00:00" + "time": "2022-03-20T14:16:28+00:00" }, { "name": "guzzlehttp/promises", @@ -686,16 +686,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.1.0", + "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72" + "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/089edd38f5b8abba6cb01567c2a8aaa47cec4c72", - "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c94a94f120803a18554c1805ef2e539f8285f9a2", + "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2", "shasum": "" }, "require": { @@ -719,7 +719,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.2-dev" } }, "autoload": { @@ -781,7 +781,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.1.0" + "source": "https://github.com/guzzle/psr7/tree/2.2.1" }, "funding": [ { @@ -797,7 +797,7 @@ "type": "tidelift" } ], - "time": "2021-10-06T17:43:30+00:00" + "time": "2022-03-20T21:55:58+00:00" }, { "name": "influxdb/influxdb-php", @@ -6580,5 +6580,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.2.0" } diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 4f396990fd..a5858d662b 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -46,6 +46,7 @@ class Exception extends \Exception const GENERAL_ROUTE_NOT_FOUND = 'general_route_not_found'; const GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found'; const GENERAL_SERVER_ERROR = 'general_server_error'; + const GENERAL_FORCED_HTTPS_IGNORED = 'general_forced_https_ignored'; /** Users */ const USER_COUNT_EXCEEDED = 'user_count_exceeded'; From 213215bffa17f3d4ef4bedd8bf195ae791088358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 23 Mar 2022 13:31:35 +0000 Subject: [PATCH 02/87] Allow same certificate with multiple projects (same domain) --- app/workers/certificates.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 9f9ce33dbd..b9f8ec26a0 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -98,6 +98,23 @@ class CertificatesV1 extends Worker && isset($certificate['issueDate']) && (($certificate['issueDate'] + ($expiry)) > \time()) ) { // Check last issue time + + // Update document anyway, if needed + // This occurs when a cert is already generated because a different project is using the domain + // By updating here we ensure all domains has certificateId assigned (share same certificate document) + if(!isset($document['certificateId'])) { + $certificate = new Document(\array_merge($document, [ + 'updated' => \time(), + 'certificateId' => $certificate->getId(), + ])); + + $certificate = $dbForConsole->updateDocument('domains', $certificate->getId(), $certificate); + + if(!$certificate) { + throw new Exception('Failed saving domain to DB'); + } + } + throw new Exception('Renew isn\'t required'); } From 41cc8bd38de27314c22728f3e98e20594d0843ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 23 Mar 2022 14:40:19 +0000 Subject: [PATCH 03/87] CertificateId update fix --- app/workers/certificates.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index b9f8ec26a0..183427fede 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -103,12 +103,14 @@ class CertificatesV1 extends Worker // This occurs when a cert is already generated because a different project is using the domain // By updating here we ensure all domains has certificateId assigned (share same certificate document) if(!isset($document['certificateId'])) { - $certificate = new Document(\array_merge($document, [ + $certificate = new Document($certificate); + + $domain = new Document(\array_merge($document, [ 'updated' => \time(), 'certificateId' => $certificate->getId(), ])); - $certificate = $dbForConsole->updateDocument('domains', $certificate->getId(), $certificate); + $domain = $dbForConsole->updateDocument('domains', $domain->getId(), $domain); if(!$certificate) { throw new Exception('Failed saving domain to DB'); From 7f7b23181f340988f6cd149473bfa35476447c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 23 Mar 2022 14:46:59 +0000 Subject: [PATCH 04/87] Added global auth disable in cert worker --- app/workers/certificates.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 183427fede..35409f3caf 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -11,6 +11,8 @@ use Utopia\Domains\Domain; require_once __DIR__.'/../init.php'; +Authorization::disable(); + Console::title('Certificates V1 Worker'); Console::success(APP_NAME . ' certificates worker v1 has started'); @@ -40,8 +42,6 @@ class CertificatesV1 extends Worker * 3.5. Schedule to renew certificate in 60 days */ - Authorization::disable(); - // Args $document = $this->args['document']; $domain = $this->args['domain']; @@ -206,8 +206,6 @@ class CertificatesV1 extends Worker 'validateTarget' => $validateTarget, 'validateCNAME' => $validateCNAME, ]); // Async task rescheduale - - Authorization::reset(); } public function shutdown(): void From 7f7a2c7dfbea8cc3cf29ec64136a4fff34c99ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 23 Mar 2022 14:47:50 +0000 Subject: [PATCH 05/87] Revert auth changes --- app/workers/certificates.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 35409f3caf..183427fede 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -11,8 +11,6 @@ use Utopia\Domains\Domain; require_once __DIR__.'/../init.php'; -Authorization::disable(); - Console::title('Certificates V1 Worker'); Console::success(APP_NAME . ' certificates worker v1 has started'); @@ -42,6 +40,8 @@ class CertificatesV1 extends Worker * 3.5. Schedule to renew certificate in 60 days */ + Authorization::disable(); + // Args $document = $this->args['document']; $domain = $this->args['domain']; @@ -206,6 +206,8 @@ class CertificatesV1 extends Worker 'validateTarget' => $validateTarget, 'validateCNAME' => $validateCNAME, ]); // Async task rescheduale + + Authorization::reset(); } public function shutdown(): void From 2bc6d7c41b6965b317f4c4333b4d931b503a42aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 23 Mar 2022 14:54:25 +0000 Subject: [PATCH 06/87] Delete certificate document alongside certificate directory --- app/workers/deletes.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/workers/deletes.php b/app/workers/deletes.php index 963488a00a..372f38dc6c 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -18,8 +18,6 @@ use Utopia\Audit\Audit; require_once __DIR__ . '/../init.php'; -Authorization::disable(); - Console::title('Deletes V1 Worker'); Console::success(APP_NAME . ' deletes worker v1 has started' . "\n"); @@ -40,6 +38,8 @@ class DeletesV1 extends Worker public function run(): void { + Authorization::disable(); + $projectId = $this->args['projectId'] ?? ''; $type = $this->args['type'] ?? ''; @@ -113,6 +113,8 @@ class DeletesV1 extends Worker Console::error('No delete operation for type: ' . $type); break; } + + Authorization::reset(); } public function shutdown(): void @@ -531,6 +533,13 @@ class DeletesV1 extends Worker $checkTraversal = realpath($directory) === $directory; if ($domain && $checkTraversal && is_dir($directory)) { + // Delete certificate document, so Appwrite is aware of change + if(isset($document['certificateId'])) { + $consoleDB = $this->getConsoleDB(); + $consoleDB->deleteDocument('certificates', $document['certificateId']); + } + + // Delete files, so Traefik is aware of change array_map('unlink', glob($directory . '/*.*')); rmdir($directory); Console::info("Deleted certificate files for {$domain}"); From 8fe5a47a3597f6eefad1f8ab33227004a7e36da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 23 Mar 2022 15:05:50 +0000 Subject: [PATCH 07/87] Skip certificate deletion if its already used by other project --- app/workers/deletes.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/workers/deletes.php b/app/workers/deletes.php index 372f38dc6c..ec920f6943 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -528,6 +528,22 @@ class DeletesV1 extends Worker */ protected function deleteCertificates(Document $document): void { + $consoleDB = $this->getConsoleDB(); + + // If domain has certificate generated + if(isset($document['certificateId'])) { + $domainUsingCertificate = $consoleDB->findOne('domains', [ + new Query('certificateId', Query::TYPE_EQUAL, [$document['certificateId']]) + ]); + + // If certificate is still used by some domain, mark we can't delete. + // Current domain should not be found, because we only have copy. Original domain is already deleted from database. + if($domainUsingCertificate) { + Console::warning("Skipping certificate deletion, because a domain is still using it."); + return; + } + } + $domain = $document->getAttribute('domain'); $directory = APP_STORAGE_CERTIFICATES . '/' . $domain; $checkTraversal = realpath($directory) === $directory; @@ -535,7 +551,6 @@ class DeletesV1 extends Worker if ($domain && $checkTraversal && is_dir($directory)) { // Delete certificate document, so Appwrite is aware of change if(isset($document['certificateId'])) { - $consoleDB = $this->getConsoleDB(); $consoleDB->deleteDocument('certificates', $document['certificateId']); } From 5897a705365f65f11bd5d4acfb84d4521552c264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 23 Mar 2022 15:07:16 +0000 Subject: [PATCH 08/87] Update comment --- app/workers/certificates.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 183427fede..1df77f9827 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -99,8 +99,8 @@ class CertificatesV1 extends Worker && (($certificate['issueDate'] + ($expiry)) > \time()) ) { // Check last issue time - // Update document anyway, if needed - // This occurs when a cert is already generated because a different project is using the domain + // Update document anyway, if needed. + // This occurs when a cert is already generated because a different project is using the domain. // By updating here we ensure all domains has certificateId assigned (share same certificate document) if(!isset($document['certificateId'])) { $certificate = new Document($certificate); From 933f3372fe32369ee7698897871338385cc87491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 30 Mar 2022 13:31:46 +0000 Subject: [PATCH 09/87] Prevent certificate deletion for main domain --- app/workers/deletes.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/workers/deletes.php b/app/workers/deletes.php index ec920f6943..cf62089165 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -536,6 +536,13 @@ class DeletesV1 extends Worker new Query('certificateId', Query::TYPE_EQUAL, [$document['certificateId']]) ]); + if(!$domainUsingCertificate) { + $mainDomain = App::getEnv('_APP_DOMAIN_TARGET', ''); + if($mainDomain === $document->getAttribute('domain')) { + $domainUsingCertificate = $mainDomain; + } + } + // If certificate is still used by some domain, mark we can't delete. // Current domain should not be found, because we only have copy. Original domain is already deleted from database. if($domainUsingCertificate) { From da6ca5347c2d7eacff90e065330d01112feb2c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 31 Mar 2022 13:10:30 +0000 Subject: [PATCH 10/87] Implemented check and tests for bucket size --- app/controllers/api/storage.php | 5 +++ composer.lock | 41 ++++++++++++---------- tests/e2e/Services/Storage/StorageBase.php | 18 ++++++++++ 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 97e82b553d..69801f1050 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -241,6 +241,11 @@ App::put('/v1/storage/buckets/:bucketId') /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Stats\Stats $usage */ + // Maximum allowed size validation + if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) { + throw new Exception('Bucket maximum file size is larger than _APP_STORAGE_LIMIT.', 400, Exception::ATTRIBUTE_VALUE_INVALID); + } + $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { diff --git a/composer.lock b/composer.lock index 4a9b786b33..04853e05f2 100644 --- a/composer.lock +++ b/composer.lock @@ -300,20 +300,23 @@ }, { "name": "colinmollenhour/credis", - "version": "v1.12.1", + "version": "v1.12.2", "source": { "type": "git", "url": "https://github.com/colinmollenhour/credis.git", - "reference": "c27faa11724229986335c23f4b6d0f1d8d6547fb" + "reference": "77e6ede2e01c4cfaade114fe1e07d2f9756949f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/c27faa11724229986335c23f4b6d0f1d8d6547fb", - "reference": "c27faa11724229986335c23f4b6d0f1d8d6547fb", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/77e6ede2e01c4cfaade114fe1e07d2f9756949f1", + "reference": "77e6ede2e01c4cfaade114fe1e07d2f9756949f1", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": ">=5.6.0" + }, + "suggest": { + "ext-redis": "Improved performance for communicating with redis" }, "type": "library", "autoload": { @@ -338,9 +341,9 @@ "homepage": "https://github.com/colinmollenhour/credis", "support": { "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.12.1" + "source": "https://github.com/colinmollenhour/credis/tree/v1.12.2" }, - "time": "2020-11-06T16:09:14+00:00" + "time": "2022-03-08T18:12:43+00:00" }, { "name": "composer/package-versions-deprecated", @@ -4118,16 +4121,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.6.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706" + "reference": "77a32518733312af16a44300404e945338981de3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706", - "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", + "reference": "77a32518733312af16a44300404e945338981de3", "shasum": "" }, "require": { @@ -4162,9 +4165,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" }, - "time": "2022-01-04T19:58:01+00:00" + "time": "2022-03-15T21:29:03+00:00" }, { "name": "phpspec/prophecy", @@ -6324,16 +6327,16 @@ }, { "name": "twig/twig", - "version": "v3.3.8", + "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "972d8604a92b7054828b539f2febb0211dd5945c" + "reference": "6ff9b0e440fa66f97f207e181c41340ddfa5683d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c", - "reference": "972d8604a92b7054828b539f2febb0211dd5945c", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/6ff9b0e440fa66f97f207e181c41340ddfa5683d", + "reference": "6ff9b0e440fa66f97f207e181c41340ddfa5683d", "shasum": "" }, "require": { @@ -6384,7 +6387,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.3.8" + "source": "https://github.com/twigphp/Twig/tree/v3.3.9" }, "funding": [ { @@ -6396,7 +6399,7 @@ "type": "tidelift" } ], - "time": "2022-02-04T06:59:48+00:00" + "time": "2022-03-25T09:37:52+00:00" }, { "name": "vimeo/psalm", diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index 4796270d68..525c4144db 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -188,6 +188,24 @@ trait StorageBase $this->assertEquals('File extension not allowed', $res['body']['message']); return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id']]; + + /** + * Test for FAILURE create bucket with too high limit (bigger then _APP_STORAGE_LIMIT) + */ + $failedBucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'bucketId' => 'unique()', + 'name' => 'Test Bucket 2', + 'permission' => 'file', + 'maximumFileSize' => 200000000, //200MB + 'allowedFileExtensions' => ["jpg", "png"], + 'read' => ['role:all'], + 'write' => ['role:all'], + ]); + $this->assertEquals(400, $failedBucket['headers']['status-code']); } /** From 1c59c8d8ddda08cc27d7a9bf6f2c4fbda9982495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Fri, 1 Apr 2022 09:00:31 +0000 Subject: [PATCH 11/87] PR review changes --- app/controllers/api/storage.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 69801f1050..e37c691082 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -67,6 +67,11 @@ App::post('/v1/storage/buckets') /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Stats\Stats $usage */ + // Maximum allowed size validation + if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) { + throw new Exception('Bucket maximum file size is larger than _APP_STORAGE_LIMIT.', 400, Exception::ATTRIBUTE_VALUE_INVALID); + } + $bucketId = $bucketId === 'unique()' ? $dbForProject->getId() : $bucketId; try { $files = Config::getParam('collections', [])['files'] ?? []; @@ -243,7 +248,7 @@ App::put('/v1/storage/buckets/:bucketId') // Maximum allowed size validation if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) { - throw new Exception('Bucket maximum file size is larger than _APP_STORAGE_LIMIT.', 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception('Bucket maximum file size allowed is larger than _APP_STORAGE_LIMIT.', 400, Exception::ATTRIBUTE_VALUE_INVALID); } $bucket = $dbForProject->getDocument('buckets', $bucketId); @@ -426,7 +431,7 @@ App::post('/v1/storage/buckets/:bucketId/files') $maximumFileSize = $bucket->getAttribute('maximumFileSize', 0); if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) { - throw new Exception('Error bucket maximum file size is larger than _APP_STORAGE_LIMIT', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception('Error bucket maximum file size is larger than _APP_STORAGE_LIMIT', 400, Exception::STORAGE_INVALID_FILE_SIZE); } $file = $request->getFiles('file'); From caec7ccc894613a6cfa67c3283fa7d99511108fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 4 Apr 2022 09:59:25 +0000 Subject: [PATCH 12/87] WIP: Sessions subQuery refactor --- composer.lock | 53 +++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/composer.lock b/composer.lock index 4a9b786b33..03d8f468d2 100644 --- a/composer.lock +++ b/composer.lock @@ -300,20 +300,23 @@ }, { "name": "colinmollenhour/credis", - "version": "v1.12.1", + "version": "v1.12.2", "source": { "type": "git", "url": "https://github.com/colinmollenhour/credis.git", - "reference": "c27faa11724229986335c23f4b6d0f1d8d6547fb" + "reference": "77e6ede2e01c4cfaade114fe1e07d2f9756949f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/c27faa11724229986335c23f4b6d0f1d8d6547fb", - "reference": "c27faa11724229986335c23f4b6d0f1d8d6547fb", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/77e6ede2e01c4cfaade114fe1e07d2f9756949f1", + "reference": "77e6ede2e01c4cfaade114fe1e07d2f9756949f1", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": ">=5.6.0" + }, + "suggest": { + "ext-redis": "Improved performance for communicating with redis" }, "type": "library", "autoload": { @@ -338,9 +341,9 @@ "homepage": "https://github.com/colinmollenhour/credis", "support": { "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.12.1" + "source": "https://github.com/colinmollenhour/credis/tree/v1.12.2" }, - "time": "2020-11-06T16:09:14+00:00" + "time": "2022-03-08T18:12:43+00:00" }, { "name": "composer/package-versions-deprecated", @@ -3491,16 +3494,16 @@ }, { "name": "felixfbecker/language-server-protocol", - "version": "1.5.1", + "version": "v1.5.2", "source": { "type": "git", "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730" + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730", - "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", "shasum": "" }, "require": { @@ -3541,9 +3544,9 @@ ], "support": { "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1" + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" }, - "time": "2021-02-22T14:02:09+00:00" + "time": "2022-03-02T22:36:06+00:00" }, { "name": "matthiasmullie/minify", @@ -4118,16 +4121,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.6.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706" + "reference": "77a32518733312af16a44300404e945338981de3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706", - "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", + "reference": "77a32518733312af16a44300404e945338981de3", "shasum": "" }, "require": { @@ -4162,9 +4165,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" }, - "time": "2022-01-04T19:58:01+00:00" + "time": "2022-03-15T21:29:03+00:00" }, { "name": "phpspec/prophecy", @@ -6324,16 +6327,16 @@ }, { "name": "twig/twig", - "version": "v3.3.8", + "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "972d8604a92b7054828b539f2febb0211dd5945c" + "reference": "6ff9b0e440fa66f97f207e181c41340ddfa5683d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c", - "reference": "972d8604a92b7054828b539f2febb0211dd5945c", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/6ff9b0e440fa66f97f207e181c41340ddfa5683d", + "reference": "6ff9b0e440fa66f97f207e181c41340ddfa5683d", "shasum": "" }, "require": { @@ -6384,7 +6387,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.3.8" + "source": "https://github.com/twigphp/Twig/tree/v3.3.9" }, "funding": [ { @@ -6396,7 +6399,7 @@ "type": "tidelift" } ], - "time": "2022-02-04T06:59:48+00:00" + "time": "2022-03-25T09:37:52+00:00" }, { "name": "vimeo/psalm", From f69bd80b0e987815b0c48589a5fffe1cd7b66b91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 4 Apr 2022 09:59:32 +0000 Subject: [PATCH 13/87] SubQuery changes --- app/config/collections.php | 2 +- app/controllers/api/account.php | 49 +++++++++++++++------------------ app/controllers/api/users.php | 29 ++++++++----------- app/init.php | 14 ++++++++++ 4 files changed, 49 insertions(+), 45 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index 98f41e8833..1da342767a 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -1056,7 +1056,7 @@ $collections = [ 'required' => false, 'default' => [], 'array' => true, - 'filters' => ['json'], + 'filters' => ['subQuerySessions'], ], [ '$id' => 'tokens', diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index f0c10dd5ea..7b190b8a1d 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -28,6 +28,8 @@ use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; +use function PHPUnit\Framework\isEmpty; + $oauthDefaultSuccess = '/v1/auth/oauth2/success'; $oauthDefaultFailure = '/v1/auth/oauth2/failure'; @@ -208,8 +210,7 @@ App::post('/v1/account/sessions') ->setAttribute('$write', ['user:' . $profile->getId()]) ); - $profile->setAttribute('sessions', $session, Document::SET_TYPE_APPEND); - $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile); + $dbForProject->deleteCachedDocument('users', $profile->getId()); $audits ->setParam('userId', $profile->getId()) @@ -458,13 +459,10 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $current = Auth::sessionVerify($sessions, Auth::$secret); if ($current) { // Delete current session of new one. - foreach ($sessions as $key => $session) {/** @var Document $session */ - if ($current === $session['$id']) { - unset($sessions[$key]); - - $dbForProject->deleteDocument('sessions', $session->getId()); - $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $sessions)); - } + $currentDocument = $dbForProject->getDocument('sessions', $current); + if(!$currentDocument->isEmpty()) { + $dbForProject->deleteDocument('sessions', $currentDocument->getId()); + $dbForProject->deleteCachedDocument('users', $user->getId()); } } @@ -553,7 +551,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $user ->setAttribute('status', true) - ->setAttribute('sessions', $session, Document::SET_TYPE_APPEND) ; Authorization::setRole('user:' . $user->getId()); @@ -563,7 +560,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->setAttribute('$write', ['user:' . $user->getId()]) ); - $user = $dbForProject->updateDocument('users', $user->getId(), $user); + $dbForProject->deleteCachedDocument('users', $user->getId()); $audits ->setParam('userId', $user->getId()) @@ -825,6 +822,8 @@ App::put('/v1/account/sessions/magic-url') ->setAttribute('$write', ['user:' . $user->getId()]) ); + $dbForProject->deleteCachedDocument('users', $user->getId()); + $tokens = $user->getAttribute('tokens', []); /** @@ -838,10 +837,8 @@ App::put('/v1/account/sessions/magic-url') } $user - ->setAttribute('sessions', $session, Document::SET_TYPE_APPEND) ->setAttribute('tokens', $tokens); - $user = $dbForProject->updateDocument('users', $user->getId(), $user); if (false === $user) { @@ -987,8 +984,7 @@ App::post('/v1/account/sessions/anonymous') ->setAttribute('$write', ['user:' . $user->getId()]) ); - $user = $dbForProject->updateDocument('users', $user->getId(), - $user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND)); + $dbForProject->deleteCachedDocument('users', $user->getId()); $audits ->setParam('userId', $user->getId()) @@ -1041,22 +1037,17 @@ App::post('/v1/account/jwt') ->label('abuse-key', 'url:{url},userId:{userId}') ->inject('response') ->inject('user') - ->action(function ($response, $user) { + ->inject('dbForProject') + ->action(function ($response, $user, $dbForProject) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Document $user */ + /** @var Utopia\Database\Database $dbForProject */ - $sessions = $user->getAttribute('sessions', []); - $current = new Document(); + $current = $dbForProject->findOne('sessions', [ + new Query('secret', Query::TYPE_EQUAL, [Auth::hash(Auth::$secret)]) + ]); - foreach ($sessions as $session) { - /** @var Utopia\Database\Document $session */ - - if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too - $current = $session; - } - } - - if ($current->isEmpty()) { + if (!$current) { throw new Exception('No valid session found', 404, Exception::USER_SESSION_NOT_FOUND); } @@ -1278,6 +1269,7 @@ App::get('/v1/account/sessions/:sessionId') /** @var Utopia\Database\Database $dbForProject */ /** @var Appwrite\Stats\Stats $usage */ + // TODO: Matej refactor $sessions = $user->getAttribute('sessions', []); $sessionId = ($sessionId === 'current') ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) @@ -1603,6 +1595,7 @@ App::delete('/v1/account/sessions/:sessionId') ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) : $sessionId; + // TODO: Matej refactor $sessions = $user->getAttribute('sessions', []); foreach ($sessions as $key => $session) {/** @var Document $session */ @@ -1693,6 +1686,7 @@ App::patch('/v1/account/sessions/:sessionId') ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) : $sessionId; + // TODO: Matej refactor $sessions = $user->getAttribute('sessions', []); foreach ($sessions as $key => $session) {/** @var Document $session */ @@ -1788,6 +1782,7 @@ App::delete('/v1/account/sessions') $protocol = $request->getProtocol(); $sessions = $user->getAttribute('sessions', []); + // TODO: Matej refactor foreach ($sessions as $session) {/** @var Document $session */ $dbForProject->deleteDocument('sessions', $session->getId()); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 6bb14c5003..2732421c33 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -622,25 +622,20 @@ App::delete('/v1/users/:userId/sessions/:sessionId') throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); } - $sessions = $user->getAttribute('sessions', []); + $session = $dbForProject->getDocument('sessions', $sessionId); - foreach ($sessions as $key => $session) { /** @var Document $session */ - - if ($sessionId == $session->getId()) { - unset($sessions[$key]); - - $dbForProject->deleteDocument('sessions', $session->getId()); - - $user->setAttribute('sessions', $sessions); - - $events - ->setParam('eventData', $response->output($user, Response::MODEL_USER)) - ; - - $dbForProject->updateDocument('users', $user->getId(), $user); - } + if($session->isEmpty()) { + throw new Exception('User not found', 404, Exception::USER_SESSION_NOT_FOUND); } + $dbForProject->deleteDocument('sessions', $session->getId()); + + $events + ->setParam('eventData', $response->output($user, Response::MODEL_USER)) + ; + + $dbForProject->deleteCachedDocument('users', $user->getId()); + $usage ->setParam('users.update', 1) ->setParam('users.sessions.delete', 1) @@ -683,7 +678,7 @@ App::delete('/v1/users/:userId/sessions') $dbForProject->deleteDocument('sessions', $session->getId()); } - $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', [])); + $dbForProject->deleteCachedDocument('users', $user->getId()); $events ->setParam('eventData', $response->output($user, Response::MODEL_USER)) diff --git a/app/init.php b/app/init.php index fd509297f6..d14c77d755 100644 --- a/app/init.php +++ b/app/init.php @@ -301,6 +301,18 @@ Database::addFilter('subQueryWebhooks', } ); +Database::addFilter('subQuerySessions', + function($value) { + return null; + }, + function($value, Document $document, Database $database) { + return $database + ->find('sessions', [ + new Query('userId', Query::TYPE_EQUAL, [$document->getId()]) + ], $database->getIndexLimit(), 0, []); + } +); + Database::addFilter('encrypt', function($value) { $key = App::getEnv('_APP_OPENSSL_KEY_V1'); @@ -693,6 +705,8 @@ App::setResource('user', function($mode, $project, $console, $request, $response $user = $dbForConsole->getDocument('users', Auth::$unique); } + \var_dump($user); + if ($user->isEmpty() // Check a document has been found in the DB || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)) { // Validate user has valid login token $user = new Document(['$id' => '', '$collection' => 'users']); From 41107098745260b62a3d6229a5ce5b5c0ff0575f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 5 Apr 2022 16:57:22 +0000 Subject: [PATCH 14/87] PR review changes --- app/config/errors.php | 6 +++--- app/controllers/general.php | 2 +- src/Appwrite/Extend/Exception.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index ca305ed0b5..6ebc311798 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -78,9 +78,9 @@ return [ 'description' => 'An internal server error occurred.', 'code' => 500, ], - Exception::GENERAL_FORCED_HTTPS_IGNORED => [ - 'name' => Exception::GENERAL_FORCED_HTTPS_IGNORED, - 'description' => 'Appwrite instance forces secure communication. Switch to HTTPS protocol in your endpoint.', + Exception::GENERAL_METHOD_UNSUPPORTED => [ + 'name' => Exception::GENERAL_METHOD_UNSUPPORTED, + 'description' => 'The HTTP method is unsupported by the current protocol or resource.', 'code' => 500, ], diff --git a/app/controllers/general.php b/app/controllers/general.php index 4cf77bb4bb..30e2d4cd96 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -174,7 +174,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS if ($request->getProtocol() !== 'https') { if($request->getMethod() !== Request::METHOD_GET) { - throw new Exception('HTTPS communication required.', 500, Exception::GENERAL_FORCED_HTTPS_IGNORED); + throw new Exception('Method unsupported over HTTP.', 405, Exception::GENERAL_METHOD_UNSUPPORTED); } return $response->redirect('https://'.$request->getHostname().$request->getURI()); diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index a5858d662b..04b44b57b2 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -46,7 +46,7 @@ class Exception extends \Exception const GENERAL_ROUTE_NOT_FOUND = 'general_route_not_found'; const GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found'; const GENERAL_SERVER_ERROR = 'general_server_error'; - const GENERAL_FORCED_HTTPS_IGNORED = 'general_forced_https_ignored'; + const GENERAL_METHOD_UNSUPPORTED = 'general_method_unsupported'; /** Users */ const USER_COUNT_EXCEEDED = 'user_count_exceeded'; From 752079c87455cd895076cb15c65f5ea607b8ae4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 10 Apr 2022 09:38:22 +0000 Subject: [PATCH 15/87] Refactored SSL genration --- app/cli.php | 3 + app/controllers/api/projects.php | 4 +- app/controllers/general.php | 8 +- app/tasks/maintenance.php | 96 ++++++- app/tasks/ssl.php | 12 +- app/views/install/compose.phtml | 3 + app/workers/certificates.php | 351 ++++++++++++++----------- composer.lock | 115 ++++---- docker-compose.yml | 6 + src/Appwrite/Exception/Certificate.php | 38 +++ 10 files changed, 412 insertions(+), 224 deletions(-) create mode 100644 src/Appwrite/Exception/Certificate.php diff --git a/app/cli.php b/app/cli.php index d2c6496dc3..8902aad886 100644 --- a/app/cli.php +++ b/app/cli.php @@ -5,6 +5,9 @@ require_once __DIR__.'/controllers/general.php'; use Utopia\App; use Utopia\CLI\CLI; use Utopia\CLI\Console; +use Utopia\Database\Validator\Authorization; + +Authorization::disable(); $cli = new CLI(); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 2bee190628..eefabe43a1 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -2,6 +2,7 @@ use Appwrite\Auth\Auth; use Appwrite\Auth\Validator\Password; +use Appwrite\Event\Event; use Appwrite\Network\Validator\CNAME; use Appwrite\Network\Validator\Domain as DomainValidator; use Appwrite\Network\Validator\Origin; @@ -1389,8 +1390,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') $dbForConsole->deleteCachedDocument('projects', $project->getId()); // Issue a TLS certificate when domain is verified - Resque::enqueue('v1-certificates', 'CertificatesV1', [ - 'document' => $domain->getArrayCopy(), + Resque::enqueue(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, [ 'domain' => $domain->getAttribute('domain'), ]); diff --git a/app/controllers/general.php b/app/controllers/general.php index a65e07c81c..3092e537c1 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -12,6 +12,7 @@ use Appwrite\Extend\Exception; use Utopia\Config\Config; use Utopia\Domains\Domain; use Appwrite\Auth\Auth; +use Appwrite\Event\Event; use Appwrite\Network\Validator\Origin; use Appwrite\Utopia\Response\Filters\V11 as ResponseV11; use Appwrite\Utopia\Response\Filters\V12 as ResponseV12; @@ -90,11 +91,8 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons Console::info('Issuing a TLS certificate for the master domain (' . $domain->get() . ') in a few seconds...'); - Resque::enqueue('v1-certificates', 'CertificatesV1', [ - 'document' => $domainDocument, - 'domain' => $domain->get(), - 'validateTarget' => false, - 'validateCNAME' => false, + Resque::enqueue(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, [ + 'domain' => $domain->get() ]); } diff --git a/app/tasks/maintenance.php b/app/tasks/maintenance.php index a6f37ff71b..a50d8c4da9 100644 --- a/app/tasks/maintenance.php +++ b/app/tasks/maintenance.php @@ -1,15 +1,58 @@ get('dbPool')->get(); + $redis = $register->get('redisPool')->get(); + + $cache = new Cache(new RedisCache($redis)); + $database = new Database(new MariaDB($db), $cache); + $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite')); + $database->setNamespace('_console'); + + break; // leave loop if successful + } catch(\Exception $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= DATABASE_RECONNECT_MAX_ATTEMPTS) { + throw new \Exception('Failed to connect to database: '. $e->getMessage()); + } + sleep(DATABASE_RECONNECT_SLEEP); + } + } while ($attempts < DATABASE_RECONNECT_MAX_ATTEMPTS); + + return [ + $database, + function () use ($register, $db, $redis) { + $register->get('dbPool')->put($db); + $register->get('redisPool')->put($redis); + } + ]; + +}; $cli ->task('maintenance') ->desc('Schedules maintenance tasks and publishes them to resque') - ->action(function () { + ->action(function () use ($register) { Console::title('Maintenance V1'); Console::success(APP_NAME.' maintenance process v1 has started'); @@ -54,6 +97,29 @@ $cli ]); } + function renewCertificates($dbForConsole) + { + $time = date('d-m-Y H:i:s', time()); + /** @var Utopia\Database\Database $dbForConsole */ + + $certificates = $dbForConsole->find('certificates', [ + new Query('attempts', Query::TYPE_LESSER, [5]), // Maximum 5 attempts + new Query('renewDate', Query::TYPE_LESSEREQUAL, [\time()]) // includes 60 days cooldown (we have 30 days to renew) + ], 300); // Limit 300 comes from LetsEncrypt (orders per 3 hours) + + if(\count($certificates) > 0) { + Console::info("[{$time}] Found " . \count($certificates) . " certificates for renewal, scheduling jobs."); + + foreach ($certificates as $certificate) { + Resque::enqueue(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, [ + 'domain' => $certificate->getAttribute('domain'), + ]); + } + } else { + Console::info("[{$time}] No certificates for renewal."); + } + } + // # of days in seconds (1 day = 86400s) $interval = (int) App::getEnv('_APP_MAINTENANCE_INTERVAL', '86400'); $executionLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', '1209600'); @@ -62,13 +128,25 @@ $cli $usageStatsRetention30m = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_30M', '129600');//36 hours $usageStatsRetention1d = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_1D', '8640000'); // 100 days - Console::loop(function() use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) { - $time = date('d-m-Y H:i:s', time()); - Console::info("[{$time}] Notifying deletes workers every {$interval} seconds"); - notifyDeleteExecutionLogs($executionLogsRetention); - notifyDeleteAbuseLogs($abuseLogsRetention); - notifyDeleteAuditLogs($auditLogRetention); - notifyDeleteUsageStats($usageStatsRetention30m, $usageStatsRetention1d); - notifyDeleteConnections(); + Console::loop(function() use ($register, $interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) { + go(function () use ($register, $interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) { + try { + [$database, $returnDatabase] = getDatabase($register, '_console'); + + $time = date('d-m-Y H:i:s', time()); + Console::info("[{$time}] Notifying deletes workers every {$interval} seconds"); + notifyDeleteExecutionLogs($executionLogsRetention); + notifyDeleteAbuseLogs($abuseLogsRetention); + notifyDeleteAuditLogs($auditLogRetention); + notifyDeleteUsageStats($usageStatsRetention30m, $usageStatsRetention1d); + notifyDeleteConnections(); + + renewCertificates($database); + } catch (\Throwable $th) { + throw $th; + } finally { + call_user_func($returnDatabase); + } + }); }, $interval); }); \ No newline at end of file diff --git a/app/tasks/ssl.php b/app/tasks/ssl.php index 2c32324fa3..6010c3fd1f 100644 --- a/app/tasks/ssl.php +++ b/app/tasks/ssl.php @@ -11,13 +11,15 @@ $cli ->action(function () { $domain = App::getEnv('_APP_DOMAIN', ''); - Console::log('Issue a TLS certificate for master domain ('.$domain.') in 30 seconds. + // TODO: Instead of waiting, let's ping Traefik. If responds, we can schedule instantly + // TODO: Add support for argument (domain) + + Console::log('Issue a TLS certificate for master domain ('.$domain.') in 2 seconds. Make sure your domain points to your server or restart to try again.'); - ResqueScheduler::enqueueAt(\time() + 30, 'v1-certificates', 'CertificatesV1', [ - 'document' => [], + // Const for types not available here + ResqueScheduler::enqueueAt(\time() + 2, 'v1-certificates', 'CertificatesV1', [ 'domain' => $domain, - 'validateTarget' => false, - 'validateCNAME' => false, + 'skipRenewCheck' => true // TODO: Discuss this behabiour. true? false? parameter? How do we document it? ]); }); \ No newline at end of file diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 5740c8680d..bb3fa8d8f3 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -449,6 +449,9 @@ services: environment: - _APP_ENV - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_DOMAIN_TARGET + - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 9f9ce33dbd..fea025d163 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -1,7 +1,9 @@ getConsoleDB(); - - /** - * 1. Get new domain document - DONE - * 1.1. Validate domain is valid, public suffix is known and CNAME records are verified - DONE - * 2. Check if a certificate already exists - DONE - * 3. Check if certificate is about to expire, if not - skip it - * 3.1. Create / renew certificate - * 3.2. Update loadblancer - * 3.3. Update database (domains, change date, expiry) - * 3.4. Set retry on failure - * 3.5. Schedule to renew certificate in 60 days - */ - Authorization::disable(); - // Args - $document = $this->args['document']; - $domain = $this->args['domain']; + $dbForConsole = $this->getConsoleDB(); - // Validation Args - $validateTarget = $this->args['validateTarget'] ?? true; - $validateCNAME = $this->args['validateCNAME'] ?? true; + $certificate = new Document(); - // Options - $domain = new Domain((!empty($domain)) ? $domain : ''); - $expiry = 60 * 60 * 24 * 30 * 2; // 60 days - $safety = 60 * 60; // 1 hour - $renew = (\time() + $expiry); + try { + /** + * TODO: Update + * 1. Get new domain document - DONE + * 1.1. Validate domain is valid, public suffix is known and CNAME records are verified - DONE + * 2. Check if a certificate already exists - DONE + * 3. Check if certificate is about to expire, if not - skip it + * 3.1. Create / renew certificate + * 3.2. Update loadblancer + * 3.3. Update database (domains, change date, expiry) + * 3.4. Set retry on failure + * 3.5. Schedule to renew certificate in 60 days + */ + + + // Get attributes + $domain = $this->args['domain']; // String of domain (hostname) + $domain = new Domain((!empty($domain)) ? $domain : ''); - if (empty($domain->get())) { - throw new Exception('Missing domain'); - } - - if (!$domain->isKnown() || $domain->isTest()) { - throw new Exception('Unknown public suffix for domain'); - } - - if ($validateTarget) { - $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); - - if(!$target->isKnown() || $target->isTest()) { - throw new Exception('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.'); + $certificate->setAttribute('domain', $domain->get()); + + $skipRenewCheck = $this->args['skipRenewCheck'] ?? false; // If true, we won't double-check expiry from cert file + + $mainDomain = null; // ENV or first ever visited domain + if (!empty(App::getEnv('_APP_DOMAIN', ''))) { + $mainDomain = App::getEnv('_APP_DOMAIN', ''); + } else { + $domainDocument = $dbForConsole->findOne('domains', [], 0, ['_id'], ['ASC']); + $mainDomain = $domainDocument ? $domainDocument->getAttribute('domain') : $domain->get(); } - } - - if ($validateCNAME) { - $validator = new CNAME($target->get()); // Verify Domain with DNS records - - if(!$validator->isValid($domain->get())) { - throw new Exception('Failed to verify domain DNS records'); + + // If not main domain, we will check CNAME record + $validateCNAME = false; + if ($domain->get() !== $mainDomain) { + $validateCNAME = true; } - } - - $certificate = $dbForConsole->findOne('certificates', [ - new Query('domain', QUERY::TYPE_EQUAL, [$domain->get()]) - ]); - - // $condition = ($certificate - // && $certificate instanceof Document - // && isset($certificate['issueDate']) - // && (($certificate['issueDate'] + ($expiry)) > time())) ? 'true' : 'false'; - - // throw new Exception('cert issued at'.date('d.m.Y H:i', $certificate['issueDate']).' | renew date is: '.date('d.m.Y H:i', ($certificate['issueDate'] + ($expiry))).' | condition is '.$condition); - - $certificate = (!empty($certificate) && $certificate instanceof $certificate) ? $certificate->getArrayCopy() : []; - - if ( - !empty($certificate) - && isset($certificate['issueDate']) - && (($certificate['issueDate'] + ($expiry)) > \time()) - ) { // Check last issue time - throw new Exception('Renew isn\'t required'); - } - - $staging = (App::isProduction()) ? '' : ' --dry-run'; - $email = App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'); - - if (empty($email)) { - throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate'); - } - - $stdout = ''; - $stderr = ''; - - $exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}" - . " --email " . $email - . " -w " . APP_STORAGE_CERTIFICATES - . " -d {$domain->get()}", '', $stdout, $stderr); - - if ($exit !== 0) { - throw new Exception('Failed to issue a certificate with message: ' . $stderr); - } - - $path = APP_STORAGE_CERTIFICATES . '/' . $domain->get(); - - if (!\is_readable($path)) { - if (!\mkdir($path, 0755, true)) { - throw new Exception('Failed to create path...'); + + if (empty($domain->get())) { + throw new ExceptionCertificate('Missing certificate domain.'); + } + + if (!$domain->isKnown() || $domain->isTest()) { + throw new ExceptionCertificate('Unknown public suffix for domain.'); + } + + if ($validateCNAME) { + // TODO: Would be awesome to also support A/AAAA records here. Maybe dry run? + + // Validate if domain target is properly configured + $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); + + if (!$target->isKnown() || $target->isTest()) { + throw new ExceptionCertificate('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.'); + } + + // Verify domain with DNS records + $validator = new CNAME($target->get()); + if (!$validator->isValid($domain->get())) { + throw new ExceptionCertificate('Failed to verify domain DNS records.'); + } + } else { + // Main domain validation + // TODO: Would be awesome to check A/AAAA record here. Maybe dry run? } - } - if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/cert.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/cert.pem')) { - throw new Exception('Failed to rename certificate cert.pem: '.\json_encode($stdout)); - } + // If certificate exists already, double-check expiry date + // If asked to skip, we won't + $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/cert.pem'; + if (!$skipRenewCheck && \file_exists($certPath)) { + $validTo = null; - if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/chain.pem')) { - throw new Exception('Failed to rename certificate chain.pem: ' . \json_encode($stdout)); - } + try { + $certData = openssl_x509_parse(file_get_contents($certPath)); + + $validTo = $certData['validTo_time_t']; + + if (empty($validTo)) { + throw new Exception('Invalid expiry date.'); + } + } catch(\Throwable $th) { + throw new ExceptionCertificate('Unable to read certificate file (cert.pem).'); + } - if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/fullchain.pem')) { - throw new Exception('Failed to rename certificate fullchain.pem: ' . \json_encode($stdout)); - } + // LetsEncrypt allows renewal 30 days before expiry + $expiryInAdvance = (60*60*24*30); + if ($validTo - $expiryInAdvance > \time()) { + $validToVerbose = date('d-m-Y H:i:s', $validTo); + throw new ExceptionCertificate('Renew isn\'t required. Next renew at ' . $validToVerbose); + } + } + + // Email for alerts is required by LetsEncrypt + $email = App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'); + if (empty($email)) { + throw new ExceptionCertificate('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate.'); + } - if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/privkey.pem')) { - throw new Exception('Failed to rename certificate privkey.pem: ' . \json_encode($stdout)); - } + // LetsEncrypt communication to issue certificate (using certbot CLI) + $stdout = ''; + $stderr = ''; + + $staging = (App::isProduction()) ? '' : ' --dry-run'; + $exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}" + . " --email " . $email + . " -w " . APP_STORAGE_CERTIFICATES + . " -d {$domain->get()}", '', $stdout, $stderr); + + // All exceptions from now on will be marked to increment attempts count. This allows us to only limit attempts for domains that failed on LectEncrypt side. + // Such attempts count allows us to prevent API limit abuse with always failing domains + + // Unexpected error, usually 5XX, API limits, ... + if ($exit !== 0) { + throw new ExceptionCertificate('Failed to issue a certificate with message: ' . $stderr, true); + } - $certificate = new Document(\array_merge($certificate, [ - 'domain' => $domain->get(), - 'issueDate' => \time(), - 'renewDate' => $renew, - 'attempts' => 0, - 'log' => \json_encode($stdout), - ])); - - $certificate = $dbForConsole->createDocument('certificates', $certificate); - - if (!$certificate) { - throw new Exception('Failed saving certificate to DB'); - } - - if(!empty($document)) { - $certificate = new Document(\array_merge($document, [ - 'updated' => \time(), - 'certificateId' => $certificate->getId(), + // Command succeeded, store all data into document + // We store stderr too, because it may include warnings + // This is only stored if everytng below passes too. Otherwise, it will be overwritten by error message + $certificate->setAttribute('log', \json_encode([ + 'stdout' => $stdout, + 'stderr' => $stderr, ])); - - $certificate = $dbForConsole->updateDocument('domains', $certificate->getId(), $certificate); - - if(!$certificate) { - throw new Exception('Failed saving domain to DB'); + + // Prepare folder in storage for domain + $path = APP_STORAGE_CERTIFICATES . '/' . $domain->get(); + if (!\is_readable($path)) { + if (!\mkdir($path, 0755, true)) { + throw new ExceptionCertificate('Failed to create path for certificate.', true); + } } + + // Move generated files from certbot into our storage + if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/cert.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/cert.pem')) { + throw new ExceptionCertificate('Failed to rename certificate cert.pem: '.\json_encode($stdout), true); + } + + if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/chain.pem')) { + throw new ExceptionCertificate('Failed to rename certificate chain.pem: ' . \json_encode($stdout), true); + } + + if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/fullchain.pem')) { + throw new ExceptionCertificate('Failed to rename certificate fullchain.pem: ' . \json_encode($stdout), true); + } + + if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/privkey.pem')) { + throw new ExceptionCertificate('Failed to rename certificate privkey.pem: ' . \json_encode($stdout), true); + } + + // This multi-line syntax helps IDE + $config = + "tls:" . + " certificates:" . + " - certFile: /storage/certificates/{$domain->get()}/fullchain.pem" . + " keyFile: /storage/certificates/{$domain->get()}/privkey.pem"; + + // Save configuration into Traefik using our new cert files + if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain->get() . '.yml', $config)) { + throw new ExceptionCertificate('Failed to save Traefik configuration.', true); + } + + // Read new renew date from cert file + // TODO: This might not be required, we could calculate it. But this feels safer + $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/cert.pem'; + $certData = openssl_x509_parse(file_get_contents($certPath)); + $validTo = $certData['validTo_time_t']; + $expiryInAdvance = (60*60*24*30); + $certificate->setAttribute('renewDate', $validTo - $expiryInAdvance); + + // All went well at this point 🥳 + + // Reset attempts count for next renwal + $certificate->setAttribute('attempts', 0); + + // Mark issue date + $certificate->setAttribute('issueDate', \time()); + } catch(ExceptionCertificate $e) { + // These exceptions are expected if renew shouldn't or can't happen + + // Add exception as log into certificate + $certificate->setAttribute('log', $e->getMessage()); + + $attempt = $certificate->getAttribute('attempts', 0); + $attempt++; + + // Save increased attempts count if requested by exception + if($e->getIncrementAttempts()) { + $certificate->setAttribute('attempts', $attempt); + } + + Console::warning('Cannot renew domain (' . $domain->get() . ') on attempt no. ' . $attempt . ' certificate: ' . $e->getMessage()); + } finally { + // All actions result in new updatedAt date + $certificate->setAttribute('updated', \time()); + + // Save certificate data into database + // Check if update or insert required + $certificateDocument = $dbForConsole->findOne('certificates', [ new Query('domain', Query::TYPE_EQUAL, [$domain->get()]) ]); + if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { + // Merge new data with current data + $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); + + $certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); + } else { + $certificate = $dbForConsole->createDocument('certificates', $certificate); + } + + // Update domains with new certificate ID + $certificateId = $certificate->getId(); + // TODO: Add logic for updating domains + + Authorization::reset(); } - - $config = -"tls: - certificates: - - certFile: /storage/certificates/{$domain->get()}/fullchain.pem - keyFile: /storage/certificates/{$domain->get()}/privkey.pem"; - - if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain->get() . '.yml', $config)) { - throw new Exception('Failed to save SSL configuration'); - } - - ResqueScheduler::enqueueAt($renew + $safety, 'v1-certificates', 'CertificatesV1', [ - 'document' => [], - 'domain' => $domain->get(), - 'validateTarget' => $validateTarget, - 'validateCNAME' => $validateCNAME, - ]); // Async task rescheduale - - Authorization::reset(); } public function shutdown(): void diff --git a/composer.lock b/composer.lock index 4a9b786b33..00ea8b8806 100644 --- a/composer.lock +++ b/composer.lock @@ -300,20 +300,23 @@ }, { "name": "colinmollenhour/credis", - "version": "v1.12.1", + "version": "v1.13.0", "source": { "type": "git", "url": "https://github.com/colinmollenhour/credis.git", - "reference": "c27faa11724229986335c23f4b6d0f1d8d6547fb" + "reference": "afec8e58ec93d2291c127fa19709a048f28641e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/c27faa11724229986335c23f4b6d0f1d8d6547fb", - "reference": "c27faa11724229986335c23f4b6d0f1d8d6547fb", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/afec8e58ec93d2291c127fa19709a048f28641e5", + "reference": "afec8e58ec93d2291c127fa19709a048f28641e5", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": ">=5.6.0" + }, + "suggest": { + "ext-redis": "Improved performance for communicating with redis" }, "type": "library", "autoload": { @@ -338,9 +341,9 @@ "homepage": "https://github.com/colinmollenhour/credis", "support": { "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.12.1" + "source": "https://github.com/colinmollenhour/credis/tree/v1.13.0" }, - "time": "2020-11-06T16:09:14+00:00" + "time": "2022-04-07T14:57:22+00:00" }, { "name": "composer/package-versions-deprecated", @@ -1580,16 +1583,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced" + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/c726b64c1ccfe2896cb7df2e1331c357ad1c8ced", - "reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", "shasum": "" }, "require": { @@ -1627,7 +1630,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1" }, "funding": [ { @@ -1643,7 +1646,7 @@ "type": "tidelift" } ], - "time": "2021-11-01T23:48:49+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3192,16 +3195,16 @@ }, { "name": "composer/semver", - "version": "3.3.1", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "5d8e574bb0e69188786b8ef77d43341222a41a71" + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/5d8e574bb0e69188786b8ef77d43341222a41a71", - "reference": "5d8e574bb0e69188786b8ef77d43341222a41a71", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", "shasum": "" }, "require": { @@ -3253,7 +3256,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.1" + "source": "https://github.com/composer/semver/tree/3.3.2" }, "funding": [ { @@ -3269,7 +3272,7 @@ "type": "tidelift" } ], - "time": "2022-03-16T11:22:07+00:00" + "time": "2022-04-01T19:23:25+00:00" }, { "name": "composer/xdebug-handler", @@ -3491,16 +3494,16 @@ }, { "name": "felixfbecker/language-server-protocol", - "version": "1.5.1", + "version": "v1.5.2", "source": { "type": "git", "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730" + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730", - "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", "shasum": "" }, "require": { @@ -3541,9 +3544,9 @@ ], "support": { "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1" + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" }, - "time": "2021-02-22T14:02:09+00:00" + "time": "2022-03-02T22:36:06+00:00" }, { "name": "matthiasmullie/minify", @@ -4118,16 +4121,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.6.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706" + "reference": "77a32518733312af16a44300404e945338981de3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706", - "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", + "reference": "77a32518733312af16a44300404e945338981de3", "shasum": "" }, "require": { @@ -4162,9 +4165,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" }, - "time": "2022-01-04T19:58:01+00:00" + "time": "2022-03-15T21:29:03+00:00" }, { "name": "phpspec/prophecy", @@ -5073,16 +5076,16 @@ }, { "name": "sebastian/environment", - "version": "5.1.3", + "version": "5.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac" + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", "shasum": "" }, "require": { @@ -5124,7 +5127,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" }, "funding": [ { @@ -5132,7 +5135,7 @@ "type": "github" } ], - "time": "2020-09-28T05:52:38+00:00" + "time": "2022-04-03T09:37:03+00:00" }, { "name": "sebastian/exporter", @@ -5715,16 +5718,16 @@ }, { "name": "symfony/console", - "version": "v6.0.5", + "version": "v6.0.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3bebf4108b9e07492a2a4057d207aa5a77d146b1" + "reference": "70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3bebf4108b9e07492a2a4057d207aa5a77d146b1", - "reference": "3bebf4108b9e07492a2a4057d207aa5a77d146b1", + "url": "https://api.github.com/repos/symfony/console/zipball/70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e", + "reference": "70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e", "shasum": "" }, "require": { @@ -5790,7 +5793,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.5" + "source": "https://github.com/symfony/console/tree/v6.0.7" }, "funding": [ { @@ -5806,7 +5809,7 @@ "type": "tidelift" } ], - "time": "2022-02-25T10:48:52+00:00" + "time": "2022-03-31T17:18:25+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -6058,16 +6061,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.0.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "36715ebf9fb9db73db0cb24263c79077c6fe8603" + "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/36715ebf9fb9db73db0cb24263c79077c6fe8603", - "reference": "36715ebf9fb9db73db0cb24263c79077c6fe8603", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e517458f278c2131ca9f262f8fbaf01410f2c65c", + "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c", "shasum": "" }, "require": { @@ -6120,7 +6123,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.0.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.0.1" }, "funding": [ { @@ -6136,7 +6139,7 @@ "type": "tidelift" } ], - "time": "2021-11-04T17:53:12+00:00" + "time": "2022-03-13T20:10:05+00:00" }, { "name": "symfony/string", @@ -6324,16 +6327,16 @@ }, { "name": "twig/twig", - "version": "v3.3.8", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "972d8604a92b7054828b539f2febb0211dd5945c" + "reference": "8442df056c51b706793adf80a9fd363406dd3674" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c", - "reference": "972d8604a92b7054828b539f2febb0211dd5945c", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/8442df056c51b706793adf80a9fd363406dd3674", + "reference": "8442df056c51b706793adf80a9fd363406dd3674", "shasum": "" }, "require": { @@ -6384,7 +6387,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.3.8" + "source": "https://github.com/twigphp/Twig/tree/v3.3.10" }, "funding": [ { @@ -6396,7 +6399,7 @@ "type": "tidelift" } ], - "time": "2022-02-04T06:59:48+00:00" + "time": "2022-04-06T06:47:41+00:00" }, { "name": "vimeo/psalm", @@ -6580,5 +6583,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } diff --git a/docker-compose.yml b/docker-compose.yml index 2ddd5dd51b..ac990eef88 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -370,6 +370,7 @@ services: environment: - _APP_ENV - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN - _APP_DOMAIN_TARGET - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_REDIS_HOST @@ -519,6 +520,11 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS - _APP_MAINTENANCE_INTERVAL - _APP_MAINTENANCE_RETENTION_EXECUTION - _APP_MAINTENANCE_RETENTION_ABUSE diff --git a/src/Appwrite/Exception/Certificate.php b/src/Appwrite/Exception/Certificate.php new file mode 100644 index 0000000000..9a146807d7 --- /dev/null +++ b/src/Appwrite/Exception/Certificate.php @@ -0,0 +1,38 @@ +incrementAttempts = $incrementAttempts; + + parent::__construct($message); + } + + /** + * Get value if attempts should be incremented. + * + * @return boolean + */ + public function getIncrementAttempts(): bool + { + return $this->incrementAttempts; + } + + /** + * Set if attempts should be incremented + * + * @param boolean $value + * + * @return void + */ + public function setIncrementAttempts(bool $value): void + { + $this->incrementAttempts = $value; + } +} \ No newline at end of file From 50412c7e806865f309daff1b0fd4ca1a6a3ada16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 11 Apr 2022 07:56:58 +0000 Subject: [PATCH 16/87] PR review changes --- app/tasks/maintenance.php | 4 +- app/tasks/ssl.php | 29 +++++++---- app/workers/certificates.php | 69 ++++++++++++++++---------- src/Appwrite/Exception/Certificate.php | 31 ------------ 4 files changed, 64 insertions(+), 69 deletions(-) diff --git a/app/tasks/maintenance.php b/app/tasks/maintenance.php index a50d8c4da9..e946daeb26 100644 --- a/app/tasks/maintenance.php +++ b/app/tasks/maintenance.php @@ -105,7 +105,7 @@ $cli $certificates = $dbForConsole->find('certificates', [ new Query('attempts', Query::TYPE_LESSER, [5]), // Maximum 5 attempts new Query('renewDate', Query::TYPE_LESSEREQUAL, [\time()]) // includes 60 days cooldown (we have 30 days to renew) - ], 300); // Limit 300 comes from LetsEncrypt (orders per 3 hours) + ], 200); // Limit 200 comes from LetsEncrypt (300 orders per 3 hours, keeping some for new domains) if(\count($certificates) > 0) { Console::info("[{$time}] Found " . \count($certificates) . " certificates for renewal, scheduling jobs."); @@ -134,7 +134,7 @@ $cli [$database, $returnDatabase] = getDatabase($register, '_console'); $time = date('d-m-Y H:i:s', time()); - Console::info("[{$time}] Notifying deletes workers every {$interval} seconds"); + Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds"); notifyDeleteExecutionLogs($executionLogsRetention); notifyDeleteAbuseLogs($abuseLogsRetention); notifyDeleteAuditLogs($auditLogRetention); diff --git a/app/tasks/ssl.php b/app/tasks/ssl.php index 6010c3fd1f..18f7ee8324 100644 --- a/app/tasks/ssl.php +++ b/app/tasks/ssl.php @@ -2,24 +2,35 @@ global $cli; +use Appwrite\Event\Event; use Utopia\App; use Utopia\CLI\Console; +use Utopia\Validator\Hostname; $cli ->task('ssl') ->desc('Validate server certificates') - ->action(function () { - $domain = App::getEnv('_APP_DOMAIN', ''); + ->param('domain', App::getEnv('_APP_DOMAIN', ''), new Hostname(), 'Domain to generate certificate for. If empty, main domain will be used.', true) + ->action(function ($domain) { + // HTTTP ping to check if domain is Appwrite server + $ch = \curl_init(); + \curl_setopt($ch, CURLOPT_URL, 'http://appwrite/manifest.json'); + \curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + \curl_setopt($ch, CURLOPT_TIMEOUT, 5); + \curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); + \curl_exec($ch); + $statusCode = \curl_getinfo($ch, CURLINFO_HTTP_CODE); + $error = \curl_error($ch); + \curl_close($ch); - // TODO: Instead of waiting, let's ping Traefik. If responds, we can schedule instantly - // TODO: Add support for argument (domain) + if($statusCode < 100) { + return Console::error('Appwrite connection refused with message: ' . $error); + } - Console::log('Issue a TLS certificate for master domain ('.$domain.') in 2 seconds. - Make sure your domain points to your server or restart to try again.'); + Console::success('Schedule a job to issue a TLS certificate for domain:' . $domain); - // Const for types not available here - ResqueScheduler::enqueueAt(\time() + 2, 'v1-certificates', 'CertificatesV1', [ + // Scheduje a job + Resque::enqueue(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, [ 'domain' => $domain, - 'skipRenewCheck' => true // TODO: Discuss this behabiour. true? false? parameter? How do we document it? ]); }); \ No newline at end of file diff --git a/app/workers/certificates.php b/app/workers/certificates.php index fea025d163..aca8519cc2 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -34,21 +34,38 @@ class CertificatesV1 extends Worker $certificate = new Document(); + + /** + * 1. Read arguments and validate domain + * 2. Get main domain + * 3. Validate CNAME DNS if parameter is not main domain (meaning it's custom domain) + * 4. Validate renew date with certificate file, unless requested to skip by parameter + * 5. Validate security email. Cannot be empty, required by LetsEncrypt + * 6. Issue a certificate using certbot CLI + * 7. Update 'log' attribute on certificate document with Certbot message + * 8. Create storage folder for certificate, if not ready already + * 9. Move certificates from Certbot location to our Storage + * 10. Create/Update our Storage with new Traefik config with new certificate paths + * 11. Read certificate file and update 'renewDate' on certificate document + * 12. Update 'issueDate' and 'attempts' on certificate + * + * If at any point unexpected error occurs, program stops without applying changes to document, and error is thrown into worker + * + * If code stops with expected error: + * 1. 'log' attribute on document is updated with error message + * 2. 'attempts' amount is increased + * 3. Console log is shown + * 4. Email is sent to security email + * + * Unless unexpected error occurs, at the end, we: + * 1. Update 'updated' attribute on document + * 2. Save document to database + * 3. Update all domains documents with current certificate ID + * + * Note: Renewals are checked and scheduled from maintenence worker + */ + try { - /** - * TODO: Update - * 1. Get new domain document - DONE - * 1.1. Validate domain is valid, public suffix is known and CNAME records are verified - DONE - * 2. Check if a certificate already exists - DONE - * 3. Check if certificate is about to expire, if not - skip it - * 3.1. Create / renew certificate - * 3.2. Update loadblancer - * 3.3. Update database (domains, change date, expiry) - * 3.4. Set retry on failure - * 3.5. Schedule to renew certificate in 60 days - */ - - // Get attributes $domain = $this->args['domain']; // String of domain (hostname) $domain = new Domain((!empty($domain)) ? $domain : ''); @@ -108,7 +125,7 @@ class CertificatesV1 extends Worker try { $certData = openssl_x509_parse(file_get_contents($certPath)); - $validTo = $certData['validTo_time_t']; + $validTo = $certData['validTo_time_t'] ?? 0; if (empty($validTo)) { throw new Exception('Invalid expiry date.'); @@ -146,7 +163,7 @@ class CertificatesV1 extends Worker // Unexpected error, usually 5XX, API limits, ... if ($exit !== 0) { - throw new ExceptionCertificate('Failed to issue a certificate with message: ' . $stderr, true); + throw new ExceptionCertificate('Failed to issue a certificate with message: ' . $stderr); } // Command succeeded, store all data into document @@ -161,25 +178,25 @@ class CertificatesV1 extends Worker $path = APP_STORAGE_CERTIFICATES . '/' . $domain->get(); if (!\is_readable($path)) { if (!\mkdir($path, 0755, true)) { - throw new ExceptionCertificate('Failed to create path for certificate.', true); + throw new ExceptionCertificate('Failed to create path for certificate.'); } } // Move generated files from certbot into our storage if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/cert.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/cert.pem')) { - throw new ExceptionCertificate('Failed to rename certificate cert.pem: '.\json_encode($stdout), true); + throw new ExceptionCertificate('Failed to rename certificate cert.pem: '.\json_encode($stdout)); } if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/chain.pem')) { - throw new ExceptionCertificate('Failed to rename certificate chain.pem: ' . \json_encode($stdout), true); + throw new ExceptionCertificate('Failed to rename certificate chain.pem: ' . \json_encode($stdout)); } if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/fullchain.pem')) { - throw new ExceptionCertificate('Failed to rename certificate fullchain.pem: ' . \json_encode($stdout), true); + throw new ExceptionCertificate('Failed to rename certificate fullchain.pem: ' . \json_encode($stdout)); } if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/privkey.pem')) { - throw new ExceptionCertificate('Failed to rename certificate privkey.pem: ' . \json_encode($stdout), true); + throw new ExceptionCertificate('Failed to rename certificate privkey.pem: ' . \json_encode($stdout)); } // This multi-line syntax helps IDE @@ -191,14 +208,14 @@ class CertificatesV1 extends Worker // Save configuration into Traefik using our new cert files if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain->get() . '.yml', $config)) { - throw new ExceptionCertificate('Failed to save Traefik configuration.', true); + throw new ExceptionCertificate('Failed to save Traefik configuration.'); } // Read new renew date from cert file // TODO: This might not be required, we could calculate it. But this feels safer $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/cert.pem'; $certData = openssl_x509_parse(file_get_contents($certPath)); - $validTo = $certData['validTo_time_t']; + $validTo = $certData['validTo_time_t'] ?? 0; $expiryInAdvance = (60*60*24*30); $certificate->setAttribute('renewDate', $validTo - $expiryInAdvance); @@ -218,10 +235,8 @@ class CertificatesV1 extends Worker $attempt = $certificate->getAttribute('attempts', 0); $attempt++; - // Save increased attempts count if requested by exception - if($e->getIncrementAttempts()) { - $certificate->setAttribute('attempts', $attempt); - } + // Save increased attempts count + $certificate->setAttribute('attempts', $attempt); Console::warning('Cannot renew domain (' . $domain->get() . ') on attempt no. ' . $attempt . ' certificate: ' . $e->getMessage()); } finally { diff --git a/src/Appwrite/Exception/Certificate.php b/src/Appwrite/Exception/Certificate.php index 9a146807d7..0b044015d4 100644 --- a/src/Appwrite/Exception/Certificate.php +++ b/src/Appwrite/Exception/Certificate.php @@ -4,35 +4,4 @@ namespace Appwrite\Exception; class Certificate extends \Exception { - private $incrementAttempts = false; - - // Only set incrementAttempts to TRUE if exception occured AFTER certbot command execution - public function __construct(string $message, bool $incrementAttempts = false) - { - $this->incrementAttempts = $incrementAttempts; - - parent::__construct($message); - } - - /** - * Get value if attempts should be incremented. - * - * @return boolean - */ - public function getIncrementAttempts(): bool - { - return $this->incrementAttempts; - } - - /** - * Set if attempts should be incremented - * - * @param boolean $value - * - * @return void - */ - public function setIncrementAttempts(bool $value): void - { - $this->incrementAttempts = $value; - } } \ No newline at end of file From 7159070067be8e49216ff088f3e8c8f014716d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 11 Apr 2022 08:34:11 +0000 Subject: [PATCH 17/87] PR review changes --- app/config/locale/translations/en.json | 6 ++++++ app/init.php | 1 + app/workers/certificates.php | 28 +++++++++++++++++++++++++- app/workers/mails.php | 12 +++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/app/config/locale/translations/en.json b/app/config/locale/translations/en.json index c00740b130..e533d82795 100644 --- a/app/config/locale/translations/en.json +++ b/app/config/locale/translations/en.json @@ -27,6 +27,12 @@ "emails.invitation.footer": "If you are not interested, you can ignore this message.", "emails.invitation.thanks": "Thanks", "emails.invitation.signature": "{{project}} team", + "emails.certificate.subject": "Certificate failure for %s", + "emails.certificate.hello": "Hello", + "emails.certificate.body": "Certificate for your domain '{{domain}}' could not be renewed. This is attempt no. {{attempt}}, and the failure was caused by: {{error}}", + "emails.certificate.footer": "Certificate will still be valid for 30 days since first failure. We highly recommend investigating the case, otherwise your domain will end up without a secure certificate.", + "emails.certificate.thanks": "Thanks", + "emails.certificate.signature": "{{project}} team", "locale.country.unknown": "Unknown", "countries.af": "Afghanistan", "countries.ao": "Angola", diff --git a/app/init.php b/app/init.php index fd509297f6..1c8dc5aadc 100644 --- a/app/init.php +++ b/app/init.php @@ -127,6 +127,7 @@ const MAIL_TYPE_VERIFICATION = 'verification'; const MAIL_TYPE_MAGIC_SESSION = 'magicSession'; const MAIL_TYPE_RECOVERY = 'recovery'; const MAIL_TYPE_INVITATION = 'invitation'; +const MAIL_TYPE_CERTIFICATE = 'certificat'; // Auth Types const APP_AUTH_TYPE_SESSION = 'Session'; const APP_AUTH_TYPE_JWT = 'JWT'; diff --git a/app/workers/certificates.php b/app/workers/certificates.php index aca8519cc2..825bb1689c 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -239,6 +239,21 @@ class CertificatesV1 extends Worker $certificate->setAttribute('attempts', $attempt); Console::warning('Cannot renew domain (' . $domain->get() . ') on attempt no. ' . $attempt . ' certificate: ' . $e->getMessage()); + + // Send email to security email + Resque::enqueue(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME, [ + 'from' => 'console', + 'project' => 'console', + 'name' => 'Appwrite Administrator', + 'recipient' => App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'), + 'url' => 'https://' . $domain->get(), + 'locale' => App::getEnv('_APP_LOCALE', 'en'), + 'type' => MAIL_TYPE_CERTIFICATE, + + 'domain' => $domain->get(), + 'error' => $e->getMessage(), + 'attempt' => $attempt + ]); } finally { // All actions result in new updatedAt date $certificate->setAttribute('updated', \time()); @@ -257,7 +272,18 @@ class CertificatesV1 extends Worker // Update domains with new certificate ID $certificateId = $certificate->getId(); - // TODO: Add logic for updating domains + + $domains = $dbForConsole->find('domains', [ + new Query('domain', Query::TYPE_EQUAL, [$domain->get()]) + ], 1000); + + foreach ($domains as $domainDocument) { + $domainDocument->setAttribute('updated', \time()); + $domainDocument->setAttribute('certificateId', $certificateId); + + $dbForConsole->updateDocument('domains', $domainDocument->getId(), $domainDocument); + $dbForConsole->deleteCachedDocument('projects', $domainDocument->getAttribute('projectId')); + } Authorization::reset(); } diff --git a/app/workers/mails.php b/app/workers/mails.php index 25ddb438c0..6905593a8c 100644 --- a/app/workers/mails.php +++ b/app/workers/mails.php @@ -46,6 +46,16 @@ class MailsV1 extends Worker $body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl'); $subject = ''; switch ($type) { + case MAIL_TYPE_CERTIFICATE: + $domain = $this->args['domain']; + $error = $this->args['error']; + $attempt = $this->args['attempt']; + + $subject = \sprintf($locale->getText("$prefix.subject"), $domain); + $body->setParam('{{domain}}', $domain); + $body->setParam('{{error}}', $error); + $body->setParam('{{attempt}}', $attempt); + break; case MAIL_TYPE_INVITATION: $subject = \sprintf($locale->getText("$prefix.subject"), $this->args['team'], $project); $body->setParam('{{owner}}', $this->args['owner']); @@ -126,6 +136,8 @@ class MailsV1 extends Worker switch ($type) { case MAIL_TYPE_RECOVERY: return 'emails.recovery'; + case MAIL_TYPE_CERTIFICATE: + return 'emails.certificate'; case MAIL_TYPE_INVITATION: return 'emails.invitation'; case MAIL_TYPE_VERIFICATION: From 99537545ced33d88295d5c6ce78e3715f6628709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 12 Apr 2022 11:46:43 +0000 Subject: [PATCH 18/87] Update lockfile after merge --- composer.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/composer.lock b/composer.lock index be60c6c07f..9bafae75a0 100644 --- a/composer.lock +++ b/composer.lock @@ -300,16 +300,16 @@ }, { "name": "colinmollenhour/credis", - "version": "v1.12.2", + "version": "v1.13.0", "source": { "type": "git", "url": "https://github.com/colinmollenhour/credis.git", - "reference": "77e6ede2e01c4cfaade114fe1e07d2f9756949f1" + "reference": "afec8e58ec93d2291c127fa19709a048f28641e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/77e6ede2e01c4cfaade114fe1e07d2f9756949f1", - "reference": "77e6ede2e01c4cfaade114fe1e07d2f9756949f1", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/afec8e58ec93d2291c127fa19709a048f28641e5", + "reference": "afec8e58ec93d2291c127fa19709a048f28641e5", "shasum": "" }, "require": { @@ -341,9 +341,9 @@ "homepage": "https://github.com/colinmollenhour/credis", "support": { "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.12.2" + "source": "https://github.com/colinmollenhour/credis/tree/v1.13.0" }, - "time": "2022-03-08T18:12:43+00:00" + "time": "2022-04-07T14:57:22+00:00" }, { "name": "composer/package-versions-deprecated", @@ -2250,16 +2250,16 @@ }, { "name": "utopia-php/framework", - "version": "0.19.7", + "version": "0.19.8", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "f17afe77a21873b9be18ebc05283813468b4283a" + "reference": "8c3b3e330546fd6cd65bd1f8d8d08882ff3abb7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/f17afe77a21873b9be18ebc05283813468b4283a", - "reference": "f17afe77a21873b9be18ebc05283813468b4283a", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/8c3b3e330546fd6cd65bd1f8d8d08882ff3abb7d", + "reference": "8c3b3e330546fd6cd65bd1f8d8d08882ff3abb7d", "shasum": "" }, "require": { @@ -2293,9 +2293,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.19.7" + "source": "https://github.com/utopia-php/framework/tree/0.19.8" }, - "time": "2022-02-18T00:04:49+00:00" + "time": "2022-04-12T00:28:15+00:00" }, { "name": "utopia-php/image", @@ -6327,16 +6327,16 @@ }, { "name": "twig/twig", - "version": "v3.3.9", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "6ff9b0e440fa66f97f207e181c41340ddfa5683d" + "reference": "8442df056c51b706793adf80a9fd363406dd3674" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/6ff9b0e440fa66f97f207e181c41340ddfa5683d", - "reference": "6ff9b0e440fa66f97f207e181c41340ddfa5683d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/8442df056c51b706793adf80a9fd363406dd3674", + "reference": "8442df056c51b706793adf80a9fd363406dd3674", "shasum": "" }, "require": { @@ -6387,7 +6387,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.3.9" + "source": "https://github.com/twigphp/Twig/tree/v3.3.10" }, "funding": [ { @@ -6399,7 +6399,7 @@ "type": "tidelift" } ], - "time": "2022-03-25T09:37:52+00:00" + "time": "2022-04-06T06:47:41+00:00" }, { "name": "vimeo/psalm", From e49d6fd7661463d23f80c5bca8670a3c7c185527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 13 Apr 2022 12:26:07 +0000 Subject: [PATCH 19/87] Review changes --- app/init.php | 2 +- app/realtime.php | 10 +++++----- app/tasks/maintenance.php | 7 ++++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/init.php b/app/init.php index 1c8dc5aadc..221742fc2f 100644 --- a/app/init.php +++ b/app/init.php @@ -127,7 +127,7 @@ const MAIL_TYPE_VERIFICATION = 'verification'; const MAIL_TYPE_MAGIC_SESSION = 'magicSession'; const MAIL_TYPE_RECOVERY = 'recovery'; const MAIL_TYPE_INVITATION = 'invitation'; -const MAIL_TYPE_CERTIFICATE = 'certificat'; +const MAIL_TYPE_CERTIFICATE = 'certificate'; // Auth Types const APP_AUTH_TYPE_SESSION = 'Session'; const APP_AUTH_TYPE_JWT = 'JWT'; diff --git a/app/realtime.php b/app/realtime.php index 8d36069edb..275305e479 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -93,7 +93,7 @@ $logError = function(Throwable $error, string $action) use ($register) { $server->error($logError); -function getDatabase(Registry &$register, string $namespace) +function getConsoleDB(Registry &$register, string $namespace) { $attempts = 0; @@ -141,7 +141,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume */ go(function () use ($register, $containerId, &$statsDocument, $logError) { try { - [$database, $returnDatabase] = getDatabase($register, '_console'); + [$database, $returnDatabase] = getConsoleDB($register, '_console'); $document = new Document([ '$id' => $database->getId(), '$collection' => 'realtime', @@ -194,7 +194,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume } try { - [$database, $returnDatabase] = getDatabase($register, '_console'); + [$database, $returnDatabase] = getConsoleDB($register, '_console'); $statsDocument ->setAttribute('timestamp', time()) @@ -221,7 +221,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, */ if ($realtime->hasSubscriber('console', 'role:member', 'project')) { - [$database, $returnDatabase] = getDatabase($register, '_console'); + [$database, $returnDatabase] = getConsoleDB($register, '_console'); $payload = []; @@ -325,7 +325,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, return; } - [$database, $returnDatabase] = getDatabase($register, "_{$projectId}"); + [$database, $returnDatabase] = getConsoleDB($register, "_{$projectId}"); $user = $database->getDocument('users', $userId); diff --git a/app/tasks/maintenance.php b/app/tasks/maintenance.php index e946daeb26..db2d2e808a 100644 --- a/app/tasks/maintenance.php +++ b/app/tasks/maintenance.php @@ -13,7 +13,7 @@ use Utopia\Registry\Registry; use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Database\Query; -function getDatabase(Registry &$register) +function getConsoleDB(Registry &$register) { $attempts = 0; @@ -103,7 +103,7 @@ $cli /** @var Utopia\Database\Database $dbForConsole */ $certificates = $dbForConsole->find('certificates', [ - new Query('attempts', Query::TYPE_LESSER, [5]), // Maximum 5 attempts + new Query('attempts', Query::TYPE_LESSEREQUAL, [5]), // Maximum 5 attempts new Query('renewDate', Query::TYPE_LESSEREQUAL, [\time()]) // includes 60 days cooldown (we have 30 days to renew) ], 200); // Limit 200 comes from LetsEncrypt (300 orders per 3 hours, keeping some for new domains) @@ -131,7 +131,8 @@ $cli Console::loop(function() use ($register, $interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) { go(function () use ($register, $interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) { try { - [$database, $returnDatabase] = getDatabase($register, '_console'); + [$database, $returnDatabase] = getConsoleDB + ($register, '_console'); $time = date('d-m-Y H:i:s', time()); Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds"); From 6141080449d0c65321ed0c99887041edb8d6d670 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Wed, 20 Apr 2022 10:10:39 +0200 Subject: [PATCH 20/87] Fix code in exception --- app/config/errors.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/errors.php b/app/config/errors.php index 6ebc311798..9c1da2c540 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -81,7 +81,7 @@ return [ Exception::GENERAL_METHOD_UNSUPPORTED => [ 'name' => Exception::GENERAL_METHOD_UNSUPPORTED, 'description' => 'The HTTP method is unsupported by the current protocol or resource.', - 'code' => 500, + 'code' => 405, ], /** User Errors */ From 5e54339673931e0f6fe50bae9f22218689e4ecde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 20 Apr 2022 08:53:29 +0000 Subject: [PATCH 21/87] Update lockfile after merge --- composer.lock | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/composer.lock b/composer.lock index e908e573c5..3084527702 100644 --- a/composer.lock +++ b/composer.lock @@ -2250,16 +2250,16 @@ }, { "name": "utopia-php/framework", - "version": "0.19.8", + "version": "0.19.9", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "8c3b3e330546fd6cd65bd1f8d8d08882ff3abb7d" + "reference": "4af9fc866edce1b8cff94731fb26c27599118e87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/8c3b3e330546fd6cd65bd1f8d8d08882ff3abb7d", - "reference": "8c3b3e330546fd6cd65bd1f8d8d08882ff3abb7d", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/4af9fc866edce1b8cff94731fb26c27599118e87", + "reference": "4af9fc866edce1b8cff94731fb26c27599118e87", "shasum": "" }, "require": { @@ -2293,9 +2293,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.19.8" + "source": "https://github.com/utopia-php/framework/tree/0.19.9" }, - "time": "2022-04-12T00:28:15+00:00" + "time": "2022-04-14T15:39:47+00:00" }, { "name": "utopia-php/image", @@ -3551,16 +3551,16 @@ }, { "name": "matthiasmullie/minify", - "version": "1.3.66", + "version": "1.3.67", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6" + "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/45fd3b0f1dfa2c965857c6d4a470bea52adc31a6", - "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", + "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", "shasum": "" }, "require": { @@ -3609,23 +3609,15 @@ ], "support": { "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.66" + "source": "https://github.com/matthiasmullie/minify/tree/1.3.67" }, "funding": [ { - "url": "https://github.com/[user1", - "type": "github" - }, - { - "url": "https://github.com/matthiasmullie] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g.", - "type": "github" - }, - { - "url": "https://github.com/user2", + "url": "https://github.com/matthiasmullie", "type": "github" } ], - "time": "2021-01-06T15:18:10+00:00" + "time": "2022-03-24T08:54:59+00:00" }, { "name": "matthiasmullie/path-converter", From 8fcb2b40589db538738b076097f4c7ed5720514c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 20 Apr 2022 09:31:17 +0000 Subject: [PATCH 22/87] Hostname-related features&fixes --- app/controllers/api/projects.php | 14 +++++++++- app/controllers/general.php | 10 +++++-- app/views/console/home/index.phtml | 29 ++++++++++++------- composer.lock | 34 +++++++++-------------- src/Appwrite/Network/Validator/Host.php | 10 +++---- src/Appwrite/Network/Validator/Origin.php | 8 ++---- 6 files changed, 61 insertions(+), 44 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index f7100a88a2..cc289a8223 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -21,7 +21,7 @@ use Utopia\Domains\Domain; use Appwrite\Extend\Exception; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; -use Utopia\Validator\Integer; +use Utopia\Validator\Hostname; use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; @@ -1016,6 +1016,12 @@ App::post('/v1/projects/:projectId/platforms') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ + // Ensure hostname has proper structure (no port, protocol..) + $validator = new Hostname(); + if (!is_null($hostname) && !$validator->isValid($hostname)) { + throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID); + } + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1135,6 +1141,12 @@ App::put('/v1/projects/:projectId/platforms/:platformId') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ + // Ensure hostname has proper structure (no port, protocol..) + $validator = new Hostname(); + if (!is_null($hostname) && !$validator->isValid($hostname)) { + throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID); + } + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { diff --git a/app/controllers/general.php b/app/controllers/general.php index a65e07c81c..aa7af6a47d 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -19,6 +19,7 @@ use Utopia\CLI\Console; use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; +use Utopia\Validator\Hostname; use Appwrite\Utopia\Request\Filters\V12 as RequestV12; use Appwrite\Utopia\Request\Filters\V13 as RequestV13; use Utopia\Validator\Text; @@ -123,8 +124,13 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons $protocol = \parse_url($request->getOrigin($referrer), PHP_URL_SCHEME); $port = \parse_url($request->getOrigin($referrer), PHP_URL_PORT); - $refDomain = (!empty($protocol) ? $protocol : $request->getProtocol()).'://'.((\in_array($origin, $clients)) - ? $origin : 'localhost').(!empty($port) ? ':'.$port : ''); + $refDomainOrigin = 'localhost'; + $validator = new Hostname($clients); + if ($validator->isValid($origin)) { + $refDomainOrigin = $origin; + } + + $refDomain = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $refDomainOrigin . (!empty($port) ? ':' . $port : ''); $refDomain = (!$route->getLabel('origin', false)) // This route is publicly accessible ? $refDomain diff --git a/app/views/console/home/index.phtml b/app/views/console/home/index.phtml index 028e7f3b79..82be016b61 100644 --- a/app/views/console/home/index.phtml +++ b/app/views/console/home/index.phtml @@ -299,8 +299,9 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true); - - + + +
You can use * to allow wildcard hostnames or subdomains.
Next Steps
@@ -329,7 +330,8 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true); data-success="alert,trigger" data-success-param-alert-text="Updated platform successfully" data-success-param-trigger-events="projects.updatePlatform" - data-failure="alert" + data-failure="alert,trigger" + data-failure-param-trigger-events="projects.updatePlatform" data-failure-param-alert-text="Failed to update platform" data-failure-param-alert-classname="error"> @@ -340,7 +342,8 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true); - + +
You can use * to allow wildcard hostnames or subdomains.

@@ -714,7 +717,8 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true); data-success="alert,trigger" data-success-param-alert-text="Updated platform successfully" data-success-param-trigger-events="projects.updatePlatform" - data-failure="alert" + data-failure="alert,trigger" + data-failure-param-trigger-events="projects.updatePlatform" data-failure-param-alert-text="Failed to update platform" data-failure-param-alert-classname="error"> @@ -746,7 +750,8 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true); data-success="alert,trigger" data-success-param-alert-text="Updated platform successfully" data-success-param-trigger-events="projects.updatePlatform" - data-failure="alert" + data-failure="alert,trigger" + data-failure-param-trigger-events="projects.updatePlatform" data-failure-param-alert-text="Failed to update platform" data-failure-param-alert-classname="error"> @@ -777,7 +782,8 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true); data-success="alert,trigger" data-success-param-alert-text="Updated platform successfully" data-success-param-trigger-events="projects.updatePlatform" - data-failure="alert" + data-failure="alert,trigger" + data-failure-param-trigger-events="projects.updatePlatform" data-failure-param-alert-text="Failed to update platform" data-failure-param-alert-classname="error"> @@ -808,7 +814,8 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true); data-success="alert,trigger" data-success-param-alert-text="Updated platform successfully" data-success-param-trigger-events="projects.updatePlatform" - data-failure="alert" + data-failure="alert,trigger" + data-failure-param-trigger-events="projects.updatePlatform" data-failure-param-alert-text="Failed to update platform" data-failure-param-alert-classname="error"> @@ -841,7 +848,8 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true); data-success="alert,trigger" data-success-param-alert-text="Updated platform successfully" data-success-param-trigger-events="projects.updatePlatform" - data-failure="alert" + data-failure="alert,trigger" + data-failure-param-trigger-events="projects.updatePlatform" data-failure-param-alert-text="Failed to update platform" data-failure-param-alert-classname="error"> @@ -873,7 +881,8 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true); data-success="alert,trigger" data-success-param-alert-text="Updated platform successfully" data-success-param-trigger-events="projects.updatePlatform" - data-failure="alert" + data-failure="alert,trigger" + data-failure-param-trigger-events="projects.updatePlatform" data-failure-param-alert-text="Failed to update platform" data-failure-param-alert-classname="error"> diff --git a/composer.lock b/composer.lock index e908e573c5..ca66336799 100644 --- a/composer.lock +++ b/composer.lock @@ -2250,16 +2250,16 @@ }, { "name": "utopia-php/framework", - "version": "0.19.8", + "version": "0.19.20", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "8c3b3e330546fd6cd65bd1f8d8d08882ff3abb7d" + "reference": "65ced168db8f6e188ceeb0d101f57552c3d8b2af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/8c3b3e330546fd6cd65bd1f8d8d08882ff3abb7d", - "reference": "8c3b3e330546fd6cd65bd1f8d8d08882ff3abb7d", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/65ced168db8f6e188ceeb0d101f57552c3d8b2af", + "reference": "65ced168db8f6e188ceeb0d101f57552c3d8b2af", "shasum": "" }, "require": { @@ -2293,9 +2293,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.19.8" + "source": "https://github.com/utopia-php/framework/tree/0.19.20" }, - "time": "2022-04-12T00:28:15+00:00" + "time": "2022-04-14T15:42:37+00:00" }, { "name": "utopia-php/image", @@ -3551,16 +3551,16 @@ }, { "name": "matthiasmullie/minify", - "version": "1.3.66", + "version": "1.3.67", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6" + "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/45fd3b0f1dfa2c965857c6d4a470bea52adc31a6", - "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", + "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", "shasum": "" }, "require": { @@ -3609,23 +3609,15 @@ ], "support": { "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.66" + "source": "https://github.com/matthiasmullie/minify/tree/1.3.67" }, "funding": [ { - "url": "https://github.com/[user1", - "type": "github" - }, - { - "url": "https://github.com/matthiasmullie] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g.", - "type": "github" - }, - { - "url": "https://github.com/user2", + "url": "https://github.com/matthiasmullie", "type": "github" } ], - "time": "2021-01-06T15:18:10+00:00" + "time": "2022-03-24T08:54:59+00:00" }, { "name": "matthiasmullie/path-converter", diff --git a/src/Appwrite/Network/Validator/Host.php b/src/Appwrite/Network/Validator/Host.php index 703907c3d3..4e5aa0b66a 100644 --- a/src/Appwrite/Network/Validator/Host.php +++ b/src/Appwrite/Network/Validator/Host.php @@ -1,6 +1,7 @@ isValid($value)) { return false; } - if (\in_array(\parse_url($value, PHP_URL_HOST), $this->whitelist)) { - return true; - } - - return false; + $hostname = \parse_url($value, PHP_URL_HOST); + $hostnameValudator = new Hostname($this->whitelist); + return $hostnameValudator->isValid($hostname); } /** diff --git a/src/Appwrite/Network/Validator/Origin.php b/src/Appwrite/Network/Validator/Origin.php index 8831707cef..c2ef6f1128 100644 --- a/src/Appwrite/Network/Validator/Origin.php +++ b/src/Appwrite/Network/Validator/Origin.php @@ -1,6 +1,7 @@ clients)) { - return true; - } - - return false; + $validator = new Hostname($this->clients); + return $validator->isValid($host); } /** From 41b17822a5b8e990af570847ea7d37eab998400d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 20 Apr 2022 12:39:53 +0000 Subject: [PATCH 23/87] PR review changes --- src/Appwrite/Network/Validator/Host.php | 4 ++-- src/Appwrite/Network/Validator/Origin.php | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Network/Validator/Host.php b/src/Appwrite/Network/Validator/Host.php index 4e5aa0b66a..b993903d10 100644 --- a/src/Appwrite/Network/Validator/Host.php +++ b/src/Appwrite/Network/Validator/Host.php @@ -54,8 +54,8 @@ class Host extends Validator } $hostname = \parse_url($value, PHP_URL_HOST); - $hostnameValudator = new Hostname($this->whitelist); - return $hostnameValudator->isValid($hostname); + $hostnameValidator = new Hostname($this->whitelist); + return $hostnameValidator->isValid($hostname); } /** diff --git a/src/Appwrite/Network/Validator/Origin.php b/src/Appwrite/Network/Validator/Origin.php index c2ef6f1128..1f9eb6462f 100644 --- a/src/Appwrite/Network/Validator/Origin.php +++ b/src/Appwrite/Network/Validator/Origin.php @@ -124,6 +124,7 @@ class Origin extends Validator } $validator = new Hostname($this->clients); + return $validator->isValid($host); } From 806c50bcdb8b0b8baf75f1078109d15a33d3d631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 20 Apr 2022 13:56:39 +0000 Subject: [PATCH 24/87] Fix tests --- app/controllers/api/projects.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index cc289a8223..fca4bfdfd0 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1017,9 +1017,11 @@ App::post('/v1/projects/:projectId/platforms') /** @var Utopia\Database\Database $dbForConsole */ // Ensure hostname has proper structure (no port, protocol..) - $validator = new Hostname(); - if (!is_null($hostname) && !$validator->isValid($hostname)) { - throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID); + if(!empty($hostname)) { + $validator = new Hostname(); + if (!is_null($hostname) && !$validator->isValid($hostname)) { + throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID); + } } $project = $dbForConsole->getDocument('projects', $projectId); @@ -1142,9 +1144,11 @@ App::put('/v1/projects/:projectId/platforms/:platformId') /** @var Utopia\Database\Database $dbForConsole */ // Ensure hostname has proper structure (no port, protocol..) - $validator = new Hostname(); - if (!is_null($hostname) && !$validator->isValid($hostname)) { - throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID); + if(!empty($hostname)) { + $validator = new Hostname(); + if (!is_null($hostname) && !$validator->isValid($hostname)) { + throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID); + } } $project = $dbForConsole->getDocument('projects', $projectId); From 023e9f0f73e6f8f551a2eedeb282523bbc6424f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 25 Apr 2022 09:03:03 +0000 Subject: [PATCH 25/87] Fix spacings --- src/Appwrite/Network/Validator/Host.php | 2 +- src/Appwrite/Network/Validator/Origin.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Network/Validator/Host.php b/src/Appwrite/Network/Validator/Host.php index b993903d10..c81a931f37 100644 --- a/src/Appwrite/Network/Validator/Host.php +++ b/src/Appwrite/Network/Validator/Host.php @@ -1,8 +1,8 @@ Date: Mon, 25 Apr 2022 09:12:43 +0000 Subject: [PATCH 26/87] PR review changes --- app/config/errors.php | 8 ++++---- app/controllers/general.php | 2 +- src/Appwrite/Extend/Exception.php | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 9c1da2c540..8dadcf079c 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -78,10 +78,10 @@ return [ 'description' => 'An internal server error occurred.', 'code' => 500, ], - Exception::GENERAL_METHOD_UNSUPPORTED => [ - 'name' => Exception::GENERAL_METHOD_UNSUPPORTED, - 'description' => 'The HTTP method is unsupported by the current protocol or resource.', - 'code' => 405, + Exception::GENERAL_PROTOCOL_UNSUPPORTED => [ + 'name' => Exception::GENERAL_PROTOCOL_UNSUPPORTED, + 'description' => 'The request cannot be fulfilled with the current protocol. Please check the value of the _APP_OPTIONS_FORCE_HTTPS environment variable.', + 'code' => 500, ], /** User Errors */ diff --git a/app/controllers/general.php b/app/controllers/general.php index 443d32140b..4cb2d9232e 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -174,7 +174,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS if ($request->getProtocol() !== 'https') { if($request->getMethod() !== Request::METHOD_GET) { - throw new Exception('Method unsupported over HTTP.', 405, Exception::GENERAL_METHOD_UNSUPPORTED); + throw new Exception('Method unsupported over HTTP.', 500, Exception::GENERAL_PROTOCOL_UNSUPPORTED); } return $response->redirect('https://'.$request->getHostname().$request->getURI()); diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 04b44b57b2..ee5d6ab729 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -46,7 +46,7 @@ class Exception extends \Exception const GENERAL_ROUTE_NOT_FOUND = 'general_route_not_found'; const GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found'; const GENERAL_SERVER_ERROR = 'general_server_error'; - const GENERAL_METHOD_UNSUPPORTED = 'general_method_unsupported'; + const GENERAL_PROTOCOL_UNSUPPORTED = 'general_protocol_unsupported'; /** Users */ const USER_COUNT_EXCEEDED = 'user_count_exceeded'; From 1eb821c8236c69cd3609e3a9718304d9dc980617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 25 Apr 2022 11:43:30 +0000 Subject: [PATCH 27/87] Default value & perms bug fix --- app/config/collections.php | 4 +- app/controllers/api/account.php | 2 - app/init.php | 11 ++- composer.lock | 120 +++++++++++++++----------------- 4 files changed, 63 insertions(+), 74 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index 1da342767a..b0d3a57b78 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -1054,8 +1054,8 @@ $collections = [ 'size' => 16384, 'signed' => true, 'required' => false, - 'default' => [], - 'array' => true, + 'default' => null, + 'array' => false, 'filters' => ['subQuerySessions'], ], [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 7b190b8a1d..f043609daf 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -28,8 +28,6 @@ use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; -use function PHPUnit\Framework\isEmpty; - $oauthDefaultSuccess = '/v1/auth/oauth2/success'; $oauthDefaultFailure = '/v1/auth/oauth2/failure'; diff --git a/app/init.php b/app/init.php index d14c77d755..0be713fabc 100644 --- a/app/init.php +++ b/app/init.php @@ -306,10 +306,11 @@ Database::addFilter('subQuerySessions', return null; }, function($value, Document $document, Database $database) { - return $database - ->find('sessions', [ - new Query('userId', Query::TYPE_EQUAL, [$document->getId()]) - ], $database->getIndexLimit(), 0, []); + $sessions = Authorization::skip(fn () => $database->find('sessions', [ + new Query('userId', Query::TYPE_EQUAL, [$document->getId()]) + ], $database->getIndexLimit(), 0, [])); + + return $sessions; } ); @@ -705,8 +706,6 @@ App::setResource('user', function($mode, $project, $console, $request, $response $user = $dbForConsole->getDocument('users', Auth::$unique); } - \var_dump($user); - if ($user->isEmpty() // Check a document has been found in the DB || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)) { // Validate user has valid login token $user = new Document(['$id' => '', '$collection' => 'users']); diff --git a/composer.lock b/composer.lock index 03d8f468d2..67ad704a81 100644 --- a/composer.lock +++ b/composer.lock @@ -300,16 +300,16 @@ }, { "name": "colinmollenhour/credis", - "version": "v1.12.2", + "version": "v1.13.0", "source": { "type": "git", "url": "https://github.com/colinmollenhour/credis.git", - "reference": "77e6ede2e01c4cfaade114fe1e07d2f9756949f1" + "reference": "afec8e58ec93d2291c127fa19709a048f28641e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/77e6ede2e01c4cfaade114fe1e07d2f9756949f1", - "reference": "77e6ede2e01c4cfaade114fe1e07d2f9756949f1", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/afec8e58ec93d2291c127fa19709a048f28641e5", + "reference": "afec8e58ec93d2291c127fa19709a048f28641e5", "shasum": "" }, "require": { @@ -341,9 +341,9 @@ "homepage": "https://github.com/colinmollenhour/credis", "support": { "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.12.2" + "source": "https://github.com/colinmollenhour/credis/tree/v1.13.0" }, - "time": "2022-03-08T18:12:43+00:00" + "time": "2022-04-07T14:57:22+00:00" }, { "name": "composer/package-versions-deprecated", @@ -1583,16 +1583,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced" + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/c726b64c1ccfe2896cb7df2e1331c357ad1c8ced", - "reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", "shasum": "" }, "require": { @@ -1630,7 +1630,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1" }, "funding": [ { @@ -1646,7 +1646,7 @@ "type": "tidelift" } ], - "time": "2021-11-01T23:48:49+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2250,16 +2250,16 @@ }, { "name": "utopia-php/framework", - "version": "0.19.7", + "version": "0.19.20", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "f17afe77a21873b9be18ebc05283813468b4283a" + "reference": "65ced168db8f6e188ceeb0d101f57552c3d8b2af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/f17afe77a21873b9be18ebc05283813468b4283a", - "reference": "f17afe77a21873b9be18ebc05283813468b4283a", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/65ced168db8f6e188ceeb0d101f57552c3d8b2af", + "reference": "65ced168db8f6e188ceeb0d101f57552c3d8b2af", "shasum": "" }, "require": { @@ -2293,9 +2293,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.19.7" + "source": "https://github.com/utopia-php/framework/tree/0.19.20" }, - "time": "2022-02-18T00:04:49+00:00" + "time": "2022-04-14T15:42:37+00:00" }, { "name": "utopia-php/image", @@ -3195,16 +3195,16 @@ }, { "name": "composer/semver", - "version": "3.3.1", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "5d8e574bb0e69188786b8ef77d43341222a41a71" + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/5d8e574bb0e69188786b8ef77d43341222a41a71", - "reference": "5d8e574bb0e69188786b8ef77d43341222a41a71", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", "shasum": "" }, "require": { @@ -3256,7 +3256,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.1" + "source": "https://github.com/composer/semver/tree/3.3.2" }, "funding": [ { @@ -3272,7 +3272,7 @@ "type": "tidelift" } ], - "time": "2022-03-16T11:22:07+00:00" + "time": "2022-04-01T19:23:25+00:00" }, { "name": "composer/xdebug-handler", @@ -3550,16 +3550,16 @@ }, { "name": "matthiasmullie/minify", - "version": "1.3.66", + "version": "1.3.67", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6" + "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/45fd3b0f1dfa2c965857c6d4a470bea52adc31a6", - "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", + "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", "shasum": "" }, "require": { @@ -3608,23 +3608,15 @@ ], "support": { "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.66" + "source": "https://github.com/matthiasmullie/minify/tree/1.3.67" }, "funding": [ { - "url": "https://github.com/[user1", - "type": "github" - }, - { - "url": "https://github.com/matthiasmullie] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g.", - "type": "github" - }, - { - "url": "https://github.com/user2", + "url": "https://github.com/matthiasmullie", "type": "github" } ], - "time": "2021-01-06T15:18:10+00:00" + "time": "2022-03-24T08:54:59+00:00" }, { "name": "matthiasmullie/path-converter", @@ -5076,16 +5068,16 @@ }, { "name": "sebastian/environment", - "version": "5.1.3", + "version": "5.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac" + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", "shasum": "" }, "require": { @@ -5127,7 +5119,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" }, "funding": [ { @@ -5135,7 +5127,7 @@ "type": "github" } ], - "time": "2020-09-28T05:52:38+00:00" + "time": "2022-04-03T09:37:03+00:00" }, { "name": "sebastian/exporter", @@ -5718,16 +5710,16 @@ }, { "name": "symfony/console", - "version": "v6.0.5", + "version": "v6.0.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3bebf4108b9e07492a2a4057d207aa5a77d146b1" + "reference": "70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3bebf4108b9e07492a2a4057d207aa5a77d146b1", - "reference": "3bebf4108b9e07492a2a4057d207aa5a77d146b1", + "url": "https://api.github.com/repos/symfony/console/zipball/70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e", + "reference": "70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e", "shasum": "" }, "require": { @@ -5793,7 +5785,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.5" + "source": "https://github.com/symfony/console/tree/v6.0.7" }, "funding": [ { @@ -5809,7 +5801,7 @@ "type": "tidelift" } ], - "time": "2022-02-25T10:48:52+00:00" + "time": "2022-03-31T17:18:25+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -6061,16 +6053,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.0.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "36715ebf9fb9db73db0cb24263c79077c6fe8603" + "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/36715ebf9fb9db73db0cb24263c79077c6fe8603", - "reference": "36715ebf9fb9db73db0cb24263c79077c6fe8603", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e517458f278c2131ca9f262f8fbaf01410f2c65c", + "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c", "shasum": "" }, "require": { @@ -6123,7 +6115,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.0.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.0.1" }, "funding": [ { @@ -6139,7 +6131,7 @@ "type": "tidelift" } ], - "time": "2021-11-04T17:53:12+00:00" + "time": "2022-03-13T20:10:05+00:00" }, { "name": "symfony/string", @@ -6327,16 +6319,16 @@ }, { "name": "twig/twig", - "version": "v3.3.9", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "6ff9b0e440fa66f97f207e181c41340ddfa5683d" + "reference": "8442df056c51b706793adf80a9fd363406dd3674" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/6ff9b0e440fa66f97f207e181c41340ddfa5683d", - "reference": "6ff9b0e440fa66f97f207e181c41340ddfa5683d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/8442df056c51b706793adf80a9fd363406dd3674", + "reference": "8442df056c51b706793adf80a9fd363406dd3674", "shasum": "" }, "require": { @@ -6387,7 +6379,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.3.9" + "source": "https://github.com/twigphp/Twig/tree/v3.3.10" }, "funding": [ { @@ -6399,7 +6391,7 @@ "type": "tidelift" } ], - "time": "2022-03-25T09:37:52+00:00" + "time": "2022-04-06T06:47:41+00:00" }, { "name": "vimeo/psalm", @@ -6583,5 +6575,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } From 9cd79a01934e5e0eff0156744177f4ec70dea945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 25 Apr 2022 11:46:33 +0000 Subject: [PATCH 28/87] Fix composer after merge --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 3084527702..ca66336799 100644 --- a/composer.lock +++ b/composer.lock @@ -2250,16 +2250,16 @@ }, { "name": "utopia-php/framework", - "version": "0.19.9", + "version": "0.19.20", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "4af9fc866edce1b8cff94731fb26c27599118e87" + "reference": "65ced168db8f6e188ceeb0d101f57552c3d8b2af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/4af9fc866edce1b8cff94731fb26c27599118e87", - "reference": "4af9fc866edce1b8cff94731fb26c27599118e87", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/65ced168db8f6e188ceeb0d101f57552c3d8b2af", + "reference": "65ced168db8f6e188ceeb0d101f57552c3d8b2af", "shasum": "" }, "require": { @@ -2293,9 +2293,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.19.9" + "source": "https://github.com/utopia-php/framework/tree/0.19.20" }, - "time": "2022-04-14T15:39:47+00:00" + "time": "2022-04-14T15:42:37+00:00" }, { "name": "utopia-php/image", From de7e70e2758bcbd77d36f473a7fedcaeb17764e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 26 Apr 2022 08:52:59 +0000 Subject: [PATCH 29/87] Tests fix --- app/controllers/api/account.php | 14 ++++++++++---- app/controllers/api/teams.php | 5 ++--- app/workers/deletes.php | 6 ++---- tests/e2e/Services/Teams/TeamsBaseServer.php | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index dd3cf496ba..83e9af04b1 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1041,11 +1041,17 @@ App::post('/v1/account/jwt') /** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Database $dbForProject */ - $current = $dbForProject->findOne('sessions', [ - new Query('secret', Query::TYPE_EQUAL, [Auth::hash(Auth::$secret)]) - ]); - if (!$current) { + $sessions = $user->getAttribute('sessions', []); + $current = new Document(); + + foreach ($sessions as $session) { /** @var Utopia\Database\Document $session */ + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + $current = $session; + } + } + + if ($current->isEmpty()) { throw new Exception('No valid session found', 404, Exception::USER_SESSION_NOT_FOUND); } diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 22705d3bf8..ce208b6deb 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -338,7 +338,7 @@ App::post('/v1/teams/:teamId/memberships') 'reset' => false, 'name' => $name, 'prefs' => new \stdClass(), - 'sessions' => [], + 'sessions' => null, 'tokens' => [], 'memberships' => [], 'search' => implode(' ', [$userId, $email, $name]), @@ -728,11 +728,10 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->setAttribute('$write', ['user:'.$user->getId()]) ); - $user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND); + $dbForProject->deleteCachedDocument('users', $user->getId()); Authorization::setRole('user:'.$userId); - $user = $dbForProject->updateDocument('users', $user->getId(), $user); $membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership); $team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('total', $team->getAttribute('total', 0) + 1))); diff --git a/app/workers/deletes.php b/app/workers/deletes.php index b8a44ebb82..007b9c2933 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -207,15 +207,13 @@ class DeletesV1 extends Worker */ $userId = $document->getId(); - $user = $this->getProjectDB($projectId)->getDocument('users', $userId); // Delete all sessions of this user from the sessions table and update the sessions field of the user record $this->deleteByGroup('sessions', [ new Query('userId', Query::TYPE_EQUAL, [$userId]) ], $this->getProjectDB($projectId)); - - $user->setAttribute('sessions', []); - $updated = $this->getProjectDB($projectId)->updateDocument('users', $userId, $user); + + $this->getProjectDB($projectId)->deleteCachedDocument('users', $userId); // Delete Memberships and decrement team membership counts $this->deleteByGroup('memberships', [ diff --git a/tests/e2e/Services/Teams/TeamsBaseServer.php b/tests/e2e/Services/Teams/TeamsBaseServer.php index 41dcc6c84c..5db4b628f6 100644 --- a/tests/e2e/Services/Teams/TeamsBaseServer.php +++ b/tests/e2e/Services/Teams/TeamsBaseServer.php @@ -204,7 +204,7 @@ trait TeamsBaseServer $this->assertEquals(1, $response['body']['total']); $this->assertIsInt($response['body']['total']); $this->assertIsInt($response['body']['dateCreated']); - + /** Delete User */ $user = $this->client->call(Client::METHOD_DELETE, '/users/' . $userUid, array_merge([ From 66d68a786b2bd4bda849d5a4fafbfc26e512ac09 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Tue, 26 Apr 2022 12:22:01 +0300 Subject: [PATCH 30/87] Update src/Appwrite/Extend/Exception.php --- src/Appwrite/Extend/Exception.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index ee5d6ab729..cf508d1198 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -46,7 +46,7 @@ class Exception extends \Exception const GENERAL_ROUTE_NOT_FOUND = 'general_route_not_found'; const GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found'; const GENERAL_SERVER_ERROR = 'general_server_error'; - const GENERAL_PROTOCOL_UNSUPPORTED = 'general_protocol_unsupported'; + const GENERAL_PROTOCOL_UNSUPPORTED = 'general_protocol_unsupported'; /** Users */ const USER_COUNT_EXCEEDED = 'user_count_exceeded'; From 1e0fe177a8d859a5490674927af0a41742b9dfd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 26 Apr 2022 10:00:20 +0000 Subject: [PATCH 31/87] Post-merge fix --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 6d052aed1c..ea07e51fea 100644 --- a/composer.lock +++ b/composer.lock @@ -6576,5 +6576,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.3.0" } From e0426598fc96611a85486c232b0d3c752b324f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 26 Apr 2022 10:36:49 +0000 Subject: [PATCH 32/87] Fix unfinished todos --- app/controllers/api/account.php | 21 ++++++++------------- app/controllers/api/users.php | 6 +++--- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 83e9af04b1..f8b9eb516b 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -104,7 +104,7 @@ App::post('/v1/account') 'reset' => false, 'name' => $name, 'prefs' => new \stdClass(), - 'sessions' => [], + 'sessions' => null, 'tokens' => [], 'memberships' => [], 'search' => implode(' ', [$userId, $email, $name]), @@ -501,7 +501,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'reset' => false, 'name' => $name, 'prefs' => new \stdClass(), - 'sessions' => [], + 'sessions' => null, 'tokens' => [], 'memberships' => [], 'search' => implode(' ', [$userId, $email, $name]), @@ -674,7 +674,7 @@ App::post('/v1/account/sessions/magic-url') 'registration' => \time(), 'reset' => false, 'prefs' => new \stdClass(), - 'sessions' => [], + 'sessions' => null, 'tokens' => [], 'memberships' => [], 'search' => implode(' ', [$userId, $email]), @@ -946,7 +946,7 @@ App::post('/v1/account/sessions/anonymous') 'reset' => false, 'name' => null, 'prefs' => new \stdClass(), - 'sessions' => [], + 'sessions' => null, 'tokens' => [], 'memberships' => [], 'search' => $userId, @@ -1273,7 +1273,6 @@ App::get('/v1/account/sessions/:sessionId') /** @var Utopia\Database\Database $dbForProject */ /** @var Appwrite\Stats\Stats $usage */ - // TODO: Matej refactor $sessions = $user->getAttribute('sessions', []); $sessionId = ($sessionId === 'current') ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) @@ -1599,7 +1598,6 @@ App::delete('/v1/account/sessions/:sessionId') ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) : $sessionId; - // TODO: Matej refactor $sessions = $user->getAttribute('sessions', []); foreach ($sessions as $key => $session) {/** @var Document $session */ @@ -1633,8 +1631,8 @@ App::delete('/v1/account/sessions/:sessionId') ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ; } - - $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $sessions)); + + $dbForProject->deleteCachedDocument('users', $user->getId()); $events ->setParam('eventData', $response->output($session, Response::MODEL_SESSION)) @@ -1690,7 +1688,6 @@ App::patch('/v1/account/sessions/:sessionId') ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) : $sessionId; - // TODO: Matej refactor $sessions = $user->getAttribute('sessions', []); foreach ($sessions as $key => $session) {/** @var Document $session */ @@ -1729,8 +1726,7 @@ App::patch('/v1/account/sessions/:sessionId') $dbForProject->updateDocument('sessions', $sessionId, $session); - $user->setAttribute("sessions", $sessions); - $user = $dbForProject->updateDocument('users', $user->getId(), $user); + $dbForProject->deleteCachedDocument('users', $user->getId()); $audits ->setParam('userId', $user->getId()) @@ -1786,7 +1782,6 @@ App::delete('/v1/account/sessions') $protocol = $request->getProtocol(); $sessions = $user->getAttribute('sessions', []); - // TODO: Matej refactor foreach ($sessions as $session) {/** @var Document $session */ $dbForProject->deleteDocument('sessions', $session->getId()); @@ -1817,7 +1812,7 @@ App::delete('/v1/account/sessions') } } - $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', [])); + $dbForProject->deleteCachedDocument('users', $user->getId()); $numOfSessions = count($sessions); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 2732421c33..c9a6c0be8a 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -63,7 +63,7 @@ App::post('/v1/users') 'reset' => false, 'name' => $name, 'prefs' => new \stdClass(), - 'sessions' => [], + 'sessions' => null, 'tokens' => [], 'memberships' => [], 'search' => implode(' ', [$userId, $email, $name]), @@ -630,12 +630,12 @@ App::delete('/v1/users/:userId/sessions/:sessionId') $dbForProject->deleteDocument('sessions', $session->getId()); + $dbForProject->deleteCachedDocument('users', $user->getId()); + $events ->setParam('eventData', $response->output($user, Response::MODEL_USER)) ; - $dbForProject->deleteCachedDocument('users', $user->getId()); - $usage ->setParam('users.update', 1) ->setParam('users.sessions.delete', 1) From f642f19ca6a99a3674adf8bce2e2d6155f467c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 26 Apr 2022 10:46:35 +0000 Subject: [PATCH 33/87] Final test fix --- app/controllers/api/account.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index f8b9eb516b..d3fbe4e714 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -553,6 +553,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') Authorization::setRole('user:' . $user->getId()); + $dbForProject->updateDocument('users', $user->getId(), $user); + $session = $dbForProject->createDocument('sessions', $session ->setAttribute('$read', ['user:' . $user->getId()]) ->setAttribute('$write', ['user:' . $user->getId()]) From a32713011f3b459798d935ddc3b4e9321a1e15aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 27 Apr 2022 09:02:31 +0000 Subject: [PATCH 34/87] PR review changes --- app/tasks/maintenance.php | 3 +-- app/tasks/ssl.php | 17 +---------------- app/workers/certificates.php | 32 +++++++++++++++++--------------- composer.lock | 34 +++++++++++++--------------------- 4 files changed, 32 insertions(+), 54 deletions(-) diff --git a/app/tasks/maintenance.php b/app/tasks/maintenance.php index db2d2e808a..087b16cf95 100644 --- a/app/tasks/maintenance.php +++ b/app/tasks/maintenance.php @@ -131,8 +131,7 @@ $cli Console::loop(function() use ($register, $interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) { go(function () use ($register, $interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) { try { - [$database, $returnDatabase] = getConsoleDB - ($register, '_console'); + [$database, $returnDatabase] = getConsoleDB($register, '_console'); $time = date('d-m-Y H:i:s', time()); Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds"); diff --git a/app/tasks/ssl.php b/app/tasks/ssl.php index 18f7ee8324..e1724e8401 100644 --- a/app/tasks/ssl.php +++ b/app/tasks/ssl.php @@ -12,22 +12,7 @@ $cli ->desc('Validate server certificates') ->param('domain', App::getEnv('_APP_DOMAIN', ''), new Hostname(), 'Domain to generate certificate for. If empty, main domain will be used.', true) ->action(function ($domain) { - // HTTTP ping to check if domain is Appwrite server - $ch = \curl_init(); - \curl_setopt($ch, CURLOPT_URL, 'http://appwrite/manifest.json'); - \curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - \curl_setopt($ch, CURLOPT_TIMEOUT, 5); - \curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); - \curl_exec($ch); - $statusCode = \curl_getinfo($ch, CURLINFO_HTTP_CODE); - $error = \curl_error($ch); - \curl_close($ch); - - if($statusCode < 100) { - return Console::error('Appwrite connection refused with message: ' . $error); - } - - Console::success('Schedule a job to issue a TLS certificate for domain:' . $domain); + Console::success('Scheduling a job to issue a TLS certificate for domain:' . $domain); // Scheduje a job Resque::enqueue(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, [ diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 825bb1689c..25eda60a91 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -18,12 +18,15 @@ Console::success(APP_NAME . ' certificates worker v1 has started'); class CertificatesV1 extends Worker { + private $certificate = null; // run function fills this. onError callback uses it + public function getName(): string { return "certificates"; } public function init(): void { + } public function run(): void @@ -32,8 +35,7 @@ class CertificatesV1 extends Worker $dbForConsole = $this->getConsoleDB(); - $certificate = new Document(); - + $this->certificate = new Document(); /** * 1. Read arguments and validate domain @@ -70,7 +72,7 @@ class CertificatesV1 extends Worker $domain = $this->args['domain']; // String of domain (hostname) $domain = new Domain((!empty($domain)) ? $domain : ''); - $certificate->setAttribute('domain', $domain->get()); + $this->certificate->setAttribute('domain', $domain->get()); $skipRenewCheck = $this->args['skipRenewCheck'] ?? false; // If true, we won't double-check expiry from cert file @@ -169,7 +171,7 @@ class CertificatesV1 extends Worker // Command succeeded, store all data into document // We store stderr too, because it may include warnings // This is only stored if everytng below passes too. Otherwise, it will be overwritten by error message - $certificate->setAttribute('log', \json_encode([ + $this->certificate->setAttribute('log', \json_encode([ 'stdout' => $stdout, 'stderr' => $stderr, ])); @@ -217,26 +219,26 @@ class CertificatesV1 extends Worker $certData = openssl_x509_parse(file_get_contents($certPath)); $validTo = $certData['validTo_time_t'] ?? 0; $expiryInAdvance = (60*60*24*30); - $certificate->setAttribute('renewDate', $validTo - $expiryInAdvance); + $this->certificate->setAttribute('renewDate', $validTo - $expiryInAdvance); // All went well at this point 🥳 // Reset attempts count for next renwal - $certificate->setAttribute('attempts', 0); + $this->certificate->setAttribute('attempts', 0); // Mark issue date - $certificate->setAttribute('issueDate', \time()); + $this->certificate->setAttribute('issueDate', \time()); } catch(ExceptionCertificate $e) { // These exceptions are expected if renew shouldn't or can't happen // Add exception as log into certificate - $certificate->setAttribute('log', $e->getMessage()); + $this->certificate->setAttribute('log', $e->getMessage()); - $attempt = $certificate->getAttribute('attempts', 0); + $attempt = $this->certificate->getAttribute('attempts', 0); $attempt++; // Save increased attempts count - $certificate->setAttribute('attempts', $attempt); + $this->certificate->setAttribute('attempts', $attempt); Console::warning('Cannot renew domain (' . $domain->get() . ') on attempt no. ' . $attempt . ' certificate: ' . $e->getMessage()); @@ -256,22 +258,22 @@ class CertificatesV1 extends Worker ]); } finally { // All actions result in new updatedAt date - $certificate->setAttribute('updated', \time()); + $this->certificate->setAttribute('updated', \time()); // Save certificate data into database // Check if update or insert required $certificateDocument = $dbForConsole->findOne('certificates', [ new Query('domain', Query::TYPE_EQUAL, [$domain->get()]) ]); if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { // Merge new data with current data - $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); + $this->certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $this->certificate->getArrayCopy())); - $certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); + $this->certificate = $dbForConsole->updateDocument('certificates', $this->certificate->getId(), $this->certificate); } else { - $certificate = $dbForConsole->createDocument('certificates', $certificate); + $this->certificate = $dbForConsole->createDocument('certificates', $this->certificate); } // Update domains with new certificate ID - $certificateId = $certificate->getId(); + $certificateId = $this->certificate->getId(); $domains = $dbForConsole->find('domains', [ new Query('domain', Query::TYPE_EQUAL, [$domain->get()]) diff --git a/composer.lock b/composer.lock index 9bafae75a0..c82900e1bb 100644 --- a/composer.lock +++ b/composer.lock @@ -2250,16 +2250,16 @@ }, { "name": "utopia-php/framework", - "version": "0.19.8", + "version": "0.19.20", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "8c3b3e330546fd6cd65bd1f8d8d08882ff3abb7d" + "reference": "65ced168db8f6e188ceeb0d101f57552c3d8b2af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/8c3b3e330546fd6cd65bd1f8d8d08882ff3abb7d", - "reference": "8c3b3e330546fd6cd65bd1f8d8d08882ff3abb7d", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/65ced168db8f6e188ceeb0d101f57552c3d8b2af", + "reference": "65ced168db8f6e188ceeb0d101f57552c3d8b2af", "shasum": "" }, "require": { @@ -2293,9 +2293,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.19.8" + "source": "https://github.com/utopia-php/framework/tree/0.19.20" }, - "time": "2022-04-12T00:28:15+00:00" + "time": "2022-04-14T15:42:37+00:00" }, { "name": "utopia-php/image", @@ -3550,16 +3550,16 @@ }, { "name": "matthiasmullie/minify", - "version": "1.3.66", + "version": "1.3.67", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6" + "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/45fd3b0f1dfa2c965857c6d4a470bea52adc31a6", - "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", + "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", "shasum": "" }, "require": { @@ -3608,23 +3608,15 @@ ], "support": { "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.66" + "source": "https://github.com/matthiasmullie/minify/tree/1.3.67" }, "funding": [ { - "url": "https://github.com/[user1", - "type": "github" - }, - { - "url": "https://github.com/matthiasmullie] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g.", - "type": "github" - }, - { - "url": "https://github.com/user2", + "url": "https://github.com/matthiasmullie", "type": "github" } ], - "time": "2021-01-06T15:18:10+00:00" + "time": "2022-03-24T08:54:59+00:00" }, { "name": "matthiasmullie/path-converter", From 18c3f021af294a69a1d885a8d0e26b94817d7667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 27 Apr 2022 09:13:34 +0000 Subject: [PATCH 35/87] Add index --- app/config/collections.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/config/collections.php b/app/config/collections.php index 47ecdf4311..1ff3c66c46 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -1395,6 +1395,13 @@ $collections = [ 'lengths' => [100, 100], 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], ], + [ + '$id' => '_key_user', + 'type' => Database::INDEX_KEY, + 'attributes' => ['userId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], ], ], From e4379ec850d8c9117c815c83b98083f11705d4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 27 Apr 2022 09:42:15 +0000 Subject: [PATCH 36/87] PR review changes --- app/workers/certificates.php | 31 +++++++++++++------------- src/Appwrite/Exception/Certificate.php | 7 ------ 2 files changed, 15 insertions(+), 23 deletions(-) delete mode 100644 src/Appwrite/Exception/Certificate.php diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 25eda60a91..6c246d0af8 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -3,7 +3,6 @@ use Appwrite\Event\Event; use Appwrite\Network\Validator\CNAME; use Appwrite\Resque\Worker; -use Appwrite\Exception\Certificate as ExceptionCertificate; use Utopia\App; use Utopia\CLI\Console; use Utopia\Database\Document; @@ -91,11 +90,11 @@ class CertificatesV1 extends Worker } if (empty($domain->get())) { - throw new ExceptionCertificate('Missing certificate domain.'); + throw new Exception('Missing certificate domain.'); } if (!$domain->isKnown() || $domain->isTest()) { - throw new ExceptionCertificate('Unknown public suffix for domain.'); + throw new Exception('Unknown public suffix for domain.'); } if ($validateCNAME) { @@ -105,13 +104,13 @@ class CertificatesV1 extends Worker $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); if (!$target->isKnown() || $target->isTest()) { - throw new ExceptionCertificate('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.'); + throw new Exception('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.'); } // Verify domain with DNS records $validator = new CNAME($target->get()); if (!$validator->isValid($domain->get())) { - throw new ExceptionCertificate('Failed to verify domain DNS records.'); + throw new Exception('Failed to verify domain DNS records.'); } } else { // Main domain validation @@ -133,21 +132,21 @@ class CertificatesV1 extends Worker throw new Exception('Invalid expiry date.'); } } catch(\Throwable $th) { - throw new ExceptionCertificate('Unable to read certificate file (cert.pem).'); + throw new Exception('Unable to read certificate file (cert.pem).'); } // LetsEncrypt allows renewal 30 days before expiry $expiryInAdvance = (60*60*24*30); if ($validTo - $expiryInAdvance > \time()) { $validToVerbose = date('d-m-Y H:i:s', $validTo); - throw new ExceptionCertificate('Renew isn\'t required. Next renew at ' . $validToVerbose); + throw new Exception('Renew isn\'t required. Next renew at ' . $validToVerbose); } } // Email for alerts is required by LetsEncrypt $email = App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'); if (empty($email)) { - throw new ExceptionCertificate('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate.'); + throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate.'); } // LetsEncrypt communication to issue certificate (using certbot CLI) @@ -165,7 +164,7 @@ class CertificatesV1 extends Worker // Unexpected error, usually 5XX, API limits, ... if ($exit !== 0) { - throw new ExceptionCertificate('Failed to issue a certificate with message: ' . $stderr); + throw new Exception('Failed to issue a certificate with message: ' . $stderr); } // Command succeeded, store all data into document @@ -180,25 +179,25 @@ class CertificatesV1 extends Worker $path = APP_STORAGE_CERTIFICATES . '/' . $domain->get(); if (!\is_readable($path)) { if (!\mkdir($path, 0755, true)) { - throw new ExceptionCertificate('Failed to create path for certificate.'); + throw new Exception('Failed to create path for certificate.'); } } // Move generated files from certbot into our storage if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/cert.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/cert.pem')) { - throw new ExceptionCertificate('Failed to rename certificate cert.pem: '.\json_encode($stdout)); + throw new Exception('Failed to rename certificate cert.pem: '.\json_encode($stdout)); } if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/chain.pem')) { - throw new ExceptionCertificate('Failed to rename certificate chain.pem: ' . \json_encode($stdout)); + throw new Exception('Failed to rename certificate chain.pem: ' . \json_encode($stdout)); } if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/fullchain.pem')) { - throw new ExceptionCertificate('Failed to rename certificate fullchain.pem: ' . \json_encode($stdout)); + throw new Exception('Failed to rename certificate fullchain.pem: ' . \json_encode($stdout)); } if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/privkey.pem')) { - throw new ExceptionCertificate('Failed to rename certificate privkey.pem: ' . \json_encode($stdout)); + throw new Exception('Failed to rename certificate privkey.pem: ' . \json_encode($stdout)); } // This multi-line syntax helps IDE @@ -210,7 +209,7 @@ class CertificatesV1 extends Worker // Save configuration into Traefik using our new cert files if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain->get() . '.yml', $config)) { - throw new ExceptionCertificate('Failed to save Traefik configuration.'); + throw new Exception('Failed to save Traefik configuration.'); } // Read new renew date from cert file @@ -228,7 +227,7 @@ class CertificatesV1 extends Worker // Mark issue date $this->certificate->setAttribute('issueDate', \time()); - } catch(ExceptionCertificate $e) { + } catch(Throwable $e) { // These exceptions are expected if renew shouldn't or can't happen // Add exception as log into certificate diff --git a/src/Appwrite/Exception/Certificate.php b/src/Appwrite/Exception/Certificate.php deleted file mode 100644 index 0b044015d4..0000000000 --- a/src/Appwrite/Exception/Certificate.php +++ /dev/null @@ -1,7 +0,0 @@ - Date: Wed, 27 Apr 2022 13:20:08 +0000 Subject: [PATCH 37/87] Code quality improvement --- app/workers/deletes.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/workers/deletes.php b/app/workers/deletes.php index cf62089165..1748060b2c 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -18,6 +18,9 @@ use Utopia\Audit\Audit; require_once __DIR__ . '/../init.php'; +Authorization::disable(); +Authorization::setDefaultStatus(false); + Console::title('Deletes V1 Worker'); Console::success(APP_NAME . ' deletes worker v1 has started' . "\n"); @@ -38,7 +41,6 @@ class DeletesV1 extends Worker public function run(): void { - Authorization::disable(); $projectId = $this->args['projectId'] ?? ''; $type = $this->args['type'] ?? ''; @@ -113,8 +115,6 @@ class DeletesV1 extends Worker Console::error('No delete operation for type: ' . $type); break; } - - Authorization::reset(); } public function shutdown(): void From feb124d16577472804ae7fe596e441fc49ca1e0f Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 22:08:32 +0300 Subject: [PATCH 38/87] feat: add method to check for email verification --- src/Appwrite/Auth/OAuth2.php | 38 ++++++++++++------------- src/Appwrite/Auth/OAuth2/Amazon.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Apple.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Bitbucket.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Bitly.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Box.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Discord.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Dropbox.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Facebook.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Github.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Gitlab.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Google.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Linkedin.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Microsoft.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Mock.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Notion.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Paypal.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Salesforce.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Slack.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Spotify.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Stripe.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Tradeshift.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Twitch.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Vk.php | 10 +++++++ src/Appwrite/Auth/OAuth2/WordPress.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Yahoo.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Yammer.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Yandex.php | 10 +++++++ src/Appwrite/Auth/OAuth2/Zoom.php | 10 +++++++ 29 files changed, 299 insertions(+), 19 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2.php b/src/Appwrite/Auth/OAuth2.php index 4eafba7610..5ef4263833 100644 --- a/src/Appwrite/Auth/OAuth2.php +++ b/src/Appwrite/Auth/OAuth2.php @@ -52,54 +52,54 @@ abstract class OAuth2 /** * @return string */ - abstract public function getName():string; + abstract public function getName(): string; /** * @return string */ - abstract public function getLoginURL():string; + abstract public function getLoginURL(): string; /** * @param string $code * * @return array */ - abstract protected function getTokens(string $code):array; + abstract protected function getTokens(string $code): array; /** * @param string $refreshToken * * @return array */ - abstract public function refreshTokens(string $refreshToken):array; + abstract public function refreshTokens(string $refreshToken): array; /** * @param $accessToken * * @return string */ - abstract public function getUserID(string $accessToken):string; + abstract public function getUserEmail(string $accessToken): string; + + /** + * Is the OAuth email verified? + * + * @return bool + */ + abstract public function isEmailVerififed(): bool; /** * @param $accessToken * * @return string */ - abstract public function getUserEmail(string $accessToken):string; - - /** - * @param $accessToken - * - * @return string - */ - abstract public function getUserName(string $accessToken):string; + abstract public function getUserName(string $accessToken): string; /** * @param $scope * * @return $this */ - protected function addScope(string $scope):OAuth2 + protected function addScope(string $scope): OAuth2 { // Add a scope to the scopes array if it isn't already present if (!\in_array($scope, $this->scopes)) { @@ -111,7 +111,7 @@ abstract class OAuth2 /** * @return array */ - protected function getScopes():array + protected function getScopes(): array { return $this->scopes; } @@ -121,7 +121,7 @@ abstract class OAuth2 * * @return string */ - public function getAccessToken(string $code):string + public function getAccessToken(string $code): string { $tokens = $this->getTokens($code); return $tokens['access_token'] ?? ''; @@ -132,7 +132,7 @@ abstract class OAuth2 * * @return string */ - public function getRefreshToken(string $code):string + public function getRefreshToken(string $code): string { $tokens = $this->getTokens($code); return $tokens['refresh_token'] ?? ''; @@ -143,7 +143,7 @@ abstract class OAuth2 * * @return string */ - public function getAccessTokenExpiry(string $code):string + public function getAccessTokenExpiry(string $code): string { $tokens = $this->getTokens($code); return $tokens['expires_in'] ?? ''; @@ -170,7 +170,7 @@ abstract class OAuth2 * * @return string */ - protected function request(string $method, string $url = '', array $headers = [], string $payload = ''):string + protected function request(string $method, string $url = '', array $headers = [], string $payload = ''): string { $ch = \curl_init($url); diff --git a/src/Appwrite/Auth/OAuth2/Amazon.php b/src/Appwrite/Auth/OAuth2/Amazon.php index 7ec2769c9a..7b8cf6dbb0 100644 --- a/src/Appwrite/Auth/OAuth2/Amazon.php +++ b/src/Appwrite/Auth/OAuth2/Amazon.php @@ -146,6 +146,16 @@ class Amazon extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Apple.php b/src/Appwrite/Auth/OAuth2/Apple.php index f1ff4b724d..c380f7b6a7 100644 --- a/src/Appwrite/Auth/OAuth2/Apple.php +++ b/src/Appwrite/Auth/OAuth2/Apple.php @@ -146,6 +146,16 @@ class Apple extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Bitbucket.php b/src/Appwrite/Auth/OAuth2/Bitbucket.php index 74d684c845..5d6c6430d9 100644 --- a/src/Appwrite/Auth/OAuth2/Bitbucket.php +++ b/src/Appwrite/Auth/OAuth2/Bitbucket.php @@ -130,6 +130,16 @@ class Bitbucket extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Bitly.php b/src/Appwrite/Auth/OAuth2/Bitly.php index 02a21dc29e..a2d72b492d 100644 --- a/src/Appwrite/Auth/OAuth2/Bitly.php +++ b/src/Appwrite/Auth/OAuth2/Bitly.php @@ -148,6 +148,16 @@ class Bitly extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Box.php b/src/Appwrite/Auth/OAuth2/Box.php index 80d59e9198..a6a1aa27d8 100644 --- a/src/Appwrite/Auth/OAuth2/Box.php +++ b/src/Appwrite/Auth/OAuth2/Box.php @@ -147,6 +147,16 @@ class Box extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Discord.php b/src/Appwrite/Auth/OAuth2/Discord.php index dece646e55..7c31c01e71 100644 --- a/src/Appwrite/Auth/OAuth2/Discord.php +++ b/src/Appwrite/Auth/OAuth2/Discord.php @@ -141,6 +141,16 @@ class Discord extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Dropbox.php b/src/Appwrite/Auth/OAuth2/Dropbox.php index f67e10b01d..ea6a9c8fce 100644 --- a/src/Appwrite/Auth/OAuth2/Dropbox.php +++ b/src/Appwrite/Auth/OAuth2/Dropbox.php @@ -131,6 +131,16 @@ class Dropbox extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Facebook.php b/src/Appwrite/Auth/OAuth2/Facebook.php index 7759a54042..f4e3c54541 100644 --- a/src/Appwrite/Auth/OAuth2/Facebook.php +++ b/src/Appwrite/Auth/OAuth2/Facebook.php @@ -129,6 +129,16 @@ class Facebook extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Github.php b/src/Appwrite/Auth/OAuth2/Github.php index dddd4a5181..4ca85f2b13 100644 --- a/src/Appwrite/Auth/OAuth2/Github.php +++ b/src/Appwrite/Auth/OAuth2/Github.php @@ -137,6 +137,16 @@ class Github extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Gitlab.php b/src/Appwrite/Auth/OAuth2/Gitlab.php index 19ee1363a7..39c827389a 100644 --- a/src/Appwrite/Auth/OAuth2/Gitlab.php +++ b/src/Appwrite/Auth/OAuth2/Gitlab.php @@ -127,6 +127,16 @@ class Gitlab extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Google.php b/src/Appwrite/Auth/OAuth2/Google.php index 5b3ab938b3..b91fd91bee 100644 --- a/src/Appwrite/Auth/OAuth2/Google.php +++ b/src/Appwrite/Auth/OAuth2/Google.php @@ -137,6 +137,16 @@ class Google extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Linkedin.php b/src/Appwrite/Auth/OAuth2/Linkedin.php index 2519859a1b..577426e96b 100644 --- a/src/Appwrite/Auth/OAuth2/Linkedin.php +++ b/src/Appwrite/Auth/OAuth2/Linkedin.php @@ -148,6 +148,16 @@ class Linkedin extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Microsoft.php b/src/Appwrite/Auth/OAuth2/Microsoft.php index ebfd2e4e83..bc4ea55b51 100644 --- a/src/Appwrite/Auth/OAuth2/Microsoft.php +++ b/src/Appwrite/Auth/OAuth2/Microsoft.php @@ -137,6 +137,16 @@ class Microsoft extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Mock.php b/src/Appwrite/Auth/OAuth2/Mock.php index aef92dac23..494674dfb6 100644 --- a/src/Appwrite/Auth/OAuth2/Mock.php +++ b/src/Appwrite/Auth/OAuth2/Mock.php @@ -130,6 +130,16 @@ class Mock extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return true; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Notion.php b/src/Appwrite/Auth/OAuth2/Notion.php index 5c117caa3c..0a293e3c4d 100644 --- a/src/Appwrite/Auth/OAuth2/Notion.php +++ b/src/Appwrite/Auth/OAuth2/Notion.php @@ -134,6 +134,16 @@ class Notion extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Paypal.php b/src/Appwrite/Auth/OAuth2/Paypal.php index 39544c6bd8..e4e1bb626f 100644 --- a/src/Appwrite/Auth/OAuth2/Paypal.php +++ b/src/Appwrite/Auth/OAuth2/Paypal.php @@ -154,6 +154,16 @@ class Paypal extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Salesforce.php b/src/Appwrite/Auth/OAuth2/Salesforce.php index 04ef1901b5..167629a497 100644 --- a/src/Appwrite/Auth/OAuth2/Salesforce.php +++ b/src/Appwrite/Auth/OAuth2/Salesforce.php @@ -148,6 +148,16 @@ class Salesforce extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Slack.php b/src/Appwrite/Auth/OAuth2/Slack.php index 92c210151b..f11d9fb653 100644 --- a/src/Appwrite/Auth/OAuth2/Slack.php +++ b/src/Appwrite/Auth/OAuth2/Slack.php @@ -128,6 +128,16 @@ class Slack extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Spotify.php b/src/Appwrite/Auth/OAuth2/Spotify.php index 5c1a43299a..ac21b6161b 100644 --- a/src/Appwrite/Auth/OAuth2/Spotify.php +++ b/src/Appwrite/Auth/OAuth2/Spotify.php @@ -141,6 +141,16 @@ class Spotify extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Stripe.php b/src/Appwrite/Auth/OAuth2/Stripe.php index 589e6b7b81..35e01c6c87 100644 --- a/src/Appwrite/Auth/OAuth2/Stripe.php +++ b/src/Appwrite/Auth/OAuth2/Stripe.php @@ -141,6 +141,16 @@ class Stripe extends OAuth2 return $user['email'] ?? ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Tradeshift.php b/src/Appwrite/Auth/OAuth2/Tradeshift.php index 38b58432a0..b2a9720c89 100644 --- a/src/Appwrite/Auth/OAuth2/Tradeshift.php +++ b/src/Appwrite/Auth/OAuth2/Tradeshift.php @@ -141,6 +141,16 @@ class Tradeshift extends OAuth2 return $user['Username'] ?? ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Twitch.php b/src/Appwrite/Auth/OAuth2/Twitch.php index 0deeb088e2..befaeefc14 100644 --- a/src/Appwrite/Auth/OAuth2/Twitch.php +++ b/src/Appwrite/Auth/OAuth2/Twitch.php @@ -140,6 +140,16 @@ class Twitch extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Vk.php b/src/Appwrite/Auth/OAuth2/Vk.php index 5e54b14394..6bcd9b1691 100644 --- a/src/Appwrite/Auth/OAuth2/Vk.php +++ b/src/Appwrite/Auth/OAuth2/Vk.php @@ -150,6 +150,16 @@ class Vk extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/WordPress.php b/src/Appwrite/Auth/OAuth2/WordPress.php index 5b957d9495..c394a96552 100644 --- a/src/Appwrite/Auth/OAuth2/WordPress.php +++ b/src/Appwrite/Auth/OAuth2/WordPress.php @@ -131,6 +131,16 @@ class WordPress extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Yahoo.php b/src/Appwrite/Auth/OAuth2/Yahoo.php index ffefbe9c63..b6650cb739 100644 --- a/src/Appwrite/Auth/OAuth2/Yahoo.php +++ b/src/Appwrite/Auth/OAuth2/Yahoo.php @@ -161,6 +161,16 @@ class Yahoo extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Yammer.php b/src/Appwrite/Auth/OAuth2/Yammer.php index 6e9f4fb86b..97e6f5e133 100644 --- a/src/Appwrite/Auth/OAuth2/Yammer.php +++ b/src/Appwrite/Auth/OAuth2/Yammer.php @@ -130,6 +130,16 @@ class Yammer extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Yandex.php b/src/Appwrite/Auth/OAuth2/Yandex.php index 1f6dda0d1b..4aa4a93cfe 100644 --- a/src/Appwrite/Auth/OAuth2/Yandex.php +++ b/src/Appwrite/Auth/OAuth2/Yandex.php @@ -144,6 +144,16 @@ class Yandex extends OAuth2 return ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Zoom.php b/src/Appwrite/Auth/OAuth2/Zoom.php index 9aa77ceb91..f0f322e571 100644 --- a/src/Appwrite/Auth/OAuth2/Zoom.php +++ b/src/Appwrite/Auth/OAuth2/Zoom.php @@ -126,6 +126,16 @@ class Zoom extends OAuth2 return $response['email'] ?? ''; } + /** + * Is the OAuth email verified? + * + * @return bool + */ + public function isEmailVerififed(): bool + { + return false; + } + /** * @param $accessToken * From 957a99ce38d9c58a793dd6283bc3bf2e9d3e6cd6 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 22:08:40 +0300 Subject: [PATCH 39/87] feat: add method to check for email verification --- src/Appwrite/Auth/OAuth2/Paypal.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Paypal.php b/src/Appwrite/Auth/OAuth2/Paypal.php index e4e1bb626f..e87d3f4d76 100644 --- a/src/Appwrite/Auth/OAuth2/Paypal.php +++ b/src/Appwrite/Auth/OAuth2/Paypal.php @@ -161,7 +161,7 @@ class Paypal extends OAuth2 */ public function isEmailVerififed(): bool { - return false + return false; } /** From 16c45fad29488a03f3ea48c765ee6fb059473d7e Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 22:14:09 +0300 Subject: [PATCH 40/87] feat: add method to check for email verification --- src/Appwrite/Auth/OAuth2.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Amazon.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Apple.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Bitbucket.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Bitly.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Box.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Discord.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Dropbox.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Facebook.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Github.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Gitlab.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Google.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Linkedin.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Microsoft.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Mock.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Notion.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Paypal.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Salesforce.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Slack.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Spotify.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Stripe.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Tradeshift.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Twitch.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Vk.php | 6 ++++-- src/Appwrite/Auth/OAuth2/WordPress.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Yahoo.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Yammer.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Yandex.php | 6 ++++-- src/Appwrite/Auth/OAuth2/Zoom.php | 6 ++++-- 29 files changed, 116 insertions(+), 58 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2.php b/src/Appwrite/Auth/OAuth2.php index 5ef4263833..3956a138c1 100644 --- a/src/Appwrite/Auth/OAuth2.php +++ b/src/Appwrite/Auth/OAuth2.php @@ -81,11 +81,13 @@ abstract class OAuth2 abstract public function getUserEmail(string $accessToken): string; /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - abstract public function isEmailVerififed(): bool; + abstract public function isEmailVerififed(string $accessToken): bool; /** * @param $accessToken diff --git a/src/Appwrite/Auth/OAuth2/Amazon.php b/src/Appwrite/Auth/OAuth2/Amazon.php index 7b8cf6dbb0..d100600592 100644 --- a/src/Appwrite/Auth/OAuth2/Amazon.php +++ b/src/Appwrite/Auth/OAuth2/Amazon.php @@ -147,11 +147,13 @@ class Amazon extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Apple.php b/src/Appwrite/Auth/OAuth2/Apple.php index c380f7b6a7..a58644588c 100644 --- a/src/Appwrite/Auth/OAuth2/Apple.php +++ b/src/Appwrite/Auth/OAuth2/Apple.php @@ -147,11 +147,13 @@ class Apple extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Bitbucket.php b/src/Appwrite/Auth/OAuth2/Bitbucket.php index 5d6c6430d9..c321ca83ba 100644 --- a/src/Appwrite/Auth/OAuth2/Bitbucket.php +++ b/src/Appwrite/Auth/OAuth2/Bitbucket.php @@ -131,11 +131,13 @@ class Bitbucket extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Bitly.php b/src/Appwrite/Auth/OAuth2/Bitly.php index a2d72b492d..0b50e9127f 100644 --- a/src/Appwrite/Auth/OAuth2/Bitly.php +++ b/src/Appwrite/Auth/OAuth2/Bitly.php @@ -149,11 +149,13 @@ class Bitly extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Box.php b/src/Appwrite/Auth/OAuth2/Box.php index a6a1aa27d8..bdc7d52701 100644 --- a/src/Appwrite/Auth/OAuth2/Box.php +++ b/src/Appwrite/Auth/OAuth2/Box.php @@ -148,11 +148,13 @@ class Box extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Discord.php b/src/Appwrite/Auth/OAuth2/Discord.php index 7c31c01e71..b7b58cfa9f 100644 --- a/src/Appwrite/Auth/OAuth2/Discord.php +++ b/src/Appwrite/Auth/OAuth2/Discord.php @@ -142,11 +142,13 @@ class Discord extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Dropbox.php b/src/Appwrite/Auth/OAuth2/Dropbox.php index ea6a9c8fce..592a395a31 100644 --- a/src/Appwrite/Auth/OAuth2/Dropbox.php +++ b/src/Appwrite/Auth/OAuth2/Dropbox.php @@ -132,11 +132,13 @@ class Dropbox extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Facebook.php b/src/Appwrite/Auth/OAuth2/Facebook.php index f4e3c54541..61617d0ea2 100644 --- a/src/Appwrite/Auth/OAuth2/Facebook.php +++ b/src/Appwrite/Auth/OAuth2/Facebook.php @@ -130,11 +130,13 @@ class Facebook extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Github.php b/src/Appwrite/Auth/OAuth2/Github.php index 4ca85f2b13..cdf0119d4b 100644 --- a/src/Appwrite/Auth/OAuth2/Github.php +++ b/src/Appwrite/Auth/OAuth2/Github.php @@ -138,11 +138,13 @@ class Github extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Gitlab.php b/src/Appwrite/Auth/OAuth2/Gitlab.php index 39c827389a..1fa139f566 100644 --- a/src/Appwrite/Auth/OAuth2/Gitlab.php +++ b/src/Appwrite/Auth/OAuth2/Gitlab.php @@ -128,11 +128,13 @@ class Gitlab extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Google.php b/src/Appwrite/Auth/OAuth2/Google.php index b91fd91bee..70d3f9b378 100644 --- a/src/Appwrite/Auth/OAuth2/Google.php +++ b/src/Appwrite/Auth/OAuth2/Google.php @@ -138,11 +138,13 @@ class Google extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Linkedin.php b/src/Appwrite/Auth/OAuth2/Linkedin.php index 577426e96b..5ff8fc42cc 100644 --- a/src/Appwrite/Auth/OAuth2/Linkedin.php +++ b/src/Appwrite/Auth/OAuth2/Linkedin.php @@ -149,11 +149,13 @@ class Linkedin extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Microsoft.php b/src/Appwrite/Auth/OAuth2/Microsoft.php index bc4ea55b51..115756fc46 100644 --- a/src/Appwrite/Auth/OAuth2/Microsoft.php +++ b/src/Appwrite/Auth/OAuth2/Microsoft.php @@ -138,11 +138,13 @@ class Microsoft extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Mock.php b/src/Appwrite/Auth/OAuth2/Mock.php index 494674dfb6..dc53041dcb 100644 --- a/src/Appwrite/Auth/OAuth2/Mock.php +++ b/src/Appwrite/Auth/OAuth2/Mock.php @@ -131,11 +131,13 @@ class Mock extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return true; } diff --git a/src/Appwrite/Auth/OAuth2/Notion.php b/src/Appwrite/Auth/OAuth2/Notion.php index 0a293e3c4d..320f9108b8 100644 --- a/src/Appwrite/Auth/OAuth2/Notion.php +++ b/src/Appwrite/Auth/OAuth2/Notion.php @@ -135,11 +135,13 @@ class Notion extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Paypal.php b/src/Appwrite/Auth/OAuth2/Paypal.php index e87d3f4d76..f74ce9e9f7 100644 --- a/src/Appwrite/Auth/OAuth2/Paypal.php +++ b/src/Appwrite/Auth/OAuth2/Paypal.php @@ -155,11 +155,13 @@ class Paypal extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Salesforce.php b/src/Appwrite/Auth/OAuth2/Salesforce.php index 167629a497..ed640b0951 100644 --- a/src/Appwrite/Auth/OAuth2/Salesforce.php +++ b/src/Appwrite/Auth/OAuth2/Salesforce.php @@ -149,11 +149,13 @@ class Salesforce extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Slack.php b/src/Appwrite/Auth/OAuth2/Slack.php index f11d9fb653..655714c947 100644 --- a/src/Appwrite/Auth/OAuth2/Slack.php +++ b/src/Appwrite/Auth/OAuth2/Slack.php @@ -129,11 +129,13 @@ class Slack extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Spotify.php b/src/Appwrite/Auth/OAuth2/Spotify.php index ac21b6161b..4a9a955c30 100644 --- a/src/Appwrite/Auth/OAuth2/Spotify.php +++ b/src/Appwrite/Auth/OAuth2/Spotify.php @@ -142,11 +142,13 @@ class Spotify extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Stripe.php b/src/Appwrite/Auth/OAuth2/Stripe.php index 35e01c6c87..c136ce5599 100644 --- a/src/Appwrite/Auth/OAuth2/Stripe.php +++ b/src/Appwrite/Auth/OAuth2/Stripe.php @@ -142,11 +142,13 @@ class Stripe extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Tradeshift.php b/src/Appwrite/Auth/OAuth2/Tradeshift.php index b2a9720c89..968bc89b49 100644 --- a/src/Appwrite/Auth/OAuth2/Tradeshift.php +++ b/src/Appwrite/Auth/OAuth2/Tradeshift.php @@ -142,11 +142,13 @@ class Tradeshift extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Twitch.php b/src/Appwrite/Auth/OAuth2/Twitch.php index befaeefc14..4c8b35385d 100644 --- a/src/Appwrite/Auth/OAuth2/Twitch.php +++ b/src/Appwrite/Auth/OAuth2/Twitch.php @@ -141,11 +141,13 @@ class Twitch extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Vk.php b/src/Appwrite/Auth/OAuth2/Vk.php index 6bcd9b1691..cbcc98a0e5 100644 --- a/src/Appwrite/Auth/OAuth2/Vk.php +++ b/src/Appwrite/Auth/OAuth2/Vk.php @@ -151,11 +151,13 @@ class Vk extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/WordPress.php b/src/Appwrite/Auth/OAuth2/WordPress.php index c394a96552..76fb010fbd 100644 --- a/src/Appwrite/Auth/OAuth2/WordPress.php +++ b/src/Appwrite/Auth/OAuth2/WordPress.php @@ -132,11 +132,13 @@ class WordPress extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Yahoo.php b/src/Appwrite/Auth/OAuth2/Yahoo.php index b6650cb739..f2c1b2e45c 100644 --- a/src/Appwrite/Auth/OAuth2/Yahoo.php +++ b/src/Appwrite/Auth/OAuth2/Yahoo.php @@ -162,11 +162,13 @@ class Yahoo extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Yammer.php b/src/Appwrite/Auth/OAuth2/Yammer.php index 97e6f5e133..00c1c512cf 100644 --- a/src/Appwrite/Auth/OAuth2/Yammer.php +++ b/src/Appwrite/Auth/OAuth2/Yammer.php @@ -131,11 +131,13 @@ class Yammer extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Yandex.php b/src/Appwrite/Auth/OAuth2/Yandex.php index 4aa4a93cfe..f25d366f8e 100644 --- a/src/Appwrite/Auth/OAuth2/Yandex.php +++ b/src/Appwrite/Auth/OAuth2/Yandex.php @@ -145,11 +145,13 @@ class Yandex extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Zoom.php b/src/Appwrite/Auth/OAuth2/Zoom.php index f0f322e571..df63749fb0 100644 --- a/src/Appwrite/Auth/OAuth2/Zoom.php +++ b/src/Appwrite/Auth/OAuth2/Zoom.php @@ -127,11 +127,13 @@ class Zoom extends OAuth2 } /** - * Is the OAuth email verified? + * Check if the OAuth email is verified + * + * @param $accessToken * * @return bool */ - public function isEmailVerififed(): bool + public function isEmailVerififed(string $accessToken): bool { return false; } From d5b2f0c7d4ccb5fa16162fc1f46f8e071ce3df5b Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 22:32:06 +0300 Subject: [PATCH 41/87] feat: update paypal OAuth provider --- src/Appwrite/Auth/OAuth2/Paypal.php | 16 +++++++++++++++- src/Appwrite/Auth/OAuth2/Salesforce.php | 8 ++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Paypal.php b/src/Appwrite/Auth/OAuth2/Paypal.php index f74ce9e9f7..06637b5ec7 100644 --- a/src/Appwrite/Auth/OAuth2/Paypal.php +++ b/src/Appwrite/Auth/OAuth2/Paypal.php @@ -148,7 +148,13 @@ class Paypal extends OAuth2 $user = $this->getUser($accessToken); if (isset($user['emails'])) { - return $user['emails'][0]['value']; + $email = array_filter($user['emails'], function ($email) { + return $email['primary'] === true; + }); + + if (!empty($email)) { + return $email[0]['value']; + } } return ''; @@ -157,12 +163,20 @@ class Paypal extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://developer.paypal.com/docs/api/identity/v1/#userinfo_get + * * @param $accessToken * * @return bool */ public function isEmailVerififed(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['verified_account']) && $user['verified_account'] === true) { + return true; + } + return false; } diff --git a/src/Appwrite/Auth/OAuth2/Salesforce.php b/src/Appwrite/Auth/OAuth2/Salesforce.php index ed640b0951..699cbc7581 100644 --- a/src/Appwrite/Auth/OAuth2/Salesforce.php +++ b/src/Appwrite/Auth/OAuth2/Salesforce.php @@ -151,12 +151,20 @@ class Salesforce extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5 + * * @param $accessToken * * @return bool */ public function isEmailVerififed(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['email_verified']) && $user['email_verified'] === true) { + return true; + } + return false; } From 996504043ee26256550a6fae5a04ffc5b85b57b7 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 22:57:36 +0300 Subject: [PATCH 42/87] feat: update slack OAuth provider --- src/Appwrite/Auth/OAuth2/Slack.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Slack.php b/src/Appwrite/Auth/OAuth2/Slack.php index 655714c947..344790c162 100644 --- a/src/Appwrite/Auth/OAuth2/Slack.php +++ b/src/Appwrite/Auth/OAuth2/Slack.php @@ -131,12 +131,22 @@ class Slack extends OAuth2 /** * Check if the OAuth email is verified * + * In case of Slack, if an email is present, it is verified + * + * @link https://slack.com/help/articles/207262907-Change-your-email-address + * * @param $accessToken * * @return bool */ public function isEmailVerififed(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['user']['email'])) { + return true; + } + return false; } @@ -157,6 +167,8 @@ class Slack extends OAuth2 } /** + * @link https://api.slack.com/methods/users.identity + * * @param string $accessToken * * @return array @@ -164,7 +176,6 @@ class Slack extends OAuth2 protected function getUser(string $accessToken):array { if (empty($this->user)) { - // https://api.slack.com/methods/users.identity $user = $this->request( 'GET', 'https://slack.com/api/users.identity?token='.\urlencode($accessToken) From cf44f8c03d8387376599f58d6269fd06e8b3aa25 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 22:58:52 +0300 Subject: [PATCH 43/87] feat: update spotify OAuth provider --- src/Appwrite/Auth/OAuth2/Spotify.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Spotify.php b/src/Appwrite/Auth/OAuth2/Spotify.php index 4a9a955c30..7509e271a7 100644 --- a/src/Appwrite/Auth/OAuth2/Spotify.php +++ b/src/Appwrite/Auth/OAuth2/Spotify.php @@ -144,6 +144,8 @@ class Spotify extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://developer.spotify.com/documentation/web-api/reference/#/operations/get-current-users-profile + * * @param $accessToken * * @return bool @@ -179,7 +181,7 @@ class Spotify extends OAuth2 if (empty($this->user)) { $this->user = \json_decode($this->request( 'GET', - $this->resourceEndpoint . "me", + $this->resourceEndpoint . 'me', ['Authorization: Bearer '.\urlencode($accessToken)] ), true); } From f815201ada368a2a12c6eecee4689511e2331103 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 23:10:48 +0300 Subject: [PATCH 44/87] feat: update stripe OAuth provider --- src/Appwrite/Auth/OAuth2/Slack.php | 8 ++------ src/Appwrite/Auth/OAuth2/Stripe.php | 5 ++++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Slack.php b/src/Appwrite/Auth/OAuth2/Slack.php index 344790c162..f61c776d41 100644 --- a/src/Appwrite/Auth/OAuth2/Slack.php +++ b/src/Appwrite/Auth/OAuth2/Slack.php @@ -141,13 +141,9 @@ class Slack extends OAuth2 */ public function isEmailVerififed(string $accessToken): bool { - $user = $this->getUser($accessToken); + $email = $this->getUserEmail($accessToken); - if (isset($user['user']['email'])) { - return true; - } - - return false; + return !empty($email); } /** diff --git a/src/Appwrite/Auth/OAuth2/Stripe.php b/src/Appwrite/Auth/OAuth2/Stripe.php index c136ce5599..5cfceaacae 100644 --- a/src/Appwrite/Auth/OAuth2/Stripe.php +++ b/src/Appwrite/Auth/OAuth2/Stripe.php @@ -144,13 +144,16 @@ class Stripe extends OAuth2 /** * Check if the OAuth email is verified * + * Stripe emails if present are verfied. + * * @param $accessToken * * @return bool */ public function isEmailVerififed(string $accessToken): bool { - return false; + $email = $this->getUserEmail($accessToken); + return !empty($email); } /** From 274a61f2c463cd15936bf0adb6b23f24efe51136 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 23:19:15 +0300 Subject: [PATCH 45/87] feat: update Tradeshift OAuth provider --- src/Appwrite/Auth/OAuth2/Spotify.php | 2 ++ src/Appwrite/Auth/OAuth2/Stripe.php | 3 ++- src/Appwrite/Auth/OAuth2/Tradeshift.php | 6 +++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Spotify.php b/src/Appwrite/Auth/OAuth2/Spotify.php index 7509e271a7..d9f3589d0b 100644 --- a/src/Appwrite/Auth/OAuth2/Spotify.php +++ b/src/Appwrite/Auth/OAuth2/Spotify.php @@ -144,6 +144,8 @@ class Spotify extends OAuth2 /** * Check if the OAuth email is verified * + * Spotify does not assure that the email is verified + * * @link https://developer.spotify.com/documentation/web-api/reference/#/operations/get-current-users-profile * * @param $accessToken diff --git a/src/Appwrite/Auth/OAuth2/Stripe.php b/src/Appwrite/Auth/OAuth2/Stripe.php index 5cfceaacae..cf55156a14 100644 --- a/src/Appwrite/Auth/OAuth2/Stripe.php +++ b/src/Appwrite/Auth/OAuth2/Stripe.php @@ -144,7 +144,7 @@ class Stripe extends OAuth2 /** * Check if the OAuth email is verified * - * Stripe emails if present are verfied. + * Stripe emails if present are verfied. This was verified manually * * @param $accessToken * @@ -153,6 +153,7 @@ class Stripe extends OAuth2 public function isEmailVerififed(string $accessToken): bool { $email = $this->getUserEmail($accessToken); + return !empty($email); } diff --git a/src/Appwrite/Auth/OAuth2/Tradeshift.php b/src/Appwrite/Auth/OAuth2/Tradeshift.php index 968bc89b49..1aac40d934 100644 --- a/src/Appwrite/Auth/OAuth2/Tradeshift.php +++ b/src/Appwrite/Auth/OAuth2/Tradeshift.php @@ -144,13 +144,17 @@ class Tradeshift extends OAuth2 /** * Check if the OAuth email is verified * + * Tradeshift's signup process requires emails to be verified. This was verified manually + * * @param $accessToken * * @return bool */ public function isEmailVerififed(string $accessToken): bool { - return false; + $email = $this->getUser($accessToken); + + return !empty($email); } /** From 114e8300b7777a7d191987aa1294111564fac8b2 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 23:25:28 +0300 Subject: [PATCH 46/87] feat: update Twitch OAuth provider --- src/Appwrite/Auth/OAuth2/Slack.php | 2 +- src/Appwrite/Auth/OAuth2/Stripe.php | 4 ++-- src/Appwrite/Auth/OAuth2/Tradeshift.php | 4 ++-- src/Appwrite/Auth/OAuth2/Twitch.php | 8 +++++++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Slack.php b/src/Appwrite/Auth/OAuth2/Slack.php index f61c776d41..da09ad318a 100644 --- a/src/Appwrite/Auth/OAuth2/Slack.php +++ b/src/Appwrite/Auth/OAuth2/Slack.php @@ -131,7 +131,7 @@ class Slack extends OAuth2 /** * Check if the OAuth email is verified * - * In case of Slack, if an email is present, it is verified + * If present, the email is verified. This was verfied through a manual Slack sign up process * * @link https://slack.com/help/articles/207262907-Change-your-email-address * diff --git a/src/Appwrite/Auth/OAuth2/Stripe.php b/src/Appwrite/Auth/OAuth2/Stripe.php index cf55156a14..dc46974317 100644 --- a/src/Appwrite/Auth/OAuth2/Stripe.php +++ b/src/Appwrite/Auth/OAuth2/Stripe.php @@ -144,7 +144,7 @@ class Stripe extends OAuth2 /** * Check if the OAuth email is verified * - * Stripe emails if present are verfied. This was verified manually + * If present, the email is verified. This was verfied through a manual Stripe sign up process * * @param $accessToken * @@ -153,7 +153,7 @@ class Stripe extends OAuth2 public function isEmailVerififed(string $accessToken): bool { $email = $this->getUserEmail($accessToken); - + return !empty($email); } diff --git a/src/Appwrite/Auth/OAuth2/Tradeshift.php b/src/Appwrite/Auth/OAuth2/Tradeshift.php index 1aac40d934..96339137c1 100644 --- a/src/Appwrite/Auth/OAuth2/Tradeshift.php +++ b/src/Appwrite/Auth/OAuth2/Tradeshift.php @@ -144,7 +144,7 @@ class Tradeshift extends OAuth2 /** * Check if the OAuth email is verified * - * Tradeshift's signup process requires emails to be verified. This was verified manually + * If present, the email is verified. This was verfied through a manual Tradeshift sign up process * * @param $accessToken * @@ -153,7 +153,7 @@ class Tradeshift extends OAuth2 public function isEmailVerififed(string $accessToken): bool { $email = $this->getUser($accessToken); - + return !empty($email); } diff --git a/src/Appwrite/Auth/OAuth2/Twitch.php b/src/Appwrite/Auth/OAuth2/Twitch.php index 4c8b35385d..73cd57ec75 100644 --- a/src/Appwrite/Auth/OAuth2/Twitch.php +++ b/src/Appwrite/Auth/OAuth2/Twitch.php @@ -143,13 +143,19 @@ class Twitch extends OAuth2 /** * Check if the OAuth email is verified * + * If present, the email is verified + * + * @link https://dev.twitch.tv/docs/api/reference#get-users + * * @param $accessToken * * @return bool */ public function isEmailVerififed(string $accessToken): bool { - return false; + $email = $this->getUserEmail($accessToken); + + return !empty($email); } /** From a2542e7716c00afde49927e4606b6358b4073a40 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 23:27:21 +0300 Subject: [PATCH 47/87] feat: fixed typo --- src/Appwrite/Auth/OAuth2.php | 2 +- src/Appwrite/Auth/OAuth2/Amazon.php | 2 +- src/Appwrite/Auth/OAuth2/Apple.php | 2 +- src/Appwrite/Auth/OAuth2/Bitbucket.php | 2 +- src/Appwrite/Auth/OAuth2/Bitly.php | 2 +- src/Appwrite/Auth/OAuth2/Box.php | 2 +- src/Appwrite/Auth/OAuth2/Discord.php | 2 +- src/Appwrite/Auth/OAuth2/Dropbox.php | 2 +- src/Appwrite/Auth/OAuth2/Facebook.php | 2 +- src/Appwrite/Auth/OAuth2/Github.php | 2 +- src/Appwrite/Auth/OAuth2/Gitlab.php | 2 +- src/Appwrite/Auth/OAuth2/Google.php | 2 +- src/Appwrite/Auth/OAuth2/Linkedin.php | 2 +- src/Appwrite/Auth/OAuth2/Microsoft.php | 2 +- src/Appwrite/Auth/OAuth2/Mock.php | 2 +- src/Appwrite/Auth/OAuth2/Notion.php | 2 +- src/Appwrite/Auth/OAuth2/Paypal.php | 2 +- src/Appwrite/Auth/OAuth2/Salesforce.php | 2 +- src/Appwrite/Auth/OAuth2/Slack.php | 2 +- src/Appwrite/Auth/OAuth2/Spotify.php | 2 +- src/Appwrite/Auth/OAuth2/Stripe.php | 2 +- src/Appwrite/Auth/OAuth2/Tradeshift.php | 2 +- src/Appwrite/Auth/OAuth2/Twitch.php | 2 +- src/Appwrite/Auth/OAuth2/Vk.php | 2 +- src/Appwrite/Auth/OAuth2/WordPress.php | 2 +- src/Appwrite/Auth/OAuth2/Yahoo.php | 2 +- src/Appwrite/Auth/OAuth2/Yammer.php | 2 +- src/Appwrite/Auth/OAuth2/Yandex.php | 2 +- src/Appwrite/Auth/OAuth2/Zoom.php | 2 +- 29 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2.php b/src/Appwrite/Auth/OAuth2.php index 3956a138c1..10a81d7b6c 100644 --- a/src/Appwrite/Auth/OAuth2.php +++ b/src/Appwrite/Auth/OAuth2.php @@ -87,7 +87,7 @@ abstract class OAuth2 * * @return bool */ - abstract public function isEmailVerififed(string $accessToken): bool; + abstract public function isEmailVerified(string $accessToken): bool; /** * @param $accessToken diff --git a/src/Appwrite/Auth/OAuth2/Amazon.php b/src/Appwrite/Auth/OAuth2/Amazon.php index d100600592..c406817f36 100644 --- a/src/Appwrite/Auth/OAuth2/Amazon.php +++ b/src/Appwrite/Auth/OAuth2/Amazon.php @@ -153,7 +153,7 @@ class Amazon extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Apple.php b/src/Appwrite/Auth/OAuth2/Apple.php index a58644588c..de438c9889 100644 --- a/src/Appwrite/Auth/OAuth2/Apple.php +++ b/src/Appwrite/Auth/OAuth2/Apple.php @@ -153,7 +153,7 @@ class Apple extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Bitbucket.php b/src/Appwrite/Auth/OAuth2/Bitbucket.php index c321ca83ba..ee675d48c0 100644 --- a/src/Appwrite/Auth/OAuth2/Bitbucket.php +++ b/src/Appwrite/Auth/OAuth2/Bitbucket.php @@ -137,7 +137,7 @@ class Bitbucket extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Bitly.php b/src/Appwrite/Auth/OAuth2/Bitly.php index 0b50e9127f..4084874265 100644 --- a/src/Appwrite/Auth/OAuth2/Bitly.php +++ b/src/Appwrite/Auth/OAuth2/Bitly.php @@ -155,7 +155,7 @@ class Bitly extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Box.php b/src/Appwrite/Auth/OAuth2/Box.php index bdc7d52701..f58ce3692f 100644 --- a/src/Appwrite/Auth/OAuth2/Box.php +++ b/src/Appwrite/Auth/OAuth2/Box.php @@ -154,7 +154,7 @@ class Box extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Discord.php b/src/Appwrite/Auth/OAuth2/Discord.php index b7b58cfa9f..033150d045 100644 --- a/src/Appwrite/Auth/OAuth2/Discord.php +++ b/src/Appwrite/Auth/OAuth2/Discord.php @@ -148,7 +148,7 @@ class Discord extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Dropbox.php b/src/Appwrite/Auth/OAuth2/Dropbox.php index 592a395a31..ac03e89153 100644 --- a/src/Appwrite/Auth/OAuth2/Dropbox.php +++ b/src/Appwrite/Auth/OAuth2/Dropbox.php @@ -138,7 +138,7 @@ class Dropbox extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Facebook.php b/src/Appwrite/Auth/OAuth2/Facebook.php index 61617d0ea2..888f45caea 100644 --- a/src/Appwrite/Auth/OAuth2/Facebook.php +++ b/src/Appwrite/Auth/OAuth2/Facebook.php @@ -136,7 +136,7 @@ class Facebook extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Github.php b/src/Appwrite/Auth/OAuth2/Github.php index cdf0119d4b..d6c52d0eab 100644 --- a/src/Appwrite/Auth/OAuth2/Github.php +++ b/src/Appwrite/Auth/OAuth2/Github.php @@ -144,7 +144,7 @@ class Github extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Gitlab.php b/src/Appwrite/Auth/OAuth2/Gitlab.php index 1fa139f566..ee898e251a 100644 --- a/src/Appwrite/Auth/OAuth2/Gitlab.php +++ b/src/Appwrite/Auth/OAuth2/Gitlab.php @@ -134,7 +134,7 @@ class Gitlab extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Google.php b/src/Appwrite/Auth/OAuth2/Google.php index 70d3f9b378..bdc3b42adb 100644 --- a/src/Appwrite/Auth/OAuth2/Google.php +++ b/src/Appwrite/Auth/OAuth2/Google.php @@ -144,7 +144,7 @@ class Google extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Linkedin.php b/src/Appwrite/Auth/OAuth2/Linkedin.php index 5ff8fc42cc..92a2e32ed1 100644 --- a/src/Appwrite/Auth/OAuth2/Linkedin.php +++ b/src/Appwrite/Auth/OAuth2/Linkedin.php @@ -155,7 +155,7 @@ class Linkedin extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Microsoft.php b/src/Appwrite/Auth/OAuth2/Microsoft.php index 115756fc46..150fbda85b 100644 --- a/src/Appwrite/Auth/OAuth2/Microsoft.php +++ b/src/Appwrite/Auth/OAuth2/Microsoft.php @@ -144,7 +144,7 @@ class Microsoft extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Mock.php b/src/Appwrite/Auth/OAuth2/Mock.php index dc53041dcb..7e8848bdf9 100644 --- a/src/Appwrite/Auth/OAuth2/Mock.php +++ b/src/Appwrite/Auth/OAuth2/Mock.php @@ -137,7 +137,7 @@ class Mock extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return true; } diff --git a/src/Appwrite/Auth/OAuth2/Notion.php b/src/Appwrite/Auth/OAuth2/Notion.php index 320f9108b8..3687be0b82 100644 --- a/src/Appwrite/Auth/OAuth2/Notion.php +++ b/src/Appwrite/Auth/OAuth2/Notion.php @@ -141,7 +141,7 @@ class Notion extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Paypal.php b/src/Appwrite/Auth/OAuth2/Paypal.php index 06637b5ec7..d517c8c685 100644 --- a/src/Appwrite/Auth/OAuth2/Paypal.php +++ b/src/Appwrite/Auth/OAuth2/Paypal.php @@ -169,7 +169,7 @@ class Paypal extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { $user = $this->getUser($accessToken); diff --git a/src/Appwrite/Auth/OAuth2/Salesforce.php b/src/Appwrite/Auth/OAuth2/Salesforce.php index 699cbc7581..3181657235 100644 --- a/src/Appwrite/Auth/OAuth2/Salesforce.php +++ b/src/Appwrite/Auth/OAuth2/Salesforce.php @@ -157,7 +157,7 @@ class Salesforce extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { $user = $this->getUser($accessToken); diff --git a/src/Appwrite/Auth/OAuth2/Slack.php b/src/Appwrite/Auth/OAuth2/Slack.php index da09ad318a..f1c5b6af9c 100644 --- a/src/Appwrite/Auth/OAuth2/Slack.php +++ b/src/Appwrite/Auth/OAuth2/Slack.php @@ -139,7 +139,7 @@ class Slack extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { $email = $this->getUserEmail($accessToken); diff --git a/src/Appwrite/Auth/OAuth2/Spotify.php b/src/Appwrite/Auth/OAuth2/Spotify.php index d9f3589d0b..9c541cf8ee 100644 --- a/src/Appwrite/Auth/OAuth2/Spotify.php +++ b/src/Appwrite/Auth/OAuth2/Spotify.php @@ -152,7 +152,7 @@ class Spotify extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Stripe.php b/src/Appwrite/Auth/OAuth2/Stripe.php index dc46974317..b34937a53e 100644 --- a/src/Appwrite/Auth/OAuth2/Stripe.php +++ b/src/Appwrite/Auth/OAuth2/Stripe.php @@ -150,7 +150,7 @@ class Stripe extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { $email = $this->getUserEmail($accessToken); diff --git a/src/Appwrite/Auth/OAuth2/Tradeshift.php b/src/Appwrite/Auth/OAuth2/Tradeshift.php index 96339137c1..142216ce26 100644 --- a/src/Appwrite/Auth/OAuth2/Tradeshift.php +++ b/src/Appwrite/Auth/OAuth2/Tradeshift.php @@ -150,7 +150,7 @@ class Tradeshift extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { $email = $this->getUser($accessToken); diff --git a/src/Appwrite/Auth/OAuth2/Twitch.php b/src/Appwrite/Auth/OAuth2/Twitch.php index 73cd57ec75..acb8587ced 100644 --- a/src/Appwrite/Auth/OAuth2/Twitch.php +++ b/src/Appwrite/Auth/OAuth2/Twitch.php @@ -151,7 +151,7 @@ class Twitch extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { $email = $this->getUserEmail($accessToken); diff --git a/src/Appwrite/Auth/OAuth2/Vk.php b/src/Appwrite/Auth/OAuth2/Vk.php index cbcc98a0e5..fefd2f7862 100644 --- a/src/Appwrite/Auth/OAuth2/Vk.php +++ b/src/Appwrite/Auth/OAuth2/Vk.php @@ -157,7 +157,7 @@ class Vk extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/WordPress.php b/src/Appwrite/Auth/OAuth2/WordPress.php index 76fb010fbd..4f9019f69d 100644 --- a/src/Appwrite/Auth/OAuth2/WordPress.php +++ b/src/Appwrite/Auth/OAuth2/WordPress.php @@ -138,7 +138,7 @@ class WordPress extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Yahoo.php b/src/Appwrite/Auth/OAuth2/Yahoo.php index f2c1b2e45c..fb6b73f583 100644 --- a/src/Appwrite/Auth/OAuth2/Yahoo.php +++ b/src/Appwrite/Auth/OAuth2/Yahoo.php @@ -168,7 +168,7 @@ class Yahoo extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Yammer.php b/src/Appwrite/Auth/OAuth2/Yammer.php index 00c1c512cf..c5c4c34b55 100644 --- a/src/Appwrite/Auth/OAuth2/Yammer.php +++ b/src/Appwrite/Auth/OAuth2/Yammer.php @@ -137,7 +137,7 @@ class Yammer extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Yandex.php b/src/Appwrite/Auth/OAuth2/Yandex.php index f25d366f8e..385127ca74 100644 --- a/src/Appwrite/Auth/OAuth2/Yandex.php +++ b/src/Appwrite/Auth/OAuth2/Yandex.php @@ -151,7 +151,7 @@ class Yandex extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } diff --git a/src/Appwrite/Auth/OAuth2/Zoom.php b/src/Appwrite/Auth/OAuth2/Zoom.php index df63749fb0..8d3987d7c6 100644 --- a/src/Appwrite/Auth/OAuth2/Zoom.php +++ b/src/Appwrite/Auth/OAuth2/Zoom.php @@ -133,7 +133,7 @@ class Zoom extends OAuth2 * * @return bool */ - public function isEmailVerififed(string $accessToken): bool + public function isEmailVerified(string $accessToken): bool { return false; } From b810d52e590f510d9125820ce3b1d5895c0c0f8a Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 23:39:11 +0300 Subject: [PATCH 48/87] feat: update Wordpress OAuth provider --- src/Appwrite/Auth/OAuth2/WordPress.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Appwrite/Auth/OAuth2/WordPress.php b/src/Appwrite/Auth/OAuth2/WordPress.php index 4f9019f69d..03c1802834 100644 --- a/src/Appwrite/Auth/OAuth2/WordPress.php +++ b/src/Appwrite/Auth/OAuth2/WordPress.php @@ -134,12 +134,20 @@ class WordPress extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://developer.wordpress.com/docs/api/1.1/get/me/ + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['email_verified']) && $user['email_verified'] === true) { + return true; + } + return false; } From 99871309c1b3681d82d2af1027625ebb23f3973b Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 23:46:34 +0300 Subject: [PATCH 49/87] feat: update Yahoo OAuth provider --- src/Appwrite/Auth/OAuth2/Yahoo.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Yahoo.php b/src/Appwrite/Auth/OAuth2/Yahoo.php index fb6b73f583..90e797568f 100644 --- a/src/Appwrite/Auth/OAuth2/Yahoo.php +++ b/src/Appwrite/Auth/OAuth2/Yahoo.php @@ -164,13 +164,17 @@ class Yahoo extends OAuth2 /** * Check if the OAuth email is verified * + * If present, the email is verified. This was verfied through a manual Yahoo sign up process + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { - return false; + $email = $this->getUserEmail($accessToken); + + return !empty($email); } /** From 35ac32bd2255bc04cc778b6ecb22dd6b1f8c7126 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 27 Apr 2022 23:50:03 +0300 Subject: [PATCH 50/87] feat: update Yammer OAuth provider --- src/Appwrite/Auth/OAuth2/Yammer.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Yammer.php b/src/Appwrite/Auth/OAuth2/Yammer.php index c5c4c34b55..a50975c82e 100644 --- a/src/Appwrite/Auth/OAuth2/Yammer.php +++ b/src/Appwrite/Auth/OAuth2/Yammer.php @@ -133,13 +133,17 @@ class Yammer extends OAuth2 /** * Check if the OAuth email is verified * + * If present, the email is verified. + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { - return false; + $email = $this->getUserEmail($accessToken); + + return !empty($email); } /** From d52af8afa987fec16cfd8c03930aa0fe1931fb86 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 28 Apr 2022 10:44:09 +0300 Subject: [PATCH 51/87] feat: update Zoom & Amazon OAuth provider --- src/Appwrite/Auth/OAuth2/Amazon.php | 6 +++++- src/Appwrite/Auth/OAuth2/Zoom.php | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Amazon.php b/src/Appwrite/Auth/OAuth2/Amazon.php index c406817f36..edace5df6b 100644 --- a/src/Appwrite/Auth/OAuth2/Amazon.php +++ b/src/Appwrite/Auth/OAuth2/Amazon.php @@ -149,13 +149,17 @@ class Amazon extends OAuth2 /** * Check if the OAuth email is verified * + * If present, the email is verified. This was verfied through a manual Amazon sign up process + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { - return false; + $email = $this->getUserEmail($accessToken); + + return !empty($email); } /** diff --git a/src/Appwrite/Auth/OAuth2/Zoom.php b/src/Appwrite/Auth/OAuth2/Zoom.php index 8d3987d7c6..a36c3e44c1 100644 --- a/src/Appwrite/Auth/OAuth2/Zoom.php +++ b/src/Appwrite/Auth/OAuth2/Zoom.php @@ -129,12 +129,20 @@ class Zoom extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://marketplace.zoom.us/docs/api-reference/zoom-api/methods/#operation/user + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['verified']) && $user['verified'] === 1) { + return true; + } + return false; } From 73fe35f2022bd478ff5518f9e98a9e39374b3f2c Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 29 Apr 2022 13:08:22 +0300 Subject: [PATCH 52/87] feat: update Apple OAuth provider --- src/Appwrite/Auth/OAuth2/Apple.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Apple.php b/src/Appwrite/Auth/OAuth2/Apple.php index de438c9889..7c6436f3cb 100644 --- a/src/Appwrite/Auth/OAuth2/Apple.php +++ b/src/Appwrite/Auth/OAuth2/Apple.php @@ -136,10 +136,7 @@ class Apple extends OAuth2 */ public function getUserEmail(string $accessToken): string { - if (isset($this->claims['email']) && - !empty($this->claims['email']) && - isset($this->claims['email_verified']) && - $this->claims['email_verified'] === 'true') { + if (isset($this->claims['email']) && !empty($this->claims['email'])) { return $this->claims['email']; } @@ -149,12 +146,18 @@ class Apple extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://developer.apple.com/forums/thread/121411 + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { + if (isset($this->claims['email_verified']) && $this->claims['email_verified'] === 'true') { + return true; + } + return false; } From e253809f7e6806c65e8b8c8dcbd7fc933407c884 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 5 May 2022 02:45:16 +0400 Subject: [PATCH 53/87] feat: update Bitbucket OAuth provider --- composer.lock | 36 +++++++++++++------------- src/Appwrite/Auth/OAuth2/Auth0.php | 12 +++++++++ src/Appwrite/Auth/OAuth2/Bitbucket.php | 20 ++++++++++++-- src/Appwrite/Auth/OAuth2/Okta.php | 12 +++++++++ 4 files changed, 60 insertions(+), 20 deletions(-) diff --git a/composer.lock b/composer.lock index ca66336799..468c4604fa 100644 --- a/composer.lock +++ b/composer.lock @@ -3551,16 +3551,16 @@ }, { "name": "matthiasmullie/minify", - "version": "1.3.67", + "version": "1.3.68", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9" + "reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", - "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297", + "reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297", "shasum": "" }, "require": { @@ -3609,7 +3609,7 @@ ], "support": { "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.67" + "source": "https://github.com/matthiasmullie/minify/tree/1.3.68" }, "funding": [ { @@ -3617,7 +3617,7 @@ "type": "github" } ], - "time": "2022-03-24T08:54:59+00:00" + "time": "2022-04-19T08:28:56+00:00" }, { "name": "matthiasmullie/path-converter", @@ -5711,16 +5711,16 @@ }, { "name": "symfony/console", - "version": "v6.0.7", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e" + "reference": "0d00aa289215353aa8746a31d101f8e60826285c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e", - "reference": "70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e", + "url": "https://api.github.com/repos/symfony/console/zipball/0d00aa289215353aa8746a31d101f8e60826285c", + "reference": "0d00aa289215353aa8746a31d101f8e60826285c", "shasum": "" }, "require": { @@ -5786,7 +5786,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.7" + "source": "https://github.com/symfony/console/tree/v6.0.8" }, "funding": [ { @@ -5802,7 +5802,7 @@ "type": "tidelift" } ], - "time": "2022-03-31T17:18:25+00:00" + "time": "2022-04-20T15:01:42+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -6136,16 +6136,16 @@ }, { "name": "symfony/string", - "version": "v6.0.3", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2" + "reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/522144f0c4c004c80d56fa47e40e17028e2eefc2", - "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2", + "url": "https://api.github.com/repos/symfony/string/zipball/ac0aa5c2282e0de624c175b68d13f2c8f2e2649d", + "reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d", "shasum": "" }, "require": { @@ -6201,7 +6201,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.0.3" + "source": "https://github.com/symfony/string/tree/v6.0.8" }, "funding": [ { @@ -6217,7 +6217,7 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2022-04-22T08:18:02+00:00" }, { "name": "textalk/websocket", diff --git a/src/Appwrite/Auth/OAuth2/Auth0.php b/src/Appwrite/Auth/OAuth2/Auth0.php index b1c9c8ce1f..44b07c3a06 100644 --- a/src/Appwrite/Auth/OAuth2/Auth0.php +++ b/src/Appwrite/Auth/OAuth2/Auth0.php @@ -138,6 +138,18 @@ class Auth0 extends OAuth2 return ''; } + /** + * Check if the OAuth email is verified + * + * @param $accessToken + * + * @return bool + */ + public function isEmailVerified(string $accessToken): bool + { + return false; + } + /** * @param string $accessToken * diff --git a/src/Appwrite/Auth/OAuth2/Bitbucket.php b/src/Appwrite/Auth/OAuth2/Bitbucket.php index ee675d48c0..4c089b36c7 100644 --- a/src/Appwrite/Auth/OAuth2/Bitbucket.php +++ b/src/Appwrite/Auth/OAuth2/Bitbucket.php @@ -139,6 +139,12 @@ class Bitbucket extends OAuth2 */ public function isEmailVerified(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['is_confirmed']) && $user['is_confirmed'] === true) { + return true; + } + return false; } @@ -169,8 +175,18 @@ class Bitbucket extends OAuth2 $user = $this->request('GET', 'https://api.bitbucket.org/2.0/user?access_token='.\urlencode($accessToken)); $this->user = \json_decode($user, true); - $email = $this->request('GET', 'https://api.bitbucket.org/2.0/user/emails?access_token='.\urlencode($accessToken)); - $this->user['email'] = \json_decode($email, true)['values'][0]['email']; + $emails = $this->request('GET', 'https://api.bitbucket.org/2.0/user/emails?access_token='.\urlencode($accessToken)); + $emails = \json_decode($emails, true); + if (isset($emails['values'])) { + foreach ($emails['values'] as $email) { + if ($email['is_primary']) { + $this->user['email'] = $email['email']; + $this->user['is_confirmed'] = $email['is_confirmed']; + break; + } + } + } + } return $this->user; } diff --git a/src/Appwrite/Auth/OAuth2/Okta.php b/src/Appwrite/Auth/OAuth2/Okta.php index 7b1b0d19e1..cf74fc0874 100644 --- a/src/Appwrite/Auth/OAuth2/Okta.php +++ b/src/Appwrite/Auth/OAuth2/Okta.php @@ -138,6 +138,18 @@ class Okta extends OAuth2 return ''; } + /** + * Check if the OAuth email is verified + * + * @param $accessToken + * + * @return bool + */ + public function isEmailVerified(string $accessToken): bool + { + return false; + } + /** * @param string $accessToken * From 324eebcbf876cbf43740464ad484edae9716a9e0 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 5 May 2022 14:49:04 +0400 Subject: [PATCH 54/87] feat: removed VK since they do not expose email anymore --- app/config/providers.php | 10 -- public/images/users/vk.png | Bin 4198 -> 0 bytes src/Appwrite/Auth/OAuth2/Bitbucket.php | 2 +- src/Appwrite/Auth/OAuth2/Vk.php | 203 ------------------------- src/Appwrite/Auth/OAuth2/Yammer.php | 2 +- 5 files changed, 2 insertions(+), 215 deletions(-) delete mode 100644 public/images/users/vk.png delete mode 100644 src/Appwrite/Auth/OAuth2/Vk.php diff --git a/app/config/providers.php b/app/config/providers.php index 1d364a3fcf..c0acceb0fc 100644 --- a/app/config/providers.php +++ b/app/config/providers.php @@ -241,16 +241,6 @@ return [ // Ordered by ABC. 'beta' => false, 'mock' => false, ], - 'vk' => [ - 'name' => 'VK', - 'developers' => 'https://vk.com/dev', - 'icon' => 'icon-vk', - 'enabled' => true, - 'sandbox' => false, - 'form' => false, - 'beta' => false, - 'mock' => false, - ], 'zoom' => [ 'name' => 'Zoom', 'developers' => 'https://marketplace.zoom.us/docs/guides/auth/oauth/', diff --git a/public/images/users/vk.png b/public/images/users/vk.png deleted file mode 100644 index e3cf2d74ef008705322cee81a7e549a5d416f15f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4198 zcma)A2{@E%|9-8hs^nS_V54zz=yIh zu>qg>&6|rA{N0FRWdi{AI-(txhDBQ;J;?zoI6T>dph6D_1aSb+)u#vIJpBkXhzG%& zNYaB&)!m0ehq7U719c+?4pHVM3sKQiy>xBAEo) z)Wms^gK2tDD2PM8i_?f+Kh;Up@9u+vfNdf$gbEz?pL7D9_z(0=D}aBc6hy@>uGMzt|Ff}l)|H4)mHn#!sw-(if}dL_lRw=!SjH}(QyBZzgg7~UC;uJMp_W5G%_V@YwBC6 z+Y*AljlK>2iCZ-efox?OiSyiyyB;)%qKRO3e#r^|$R!0Qbtsxx?|h;^XPk%F zbGiZ}!&fAs@9UBC^m>+5f;H!rbnnO;%QN9*8FY1PNUV_9eUUW||I{iQlYlO}YBRU~HmY_>1Q`=IwNsnNc(d4X}s=asp|c~{kIuT57KJW*W3 zQwkf!W&Dx?1&h&*j2E!;8y?angwsPq^vd+?S*rM)bg2m`#LOd~u7kuGu}{Mv>^5Bc zqNY@t`8((5RAG(T)%v;oX3J7&96Ien&lRElj~A zkHNr6++CXCSI&quz7NOV!4C*Dga~-o`bI_yoN+aUIciL)-0o$5yj%FFhDqDkH~Hd{ z$ZDGZ;7hy^Bk>x7r~M-Hj~idcy)&C!&K9>i9%)$Mr?rJG4(<|h@P1&-MbZY}9Nd8x zPE-Kk-LZMI0GF@s2IrbkCWdzOs}mV67_nZ_sEx|~1IjEyjZsZK?mGO{O}CY^4rR

aT_%{VY$-u>i|n`%b2K>O_!_26!)ZhY6R^HYdY*aLSsRxXN9 z$byw-xJznca^$1|XL%Px@>JGRguu2+FwPHOKCK93X$XH#QgSG{BQTMKPteKPlE){2W$*i-le%?x#Yh^|n1Oh%^0EX`WzPM>dPKX~b&4KYn~@|qxc!oJB+5aY-J>Z*q7Vwp)SlqaZrB(tVR-%~7HnJpwPXFh+r9e- z&cI{Bkz?p&0~;qT{;J!d`zCHEDq9LIcgynMozM7sb$GE-MEHY#q~_Aw)7$%&aig4! zPf3PQo3#&9!5T06ddZFztsRla*YnAls~$`V)KRH(ciV7jmNLaf)%DNkcb=0645BXk z1}LDqsxWQN2iw>_7^Ysoz`FQ#au`~Z&Wuq@dUuT0|ABXG_$2WBVAQUhx*SqymHqyr zAx}?TN7NtF`Bd z)%H9$bb3aYfXX`o)A>Yi!R5DX2|b#&=IQBvhHLv6DiFjrBAOs!INkM>*LmjwlJ#ur|$G7wS4z60-DvKVZ9s|$8188Etc zw)kS0?T8XCJV7$;UcORM@8ojCTu}1Ee79Hc>^?nTvv{Y;*?!33WcR{srvr+KI^-^| zxG~?={LufDR1iW_eT+GOde!W!lzeDuw(xB?iptk# z=X0c3&a&KHZ`RrPz3bj^GUi0feSxy@$7?1553(G3{qF56g1MHcT$v15WR2NthS&(u z@_(EeQPW1!xUe@!A`MB6e5GGwk&L~_(`yGuW^XhnwK4XfC?q%^%-w3!N>7&^E*9Phno%;tj`cpe&*_pHHb{{Vm?`FEJ zjvbGjy+L*COltSF6Hbir2+8?eafMr8`(ZDj!8eU&JA8=|xAD1VBe`;AKI3ZZ3nljF zQ+$7jNsO8Sz>}}_Lb@@?1v`B%<(1)Giay1S6##_MDpN^ujBB%~E-go469$v2`<6|@ zJv5yQbQNK|p1Q*EJ@zg-Vkp_en%tF!5qYmgQP*X?vW1De6kxo7UHosdM&^BEe=L-2 zGXvl#BgZ(!ot9|MY%#LMep3+uPnds3pLVbG^Am#s@Yu$rmwi}OwF6=U%vA46Jd1&D z`i!y^a?wsl$>ikQuIAWqQsC(9%?>Z2qGGecNBEP?;-nHZ0pIbZ(To<5tpsqL+VS3G zCD3K!vDj**Tlo6)NJb=?x$@cSUiY0R#N6=WqYpUyR@+lKfRqzH>gkbp zS_pknW$C9Mrq4C4*J?f1N@{(x`$_=$MqybsW?2d}$(T)1SVRS2 zU_bNR;y&^f{3$~oJe;2YS1Wm{o3-d?HASG7n=*qI!33vM6mu~nW z`JCNjnKCW@BPqu(e+*d)Ue%O_#hjMlNMzWy(umn#Uywv%E*HOS{GH(z%PKMcemPf8 zuo0QN^QL2qPm`;(G0S`rp3%VLaHn~t?0&e>pF^=u>v28q6YDxNsmBugWRmk@23=z^ ldees9npiLx!TaQv#Phh?+!eYcOE*6cpiB>$lp485{|nq@6MX;x diff --git a/src/Appwrite/Auth/OAuth2/Bitbucket.php b/src/Appwrite/Auth/OAuth2/Bitbucket.php index 4c089b36c7..36d55fc3ce 100644 --- a/src/Appwrite/Auth/OAuth2/Bitbucket.php +++ b/src/Appwrite/Auth/OAuth2/Bitbucket.php @@ -179,7 +179,7 @@ class Bitbucket extends OAuth2 $emails = \json_decode($emails, true); if (isset($emails['values'])) { foreach ($emails['values'] as $email) { - if ($email['is_primary']) { + if ($email['is_confirmed']) { $this->user['email'] = $email['email']; $this->user['is_confirmed'] = $email['is_confirmed']; break; diff --git a/src/Appwrite/Auth/OAuth2/Vk.php b/src/Appwrite/Auth/OAuth2/Vk.php deleted file mode 100644 index fefd2f7862..0000000000 --- a/src/Appwrite/Auth/OAuth2/Vk.php +++ /dev/null @@ -1,203 +0,0 @@ - $this->appID, - 'redirect_uri' => $this->callback, - 'response_type' => 'code', - 'state' => \json_encode($this->state), - 'v' => $this->version, - 'scope' => \implode(' ', $this->getScopes()) - ]); - } - - /** - * @param string $code - * - * @return array - */ - protected function getTokens(string $code): array - { - if(empty($this->tokens)) { - $headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8']; - $this->tokens = \json_decode($this->request( - 'POST', - 'https://oauth.vk.com/access_token?', - $headers, - \http_build_query([ - 'code' => $code, - 'client_id' => $this->appID, - 'client_secret' => $this->appSecret, - 'redirect_uri' => $this->callback - ]) - ), true); - - $this->user['email'] = $this->tokens['email']; - $this->user['user_id'] = $this->tokens['user_id']; - } - - return $this->tokens; - } - - /** - * @param string $refreshToken - * - * @return array - */ - public function refreshTokens(string $refreshToken):array - { - $headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8']; - $this->tokens = \json_decode($this->request( - 'POST', - 'https://oauth.vk.com/access_token?', - $headers, - \http_build_query([ - 'refresh_token' => $refreshToken, - 'client_id' => $this->appID, - 'client_secret' => $this->appSecret, - 'grant_type' => 'refresh_token' - ]) - ), true); - - if(empty($this->tokens['refresh_token'])) { - $this->tokens['refresh_token'] = $refreshToken; - } - - $this->user['email'] = $this->tokens['email']; - $this->user['user_id'] = $this->tokens['user_id']; - - return $this->tokens; - } - - /** - * @param string $accessToken - * - * @return string - */ - public function getUserID(string $accessToken): string - { - $user = $this->getUser($accessToken); - - if (isset($user['user_id'])) { - return $user['user_id']; - } - - return ''; - } - - /** - * @param string $accessToken - * - * @return string - */ - public function getUserEmail(string $accessToken): string - { - $user = $this->getUser($accessToken); - - if (isset($user['email'])) { - return $user['email']; - } - - return ''; - } - - /** - * Check if the OAuth email is verified - * - * @param $accessToken - * - * @return bool - */ - public function isEmailVerified(string $accessToken): bool - { - return false; - } - - /** - * @param string $accessToken - * - * @return string - */ - public function getUserName(string $accessToken): string - { - $user = $this->getUser($accessToken); - - if (isset($user['name'])) { - return $user['name']; - } - - return ''; - } - - /** - * @param string $accessToken - * - * @return array - */ - protected function getUser(string $accessToken): array - { - if (empty($this->user['name'])) { - $user = $this->request( - 'GET', - 'https://api.vk.com/method/users.get?'. \http_build_query([ - 'v' => $this->version, - 'fields' => 'id,name,email,first_name,last_name', - 'access_token' => $accessToken - ]) - ); - - $user = \json_decode($user, true); - $this->user['name'] = $user['response'][0]['first_name'] ." ".$user['response'][0]['last_name']; - } - return $this->user; - } -} diff --git a/src/Appwrite/Auth/OAuth2/Yammer.php b/src/Appwrite/Auth/OAuth2/Yammer.php index a50975c82e..9ec9b56eea 100644 --- a/src/Appwrite/Auth/OAuth2/Yammer.php +++ b/src/Appwrite/Auth/OAuth2/Yammer.php @@ -133,7 +133,7 @@ class Yammer extends OAuth2 /** * Check if the OAuth email is verified * - * If present, the email is verified. + * If present, the email is verified. This was verfied through a manual Yammer sign up process * * @param $accessToken * From e052999c748755e26c90210fc4896d201703bcdb Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 5 May 2022 14:53:18 +0400 Subject: [PATCH 55/87] feat: added check for Okta OAuth --- src/Appwrite/Auth/OAuth2/Okta.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Appwrite/Auth/OAuth2/Okta.php b/src/Appwrite/Auth/OAuth2/Okta.php index cf74fc0874..e806e04f31 100644 --- a/src/Appwrite/Auth/OAuth2/Okta.php +++ b/src/Appwrite/Auth/OAuth2/Okta.php @@ -141,12 +141,20 @@ class Okta extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://developer.okta.com/docs/reference/api/oidc/#userinfo + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['email_verified']) && $user['email_verified'] === true) { + return true; + } + return false; } From cd31cf22d5efbe7276363397d3992f83bb4fc8e8 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 5 May 2022 14:59:32 +0400 Subject: [PATCH 56/87] feat: added check for Notion OAuth --- src/Appwrite/Auth/OAuth2/Notion.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Notion.php b/src/Appwrite/Auth/OAuth2/Notion.php index 3687be0b82..eee4ed6403 100644 --- a/src/Appwrite/Auth/OAuth2/Notion.php +++ b/src/Appwrite/Auth/OAuth2/Notion.php @@ -137,13 +137,17 @@ class Notion extends OAuth2 /** * Check if the OAuth email is verified * + * If present, the email is verified. This was verfied through a manual Notion sign up process + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { - return false; + $email = $this->getUserEmail($accessToken); + + return !empty($email); } /** From 7e4e0135b69586477b02592e6ea093ad0eb1712a Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 5 May 2022 15:09:43 +0400 Subject: [PATCH 57/87] feat: added check for Microsoft OAuth --- src/Appwrite/Auth/OAuth2/Microsoft.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Microsoft.php b/src/Appwrite/Auth/OAuth2/Microsoft.php index 150fbda85b..38f125336c 100644 --- a/src/Appwrite/Auth/OAuth2/Microsoft.php +++ b/src/Appwrite/Auth/OAuth2/Microsoft.php @@ -140,13 +140,17 @@ class Microsoft extends OAuth2 /** * Check if the OAuth email is verified * + * If present, the email is verified. This was verfied through a manual Microsoft sign up process + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { - return false; + $email = $this->getUserEmail($accessToken); + + return !empty($email); } /** From 974a684032d198a06b0472a990762e277b0da112 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 5 May 2022 19:51:02 +0400 Subject: [PATCH 58/87] feat: added check for Linkedin OAuth --- src/Appwrite/Auth/OAuth2/Linkedin.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Linkedin.php b/src/Appwrite/Auth/OAuth2/Linkedin.php index 92a2e32ed1..e13ac7869a 100644 --- a/src/Appwrite/Auth/OAuth2/Linkedin.php +++ b/src/Appwrite/Auth/OAuth2/Linkedin.php @@ -151,13 +151,17 @@ class Linkedin extends OAuth2 /** * Check if the OAuth email is verified * + * If present, the email is verified. This was verfied through a manual Linkedin sign up process + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { - return false; + $email = $this->getUserEmail($accessToken); + + return !empty($email); } /** From e2db8faaf3144197945f141408135581b6e05a8b Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 5 May 2022 20:07:27 +0400 Subject: [PATCH 59/87] feat: added check for Google OAuth --- src/Appwrite/Auth/OAuth2/Google.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Google.php b/src/Appwrite/Auth/OAuth2/Google.php index bdc3b42adb..2c78e63b5e 100644 --- a/src/Appwrite/Auth/OAuth2/Google.php +++ b/src/Appwrite/Auth/OAuth2/Google.php @@ -140,12 +140,20 @@ class Google extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://www.oauth.com/oauth2-servers/signing-in-with-google/verifying-the-user-info/ + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['email_verified']) && $user['email_verified'] === true) { + return true; + } + return false; } @@ -173,7 +181,7 @@ class Google extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $user = $this->request('GET', 'https://www.googleapis.com/oauth2/v2/userinfo?access_token='.\urlencode($accessToken)); + $user = $this->request('GET', 'https://www.googleapis.com/oauth2/v3/userinfo?access_token='.\urlencode($accessToken)); $this->user = \json_decode($user, true); } From d0fa65dee0cbd1b231d857e6e163388a05ab7217 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 5 May 2022 20:17:29 +0400 Subject: [PATCH 60/87] feat: added check for Gitlab OAuth --- src/Appwrite/Auth/OAuth2/Gitlab.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Appwrite/Auth/OAuth2/Gitlab.php b/src/Appwrite/Auth/OAuth2/Gitlab.php index ee898e251a..ed0515d0ad 100644 --- a/src/Appwrite/Auth/OAuth2/Gitlab.php +++ b/src/Appwrite/Auth/OAuth2/Gitlab.php @@ -130,12 +130,20 @@ class Gitlab extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://docs.gitlab.com/ee/api/users.html#list-current-user-for-normal-users + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['confirmed_at']) && $user['confirmed_at'] !== null) { + return true; + } + return false; } From ac4bca5887151fcde666e139e451ebc37e2820d1 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 6 May 2022 00:30:16 +0400 Subject: [PATCH 61/87] feat: added check for GitHub OAuth --- src/Appwrite/Auth/OAuth2/Github.php | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Github.php b/src/Appwrite/Auth/OAuth2/Github.php index d6c52d0eab..cff67b3326 100644 --- a/src/Appwrite/Auth/OAuth2/Github.php +++ b/src/Appwrite/Auth/OAuth2/Github.php @@ -126,12 +126,10 @@ class Github extends OAuth2 */ public function getUserEmail(string $accessToken):string { - $emails = \json_decode($this->request('GET', 'https://api.github.com/user/emails', ['Authorization: token '.\urlencode($accessToken)]), true); + $user = $this->getUser($accessToken); - foreach ($emails as $email) { - if ($email['primary'] && $email['verified']) { - return $email['email']; - } + if (isset($user['email'])) { + return $user['email']; } return ''; @@ -146,6 +144,12 @@ class Github extends OAuth2 */ public function isEmailVerified(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['verified']) && $user['verified'] !== null) { + return true; + } + return false; } @@ -174,6 +178,17 @@ class Github extends OAuth2 { if (empty($this->user)) { $this->user = \json_decode($this->request('GET', 'https://api.github.com/user', ['Authorization: token '.\urlencode($accessToken)]), true); + + $emails = $this->request('GET', 'https://api.github.com/user/emails', ['Authorization: token '.\urlencode($accessToken)]); + + $emails = \json_decode($emails, true); + foreach ($emails as $email) { + if (isset($email['verified']) && $email['verified'] === true) { + $this->user['email'] = $email['email']; + $this->user['verified'] = $email['verified']; + break; + } + } } return $this->user; From e58c8095cdf7af3e59849d9be7893253951f71a0 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 6 May 2022 00:32:49 +0400 Subject: [PATCH 62/87] feat: added check for GitHub OAuth --- src/Appwrite/Auth/OAuth2/Github.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Appwrite/Auth/OAuth2/Github.php b/src/Appwrite/Auth/OAuth2/Github.php index cff67b3326..dbed7ad818 100644 --- a/src/Appwrite/Auth/OAuth2/Github.php +++ b/src/Appwrite/Auth/OAuth2/Github.php @@ -138,6 +138,8 @@ class Github extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://docs.github.com/en/rest/users/emails#list-email-addresses-for-the-authenticated-user + * * @param $accessToken * * @return bool From 0a13706f0fd007fd41a655b96ad853e9bcf8823a Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 6 May 2022 00:41:35 +0400 Subject: [PATCH 63/87] feat: added check for Facebook OAuth --- src/Appwrite/Auth/OAuth2/Facebook.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Facebook.php b/src/Appwrite/Auth/OAuth2/Facebook.php index 888f45caea..619504207e 100644 --- a/src/Appwrite/Auth/OAuth2/Facebook.php +++ b/src/Appwrite/Auth/OAuth2/Facebook.php @@ -132,13 +132,17 @@ class Facebook extends OAuth2 /** * Check if the OAuth email is verified * + * If present, the email is verified. This was verfied through a manual Facebook sign up process + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { - return false; + $email = $this->getUserEmail($accessToken); + + return !empty($email); } /** From e60338065e1aaff94eccd5ca8e7c22c2f8221d26 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sat, 7 May 2022 14:45:36 +0400 Subject: [PATCH 64/87] feat: added check for Dropbox OAuth --- src/Appwrite/Auth/OAuth2/Dropbox.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Appwrite/Auth/OAuth2/Dropbox.php b/src/Appwrite/Auth/OAuth2/Dropbox.php index ac03e89153..c29b3ccb75 100644 --- a/src/Appwrite/Auth/OAuth2/Dropbox.php +++ b/src/Appwrite/Auth/OAuth2/Dropbox.php @@ -140,6 +140,12 @@ class Dropbox extends OAuth2 */ public function isEmailVerified(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['email_verified']) && $user['email_verified'] === true) { + return true; + } + return false; } From dcaa89e75f04054b9521a17ade90e2502ed466ae Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sat, 7 May 2022 14:47:45 +0400 Subject: [PATCH 65/87] feat: added check for Discord OAuth --- src/Appwrite/Auth/OAuth2/Discord.php | 8 ++++++++ src/Appwrite/Auth/OAuth2/Dropbox.php | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/Appwrite/Auth/OAuth2/Discord.php b/src/Appwrite/Auth/OAuth2/Discord.php index 033150d045..631f4a3f7e 100644 --- a/src/Appwrite/Auth/OAuth2/Discord.php +++ b/src/Appwrite/Auth/OAuth2/Discord.php @@ -144,12 +144,20 @@ class Discord extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://discord.com/developers/docs/resources/user + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['verified']) && $user['verified'] === true) { + return true; + } + return false; } diff --git a/src/Appwrite/Auth/OAuth2/Dropbox.php b/src/Appwrite/Auth/OAuth2/Dropbox.php index c29b3ccb75..04a07f486a 100644 --- a/src/Appwrite/Auth/OAuth2/Dropbox.php +++ b/src/Appwrite/Auth/OAuth2/Dropbox.php @@ -134,6 +134,8 @@ class Dropbox extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account + * * @param $accessToken * * @return bool From 984c6f0e30341c51560bc8b06a2a83051281a314 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sat, 7 May 2022 15:03:00 +0400 Subject: [PATCH 66/87] feat: added check for Box OAuth --- src/Appwrite/Auth/OAuth2/Box.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Box.php b/src/Appwrite/Auth/OAuth2/Box.php index f58ce3692f..6e2f893496 100644 --- a/src/Appwrite/Auth/OAuth2/Box.php +++ b/src/Appwrite/Auth/OAuth2/Box.php @@ -150,15 +150,19 @@ class Box extends OAuth2 /** * Check if the OAuth email is verified * + * If present, the email is verified. This was verfied through a manual Box sign up process + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { - return false; - } + $email = $this->getUserEmail($accessToken); + return !empty($email); + } + /** * @param string $accessToken * From b5717d749c4c39d1580fd254bc381560c5517eb0 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sat, 7 May 2022 16:06:08 +0400 Subject: [PATCH 67/87] feat: added check for Bitly OAuth --- src/Appwrite/Auth/OAuth2/Bitly.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Bitly.php b/src/Appwrite/Auth/OAuth2/Bitly.php index 4084874265..6feec88d1e 100644 --- a/src/Appwrite/Auth/OAuth2/Bitly.php +++ b/src/Appwrite/Auth/OAuth2/Bitly.php @@ -142,7 +142,11 @@ class Bitly extends OAuth2 $user = $this->getUser($accessToken); if (isset($user['emails'])) { - return $user['emails'][0]['email']; + foreach ($user['emails'] as $email) { + if ($email['is_verified'] === true) { + return $email['email']; + } + } } return ''; @@ -151,13 +155,15 @@ class Bitly extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://dev.bitly.com/api-reference#getUser + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { - return false; + return true; } /** From fa363a09ff96a28c6fa4839ed29d2721d17686e1 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sat, 7 May 2022 16:10:28 +0400 Subject: [PATCH 68/87] feat: added check for Auth0 OAuth --- src/Appwrite/Auth/OAuth2/Auth0.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Appwrite/Auth/OAuth2/Auth0.php b/src/Appwrite/Auth/OAuth2/Auth0.php index 44b07c3a06..ed16b04c08 100644 --- a/src/Appwrite/Auth/OAuth2/Auth0.php +++ b/src/Appwrite/Auth/OAuth2/Auth0.php @@ -141,12 +141,20 @@ class Auth0 extends OAuth2 /** * Check if the OAuth email is verified * + * @link https://auth0.com/docs/api/authentication?javascript#user-profile + * * @param $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { + $user = $this->getUser($accessToken); + + if (isset($user['email_verified']) && $user['email_verified'] === true) { + return true; + } + return false; } From 726b71ae3b27e8fc21b74bf2833b1a0039fc6018 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sat, 7 May 2022 19:43:55 +0400 Subject: [PATCH 69/87] feat: create new account if email not verified --- app/controllers/api/account.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 200b845987..13f391b290 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -476,9 +476,12 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') if ($user === false || $user->isEmpty()) { // No user logged in or with OAuth2 provider ID, create new one or connect with account with same email $name = $oauth2->getUserName($accessToken); $email = $oauth2->getUserEmail($accessToken); + $isVerified = $oauth2->isEmailVerified($accessToken); - $user = $dbForProject->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address - + if ($isVerified === true) { + $user = $dbForProject->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address + } + if ($user === false || $user->isEmpty()) { // Last option -> create the user, generate random password $limit = $project->getAttribute('auths', [])['limit'] ?? 0; @@ -497,7 +500,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') '$read' => ['role:all'], '$write' => ['user:' . $userId], 'email' => $email, - 'emailVerification' => true, + 'emailVerification' => $isVerified, 'status' => true, // Email should already be authenticated by OAuth2 provider 'password' => Auth::passwordHash(Auth::passwordGenerator()), 'passwordUpdate' => 0, @@ -522,7 +525,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') } // Create session token, verify user account and update OAuth2 ID and Access Token - $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); $secret = Auth::tokenGenerator(); From c2d11aab7e87dfef3fa52a488ed16daa73c85133 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Sun, 8 May 2022 21:35:28 +0200 Subject: [PATCH 70/87] fix: magic url email verification --- app/controllers/api/account.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 3462eec2cb..680b98274f 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -831,7 +831,11 @@ App::put('/v1/account/sessions/magic-url') $dbForProject->deleteDocument('tokens', $token); $dbForProject->deleteCachedDocument('users', $user->getId()); - $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND)); + $user + ->setAttribute('emailVerification', true) + ->setAttribute('sessions', $session, Document::SET_TYPE_APPEND); + + $user = $dbForProject->updateDocument('users', $user->getId(), $user); if (false === $user) { throw new Exception('Failed saving user to DB', 500, Exception::GENERAL_SERVER_ERROR); From d3560ea91b4597549d91b27e228d2459537f2687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 10 May 2022 10:28:16 +0000 Subject: [PATCH 71/87] PR review changes --- app/controllers/api/storage.php | 16 +--- composer.lock | 144 +++++++++++++++----------------- 2 files changed, 71 insertions(+), 89 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index e37c691082..590ec00f99 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -53,7 +53,7 @@ App::post('/v1/storage/buckets') ->param('read', null, new Permissions(), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true) ->param('write', null, new Permissions(), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true) ->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true) - ->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Integer(), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self-hosted setups you can change the max limit by changing the `_APP_STORAGE_LIMIT` environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true) + ->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self-hosted setups you can change the max limit by changing the `_APP_STORAGE_LIMIT` environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true) ->param('allowedFileExtensions', [], new ArrayList(new Text(64)), 'Allowed file extensions', true) ->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true) ->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true) @@ -67,11 +67,6 @@ App::post('/v1/storage/buckets') /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Stats\Stats $usage */ - // Maximum allowed size validation - if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) { - throw new Exception('Bucket maximum file size is larger than _APP_STORAGE_LIMIT.', 400, Exception::ATTRIBUTE_VALUE_INVALID); - } - $bucketId = $bucketId === 'unique()' ? $dbForProject->getId() : $bucketId; try { $files = Config::getParam('collections', [])['files'] ?? []; @@ -232,7 +227,7 @@ App::put('/v1/storage/buckets/:bucketId') ->param('read', null, new Permissions(), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true) ->param('write', null, new Permissions(), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true) ->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true) - ->param('maximumFileSize', null, new Integer(), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self hosted version you can change the limit by changing _APP_STORAGE_LIMIT environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true) + ->param('maximumFileSize', null, new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self hosted version you can change the limit by changing _APP_STORAGE_LIMIT environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true) ->param('allowedFileExtensions', [], new ArrayList(new Text(64)), 'Allowed file extensions', true) ->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true) ->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true) @@ -246,11 +241,6 @@ App::put('/v1/storage/buckets/:bucketId') /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Stats\Stats $usage */ - // Maximum allowed size validation - if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) { - throw new Exception('Bucket maximum file size allowed is larger than _APP_STORAGE_LIMIT.', 400, Exception::ATTRIBUTE_VALUE_INVALID); - } - $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { @@ -431,7 +421,7 @@ App::post('/v1/storage/buckets/:bucketId/files') $maximumFileSize = $bucket->getAttribute('maximumFileSize', 0); if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) { - throw new Exception('Error bucket maximum file size is larger than _APP_STORAGE_LIMIT', 400, Exception::STORAGE_INVALID_FILE_SIZE); + throw new Exception('Maximum bucket file size is larger than _APP_STORAGE_LIMIT', 500, Exception:: GENERAL_SERVER_ERROR); } $file = $request->getFiles('file'); diff --git a/composer.lock b/composer.lock index 04853e05f2..84f89fc7b4 100644 --- a/composer.lock +++ b/composer.lock @@ -300,16 +300,16 @@ }, { "name": "colinmollenhour/credis", - "version": "v1.12.2", + "version": "v1.13.0", "source": { "type": "git", "url": "https://github.com/colinmollenhour/credis.git", - "reference": "77e6ede2e01c4cfaade114fe1e07d2f9756949f1" + "reference": "afec8e58ec93d2291c127fa19709a048f28641e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/77e6ede2e01c4cfaade114fe1e07d2f9756949f1", - "reference": "77e6ede2e01c4cfaade114fe1e07d2f9756949f1", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/afec8e58ec93d2291c127fa19709a048f28641e5", + "reference": "afec8e58ec93d2291c127fa19709a048f28641e5", "shasum": "" }, "require": { @@ -341,9 +341,9 @@ "homepage": "https://github.com/colinmollenhour/credis", "support": { "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.12.2" + "source": "https://github.com/colinmollenhour/credis/tree/v1.13.0" }, - "time": "2022-03-08T18:12:43+00:00" + "time": "2022-04-07T14:57:22+00:00" }, { "name": "composer/package-versions-deprecated", @@ -1583,16 +1583,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced" + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/c726b64c1ccfe2896cb7df2e1331c357ad1c8ced", - "reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", "shasum": "" }, "require": { @@ -1630,7 +1630,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1" }, "funding": [ { @@ -1646,7 +1646,7 @@ "type": "tidelift" } ], - "time": "2021-11-01T23:48:49+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2250,16 +2250,16 @@ }, { "name": "utopia-php/framework", - "version": "0.19.7", + "version": "0.19.20", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "f17afe77a21873b9be18ebc05283813468b4283a" + "reference": "65ced168db8f6e188ceeb0d101f57552c3d8b2af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/f17afe77a21873b9be18ebc05283813468b4283a", - "reference": "f17afe77a21873b9be18ebc05283813468b4283a", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/65ced168db8f6e188ceeb0d101f57552c3d8b2af", + "reference": "65ced168db8f6e188ceeb0d101f57552c3d8b2af", "shasum": "" }, "require": { @@ -2293,9 +2293,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.19.7" + "source": "https://github.com/utopia-php/framework/tree/0.19.20" }, - "time": "2022-02-18T00:04:49+00:00" + "time": "2022-04-14T15:42:37+00:00" }, { "name": "utopia-php/image", @@ -3195,16 +3195,16 @@ }, { "name": "composer/semver", - "version": "3.3.1", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "5d8e574bb0e69188786b8ef77d43341222a41a71" + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/5d8e574bb0e69188786b8ef77d43341222a41a71", - "reference": "5d8e574bb0e69188786b8ef77d43341222a41a71", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", "shasum": "" }, "require": { @@ -3256,7 +3256,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.1" + "source": "https://github.com/composer/semver/tree/3.3.2" }, "funding": [ { @@ -3272,7 +3272,7 @@ "type": "tidelift" } ], - "time": "2022-03-16T11:22:07+00:00" + "time": "2022-04-01T19:23:25+00:00" }, { "name": "composer/xdebug-handler", @@ -3494,16 +3494,16 @@ }, { "name": "felixfbecker/language-server-protocol", - "version": "1.5.1", + "version": "v1.5.2", "source": { "type": "git", "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730" + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730", - "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", "shasum": "" }, "require": { @@ -3544,22 +3544,22 @@ ], "support": { "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1" + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" }, - "time": "2021-02-22T14:02:09+00:00" + "time": "2022-03-02T22:36:06+00:00" }, { "name": "matthiasmullie/minify", - "version": "1.3.66", + "version": "1.3.68", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6" + "reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/45fd3b0f1dfa2c965857c6d4a470bea52adc31a6", - "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297", + "reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297", "shasum": "" }, "require": { @@ -3608,23 +3608,15 @@ ], "support": { "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.66" + "source": "https://github.com/matthiasmullie/minify/tree/1.3.68" }, "funding": [ { - "url": "https://github.com/[user1", - "type": "github" - }, - { - "url": "https://github.com/matthiasmullie] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g.", - "type": "github" - }, - { - "url": "https://github.com/user2", + "url": "https://github.com/matthiasmullie", "type": "github" } ], - "time": "2021-01-06T15:18:10+00:00" + "time": "2022-04-19T08:28:56+00:00" }, { "name": "matthiasmullie/path-converter", @@ -5076,16 +5068,16 @@ }, { "name": "sebastian/environment", - "version": "5.1.3", + "version": "5.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac" + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", "shasum": "" }, "require": { @@ -5127,7 +5119,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" }, "funding": [ { @@ -5135,7 +5127,7 @@ "type": "github" } ], - "time": "2020-09-28T05:52:38+00:00" + "time": "2022-04-03T09:37:03+00:00" }, { "name": "sebastian/exporter", @@ -5718,16 +5710,16 @@ }, { "name": "symfony/console", - "version": "v6.0.5", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3bebf4108b9e07492a2a4057d207aa5a77d146b1" + "reference": "0d00aa289215353aa8746a31d101f8e60826285c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3bebf4108b9e07492a2a4057d207aa5a77d146b1", - "reference": "3bebf4108b9e07492a2a4057d207aa5a77d146b1", + "url": "https://api.github.com/repos/symfony/console/zipball/0d00aa289215353aa8746a31d101f8e60826285c", + "reference": "0d00aa289215353aa8746a31d101f8e60826285c", "shasum": "" }, "require": { @@ -5793,7 +5785,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.5" + "source": "https://github.com/symfony/console/tree/v6.0.8" }, "funding": [ { @@ -5809,7 +5801,7 @@ "type": "tidelift" } ], - "time": "2022-02-25T10:48:52+00:00" + "time": "2022-04-20T15:01:42+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -6061,16 +6053,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.0.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "36715ebf9fb9db73db0cb24263c79077c6fe8603" + "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/36715ebf9fb9db73db0cb24263c79077c6fe8603", - "reference": "36715ebf9fb9db73db0cb24263c79077c6fe8603", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e517458f278c2131ca9f262f8fbaf01410f2c65c", + "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c", "shasum": "" }, "require": { @@ -6123,7 +6115,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.0.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.0.1" }, "funding": [ { @@ -6139,20 +6131,20 @@ "type": "tidelift" } ], - "time": "2021-11-04T17:53:12+00:00" + "time": "2022-03-13T20:10:05+00:00" }, { "name": "symfony/string", - "version": "v6.0.3", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2" + "reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/522144f0c4c004c80d56fa47e40e17028e2eefc2", - "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2", + "url": "https://api.github.com/repos/symfony/string/zipball/ac0aa5c2282e0de624c175b68d13f2c8f2e2649d", + "reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d", "shasum": "" }, "require": { @@ -6208,7 +6200,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.0.3" + "source": "https://github.com/symfony/string/tree/v6.0.8" }, "funding": [ { @@ -6224,7 +6216,7 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2022-04-22T08:18:02+00:00" }, { "name": "textalk/websocket", @@ -6327,16 +6319,16 @@ }, { "name": "twig/twig", - "version": "v3.3.9", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "6ff9b0e440fa66f97f207e181c41340ddfa5683d" + "reference": "8442df056c51b706793adf80a9fd363406dd3674" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/6ff9b0e440fa66f97f207e181c41340ddfa5683d", - "reference": "6ff9b0e440fa66f97f207e181c41340ddfa5683d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/8442df056c51b706793adf80a9fd363406dd3674", + "reference": "8442df056c51b706793adf80a9fd363406dd3674", "shasum": "" }, "require": { @@ -6387,7 +6379,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.3.9" + "source": "https://github.com/twigphp/Twig/tree/v3.3.10" }, "funding": [ { @@ -6399,7 +6391,7 @@ "type": "tidelift" } ], - "time": "2022-03-25T09:37:52+00:00" + "time": "2022-04-06T06:47:41+00:00" }, { "name": "vimeo/psalm", @@ -6583,5 +6575,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } From 8b11ebd973a62b02e8b1cdf3dde1d7dab9bca9e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 10 May 2022 10:28:50 +0000 Subject: [PATCH 72/87] Update lockfile --- composer.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index 5e7f430976..091887d63b 100644 --- a/composer.lock +++ b/composer.lock @@ -3551,16 +3551,16 @@ }, { "name": "matthiasmullie/minify", - "version": "1.3.67", + "version": "1.3.68", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9" + "reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", - "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297", + "reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297", "shasum": "" }, "require": { @@ -3609,7 +3609,7 @@ ], "support": { "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.67" + "source": "https://github.com/matthiasmullie/minify/tree/1.3.68" }, "funding": [ { @@ -3617,7 +3617,7 @@ "type": "github" } ], - "time": "2022-03-24T08:54:59+00:00" + "time": "2022-04-19T08:28:56+00:00" }, { "name": "matthiasmullie/path-converter", @@ -5711,16 +5711,16 @@ }, { "name": "symfony/console", - "version": "v6.0.7", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e" + "reference": "0d00aa289215353aa8746a31d101f8e60826285c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e", - "reference": "70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e", + "url": "https://api.github.com/repos/symfony/console/zipball/0d00aa289215353aa8746a31d101f8e60826285c", + "reference": "0d00aa289215353aa8746a31d101f8e60826285c", "shasum": "" }, "require": { @@ -5786,7 +5786,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.7" + "source": "https://github.com/symfony/console/tree/v6.0.8" }, "funding": [ { @@ -5802,7 +5802,7 @@ "type": "tidelift" } ], - "time": "2022-03-31T17:18:25+00:00" + "time": "2022-04-20T15:01:42+00:00" }, { "name": "symfony/polyfill-intl-grapheme", From 30dfba6238775561faeb68509fc4d2276c40e630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 10 May 2022 10:30:44 +0000 Subject: [PATCH 73/87] Bug fix --- app/controllers/api/storage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index a47b0bded8..91727bb30b 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -397,7 +397,7 @@ App::post('/v1/storage/buckets/:bucketId/files') $maximumFileSize = $bucket->getAttribute('maximumFileSize', 0); if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) { - throw new Exception('Maximum bucket file size is larger than _APP_STORAGE_LIMIT', 500, Exception:: GENERAL_SERVER_ERROR); + throw new Exception('Maximum bucket file size is larger than _APP_STORAGE_LIMIT', 500, Exception::GENERAL_SERVER_ERROR); } $file = $request->getFiles('file'); From 6d94c1d6e8ffd0d4dd324b84ad83d3394742fc65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 11 May 2022 13:11:58 +0000 Subject: [PATCH 74/87] PR review changes --- app/realtime.php | 10 +- app/tasks/maintenance.php | 61 ++-- app/tasks/ssl.php | 1 + app/views/install/compose.phtml | 2 +- app/workers/certificates.php | 506 +++++++++++++++++++------------- composer.lock | 36 +-- 6 files changed, 352 insertions(+), 264 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index 275305e479..8d36069edb 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -93,7 +93,7 @@ $logError = function(Throwable $error, string $action) use ($register) { $server->error($logError); -function getConsoleDB(Registry &$register, string $namespace) +function getDatabase(Registry &$register, string $namespace) { $attempts = 0; @@ -141,7 +141,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume */ go(function () use ($register, $containerId, &$statsDocument, $logError) { try { - [$database, $returnDatabase] = getConsoleDB($register, '_console'); + [$database, $returnDatabase] = getDatabase($register, '_console'); $document = new Document([ '$id' => $database->getId(), '$collection' => 'realtime', @@ -194,7 +194,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume } try { - [$database, $returnDatabase] = getConsoleDB($register, '_console'); + [$database, $returnDatabase] = getDatabase($register, '_console'); $statsDocument ->setAttribute('timestamp', time()) @@ -221,7 +221,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, */ if ($realtime->hasSubscriber('console', 'role:member', 'project')) { - [$database, $returnDatabase] = getConsoleDB($register, '_console'); + [$database, $returnDatabase] = getDatabase($register, '_console'); $payload = []; @@ -325,7 +325,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, return; } - [$database, $returnDatabase] = getConsoleDB($register, "_{$projectId}"); + [$database, $returnDatabase] = getDatabase($register, "_{$projectId}"); $user = $database->getDocument('users', $userId); diff --git a/app/tasks/maintenance.php b/app/tasks/maintenance.php index 087b16cf95..056ee59fcd 100644 --- a/app/tasks/maintenance.php +++ b/app/tasks/maintenance.php @@ -9,26 +9,22 @@ use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Database; -use Utopia\Registry\Registry; use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Database\Query; -function getConsoleDB(Registry &$register) +function getConsoleDB(): Database { + global $register; + $attempts = 0; do { try { $attempts++; - - $db = $register->get('dbPool')->get(); - $redis = $register->get('redisPool')->get(); - - $cache = new Cache(new RedisCache($redis)); - $database = new Database(new MariaDB($db), $cache); + $cache = new Cache(new RedisCache($register->get('cache'))); + $database = new Database(new MariaDB($register->get('db')), $cache); $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite')); - $database->setNamespace('_console'); - + $database->setNamespace('_console'); // Main DB break; // leave loop if successful } catch(\Exception $e) { Console::warning("Database not ready. Retrying connection ({$attempts})..."); @@ -39,20 +35,13 @@ function getConsoleDB(Registry &$register) } } while ($attempts < DATABASE_RECONNECT_MAX_ATTEMPTS); - return [ - $database, - function () use ($register, $db, $redis) { - $register->get('dbPool')->put($db); - $register->get('redisPool')->put($redis); - } - ]; - -}; + return $database; +} $cli ->task('maintenance') ->desc('Schedules maintenance tasks and publishes them to resque') - ->action(function () use ($register) { + ->action(function () { Console::title('Maintenance V1'); Console::success(APP_NAME.' maintenance process v1 has started'); @@ -128,25 +117,17 @@ $cli $usageStatsRetention30m = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_30M', '129600');//36 hours $usageStatsRetention1d = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_1D', '8640000'); // 100 days - Console::loop(function() use ($register, $interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) { - go(function () use ($register, $interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) { - try { - [$database, $returnDatabase] = getConsoleDB($register, '_console'); - - $time = date('d-m-Y H:i:s', time()); - Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds"); - notifyDeleteExecutionLogs($executionLogsRetention); - notifyDeleteAbuseLogs($abuseLogsRetention); - notifyDeleteAuditLogs($auditLogRetention); - notifyDeleteUsageStats($usageStatsRetention30m, $usageStatsRetention1d); - notifyDeleteConnections(); - - renewCertificates($database); - } catch (\Throwable $th) { - throw $th; - } finally { - call_user_func($returnDatabase); - } - }); + Console::loop(function() use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) { + $database = getConsoleDB(); + + $time = date('d-m-Y H:i:s', time()); + Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds"); + notifyDeleteExecutionLogs($executionLogsRetention); + notifyDeleteAbuseLogs($abuseLogsRetention); + notifyDeleteAuditLogs($auditLogRetention); + notifyDeleteUsageStats($usageStatsRetention30m, $usageStatsRetention1d); + notifyDeleteConnections(); + + renewCertificates($database); }, $interval); }); \ No newline at end of file diff --git a/app/tasks/ssl.php b/app/tasks/ssl.php index e1724e8401..f28c0b8532 100644 --- a/app/tasks/ssl.php +++ b/app/tasks/ssl.php @@ -17,5 +17,6 @@ $cli // Scheduje a job Resque::enqueue(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, [ 'domain' => $domain, + 'skipRenewCheck' => true ]); }); \ No newline at end of file diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index c1c28d2e2c..470efc8750 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -339,6 +339,7 @@ services: environment: - _APP_ENV - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN - _APP_DOMAIN_TARGET - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_REDIS_HOST @@ -471,7 +472,6 @@ services: - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_DOMAIN_TARGET - - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 6c246d0af8..0b6a45f0c3 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -17,7 +17,13 @@ Console::success(APP_NAME . ' certificates worker v1 has started'); class CertificatesV1 extends Worker { - private $certificate = null; // run function fills this. onError callback uses it + + /** + * Database connection shared across all methods of this file + * + * @var Database + */ + private $dbForConsole; public function getName(): string { return "certificates"; @@ -32,16 +38,14 @@ class CertificatesV1 extends Worker { Authorization::disable(); - $dbForConsole = $this->getConsoleDB(); - - $this->certificate = new Document(); + $this->dbForConsole = $this->getConsoleDB(); /** * 1. Read arguments and validate domain * 2. Get main domain * 3. Validate CNAME DNS if parameter is not main domain (meaning it's custom domain) - * 4. Validate renew date with certificate file, unless requested to skip by parameter - * 5. Validate security email. Cannot be empty, required by LetsEncrypt + * 4. Validate security email. Cannot be empty, required by LetsEncrypt + * 5. Validate renew date with certificate file, unless requested to skip by parameter * 6. Issue a certificate using certbot CLI * 7. Update 'log' attribute on certificate document with Certbot message * 8. Create storage folder for certificate, if not ready already @@ -67,225 +71,71 @@ class CertificatesV1 extends Worker */ try { - // Get attributes + // Read arguments $domain = $this->args['domain']; // String of domain (hostname) + $skipRenewCheck = $this->args['skipRenewCheck'] ?? false; // If true, we won't double-check expiry from cert file + $domain = new Domain((!empty($domain)) ? $domain : ''); - $this->certificate->setAttribute('domain', $domain->get()); - - $skipRenewCheck = $this->args['skipRenewCheck'] ?? false; // If true, we won't double-check expiry from cert file - - $mainDomain = null; // ENV or first ever visited domain - if (!empty(App::getEnv('_APP_DOMAIN', ''))) { - $mainDomain = App::getEnv('_APP_DOMAIN', ''); - } else { - $domainDocument = $dbForConsole->findOne('domains', [], 0, ['_id'], ['ASC']); - $mainDomain = $domainDocument ? $domainDocument->getAttribute('domain') : $domain->get(); - } - - // If not main domain, we will check CNAME record - $validateCNAME = false; - if ($domain->get() !== $mainDomain) { - $validateCNAME = true; - } - - if (empty($domain->get())) { - throw new Exception('Missing certificate domain.'); - } - - if (!$domain->isKnown() || $domain->isTest()) { - throw new Exception('Unknown public suffix for domain.'); - } - - if ($validateCNAME) { - // TODO: Would be awesome to also support A/AAAA records here. Maybe dry run? - - // Validate if domain target is properly configured - $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); - - if (!$target->isKnown() || $target->isTest()) { - throw new Exception('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.'); - } - - // Verify domain with DNS records - $validator = new CNAME($target->get()); - if (!$validator->isValid($domain->get())) { - throw new Exception('Failed to verify domain DNS records.'); - } - } else { - // Main domain validation - // TODO: Would be awesome to check A/AAAA record here. Maybe dry run? + // Get current certificate + $certificate = $this->dbForConsole->findOne('certificates', [ new Query('domain', Query::TYPE_EQUAL, [$domain->get()]) ]); + + // If we don't have certificate for domain yet, let's create new document. At the end we save it + if(!$certificate) { + $certificate = new Document(); + $certificate->setAttribute('domain', $domain->get()); } - // If certificate exists already, double-check expiry date - // If asked to skip, we won't - $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/cert.pem'; - if (!$skipRenewCheck && \file_exists($certPath)) { - $validTo = null; - - try { - $certData = openssl_x509_parse(file_get_contents($certPath)); - - $validTo = $certData['validTo_time_t'] ?? 0; - - if (empty($validTo)) { - throw new Exception('Invalid expiry date.'); - } - } catch(\Throwable $th) { - throw new Exception('Unable to read certificate file (cert.pem).'); - } - - // LetsEncrypt allows renewal 30 days before expiry - $expiryInAdvance = (60*60*24*30); - if ($validTo - $expiryInAdvance > \time()) { - $validToVerbose = date('d-m-Y H:i:s', $validTo); - throw new Exception('Renew isn\'t required. Next renew at ' . $validToVerbose); - } - } - // Email for alerts is required by LetsEncrypt $email = App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'); if (empty($email)) { throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate.'); } - - // LetsEncrypt communication to issue certificate (using certbot CLI) - $stdout = ''; - $stderr = ''; - - $staging = (App::isProduction()) ? '' : ' --dry-run'; - $exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}" - . " --email " . $email - . " -w " . APP_STORAGE_CERTIFICATES - . " -d {$domain->get()}", '', $stdout, $stderr); - // All exceptions from now on will be marked to increment attempts count. This allows us to only limit attempts for domains that failed on LectEncrypt side. - // Such attempts count allows us to prevent API limit abuse with always failing domains - - // Unexpected error, usually 5XX, API limits, ... - if ($exit !== 0) { - throw new Exception('Failed to issue a certificate with message: ' . $stderr); + $mainDomain = $this->getMainDomain(); + $isMainDomain = !isset($mainDomain) || $domain->get() === $mainDomain; + $this->validateDomain($domain, $isMainDomain); + + // If certificate exists already, double-check expiry date + // If asked to skip, we won't + if(!$skipRenewCheck && !$this->isRenewRequired($domain->get())) { + throw new Exception('Renew isn\'t required.'); } + // Generate certificate files using Let's Encrypt + $letsEncryptData = $this->letsEncryptAction($domain->get(), $email); + // Command succeeded, store all data into document // We store stderr too, because it may include warnings - // This is only stored if everytng below passes too. Otherwise, it will be overwritten by error message - $this->certificate->setAttribute('log', \json_encode([ - 'stdout' => $stdout, - 'stderr' => $stderr, + $certificate->setAttribute('log', \json_encode([ + 'stdout' => $letsEncryptData['stdout'], + 'stderr' => $letsEncryptData['stderr'], ])); - // Prepare folder in storage for domain - $path = APP_STORAGE_CERTIFICATES . '/' . $domain->get(); - if (!\is_readable($path)) { - if (!\mkdir($path, 0755, true)) { - throw new Exception('Failed to create path for certificate.'); - } - } - - // Move generated files from certbot into our storage - if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/cert.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/cert.pem')) { - throw new Exception('Failed to rename certificate cert.pem: '.\json_encode($stdout)); - } - - if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/chain.pem')) { - throw new Exception('Failed to rename certificate chain.pem: ' . \json_encode($stdout)); - } - - if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/fullchain.pem')) { - throw new Exception('Failed to rename certificate fullchain.pem: ' . \json_encode($stdout)); - } - - if (!@\rename('/etc/letsencrypt/live/' . $domain->get() . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/privkey.pem')) { - throw new Exception('Failed to rename certificate privkey.pem: ' . \json_encode($stdout)); - } - - // This multi-line syntax helps IDE - $config = - "tls:" . - " certificates:" . - " - certFile: /storage/certificates/{$domain->get()}/fullchain.pem" . - " keyFile: /storage/certificates/{$domain->get()}/privkey.pem"; + // Give certificates to Traefik + $this->applyCertificateFiles($domain->get()); - // Save configuration into Traefik using our new cert files - if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain->get() . '.yml', $config)) { - throw new Exception('Failed to save Traefik configuration.'); - } - - // Read new renew date from cert file - // TODO: This might not be required, we could calculate it. But this feels safer - $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain->get() . '/cert.pem'; - $certData = openssl_x509_parse(file_get_contents($certPath)); - $validTo = $certData['validTo_time_t'] ?? 0; - $expiryInAdvance = (60*60*24*30); - $this->certificate->setAttribute('renewDate', $validTo - $expiryInAdvance); - - // All went well at this point 🥳 - - // Reset attempts count for next renwal - $this->certificate->setAttribute('attempts', 0); - - // Mark issue date - $this->certificate->setAttribute('issueDate', \time()); + // Update certificate info stored in database + $certificate->setAttribute('renewDate', $this->getRenewDate($domain->get())); + $certificate->setAttribute('attempts', 0); + $certificate->setAttribute('issueDate', \time()); } catch(Throwable $e) { - // These exceptions are expected if renew shouldn't or can't happen + // Set exception as log in certificate document + $certificate->setAttribute('log', $e->getMessage()); - // Add exception as log into certificate - $this->certificate->setAttribute('log', $e->getMessage()); - - $attempt = $this->certificate->getAttribute('attempts', 0); - $attempt++; - - // Save increased attempts count - $this->certificate->setAttribute('attempts', $attempt); - - Console::warning('Cannot renew domain (' . $domain->get() . ') on attempt no. ' . $attempt . ' certificate: ' . $e->getMessage()); + // Increase attempts count + $attempts = $certificate->getAttribute('attempts', 0) + 1; + $certificate->setAttribute('attempts', $attempts); // Send email to security email - Resque::enqueue(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME, [ - 'from' => 'console', - 'project' => 'console', - 'name' => 'Appwrite Administrator', - 'recipient' => App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'), - 'url' => 'https://' . $domain->get(), - 'locale' => App::getEnv('_APP_LOCALE', 'en'), - 'type' => MAIL_TYPE_CERTIFICATE, - - 'domain' => $domain->get(), - 'error' => $e->getMessage(), - 'attempt' => $attempt - ]); + $this->notifyError($domain->get(), $e->getMessage(), $attempts); } finally { // All actions result in new updatedAt date - $this->certificate->setAttribute('updated', \time()); - - // Save certificate data into database - // Check if update or insert required - $certificateDocument = $dbForConsole->findOne('certificates', [ new Query('domain', Query::TYPE_EQUAL, [$domain->get()]) ]); - if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { - // Merge new data with current data - $this->certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $this->certificate->getArrayCopy())); - - $this->certificate = $dbForConsole->updateDocument('certificates', $this->certificate->getId(), $this->certificate); - } else { - $this->certificate = $dbForConsole->createDocument('certificates', $this->certificate); - } - - // Update domains with new certificate ID - $certificateId = $this->certificate->getId(); - - $domains = $dbForConsole->find('domains', [ - new Query('domain', Query::TYPE_EQUAL, [$domain->get()]) - ], 1000); - - foreach ($domains as $domainDocument) { - $domainDocument->setAttribute('updated', \time()); - $domainDocument->setAttribute('certificateId', $certificateId); - - $dbForConsole->updateDocument('domains', $domainDocument->getId(), $domainDocument); - $dbForConsole->deleteCachedDocument('projects', $domainDocument->getAttribute('projectId')); - } + $certificate->setAttribute('updated', \time()); + // Save all changes we made to certificate document into database + $this->saveCertificateDocument($domain->get(), $certificate); + Authorization::reset(); } } @@ -293,4 +143,260 @@ class CertificatesV1 extends Worker public function shutdown(): void { } + + /** + * Save certificate data into database. + * + * @param string $domain Domain name that certificate is for + * @param Document $certificate Certificate document that we need to save + * + * @return void + */ + private function saveCertificateDocument(string $domain, Document $certificate) { + // Check if update or insert required + $certificateDocument = $this->dbForConsole->findOne('certificates', [ new Query('domain', Query::TYPE_EQUAL, [$domain]) ]); + if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { + // Merge new data with current data + $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); + + $certificate = $this->dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); + } else { + $certificate = $this->dbForConsole->createDocument('certificates', $certificate); + } + + $certificateId = $certificate->getId(); + $this->updateDomainDocuments($certificateId, $domain); + } + + /** + * Get main domain. Needed as we do different checks for main and non-main domains. + * + * @return null|string Returns main domain. If null, there is no main domain yet. + */ + private function getMainDomain(): ?string { + if (!empty(App::getEnv('_APP_DOMAIN', ''))) { + $mainDomain = App::getEnv('_APP_DOMAIN', ''); + } else { + $domainDocument = $this->dbForConsole->findOne('domains', [], 0, ['_id'], ['ASC']); + if($domainDocument) { + $mainDomain = $domainDocument->getAttribute('domain'); + } + } + + return $mainDomain; + } + + /** + * Internal domain validation functionality to prevent unnecessary attempts failed from Let's Encrypt side. We check: + * - Domain needs to be public and valid (prevents NFT domains that are not supported by Let's Encrypt) + * - Domain must have proper DNS record + * + * @param Domain $domain Domain which we validate + * @param bool $isMainDomain In case of master domain, we look for different DNS configurations + * + * @return void + */ + private function validateDomain(Domain $domain, bool $isMainDomain): void { + if (empty($domain->get())) { + throw new Exception('Missing certificate domain.'); + } + + if (!$domain->isKnown() || $domain->isTest()) { + throw new Exception('Unknown public suffix for domain.'); + } + + if (!$isMainDomain) { + // TODO: Would be awesome to also support A/AAAA records here. Maybe dry run? + + // Validate if domain target is properly configured + $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); + + if (!$target->isKnown() || $target->isTest()) { + throw new Exception('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.'); + } + + // Verify domain with DNS records + $validator = new CNAME($target->get()); + if (!$validator->isValid($domain->get())) { + throw new Exception('Failed to verify domain DNS records.'); + } + } else { + // Main domain validation + // TODO: Would be awesome to check A/AAAA record here. Maybe dry run? + } + } + + /** + * Reads expiry date of certificate from file and decides if renewal is required or not. + * + * @param string $domain Domain for which we check certificate file + * + * @return bool True, if certificate needs to be renewed + */ + private function isRenewRequired(string $domain): bool { + $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; + if (\file_exists($certPath)) { + $validTo = null; + + $certData = openssl_x509_parse(file_get_contents($certPath)); + $validTo = $certData['validTo_time_t'] ?? 0; + + if (empty($validTo)) { + throw new Exception('Unable to read certificate file (cert.pem).'); + } + + // LetsEncrypt allows renewal 30 days before expiry + $expiryInAdvance = (60*60*24*30); + if ($validTo - $expiryInAdvance > \time()) { + return false; + } + } + + return true; + } + + /** + * LetsEncrypt communication to issue certificate (using certbot CLI) + * + * @param string $domain Domain to generate certificate for + * + * @return array Named array with keys 'stdout' and 'stderr', both string + */ + private function letsEncryptAction(string $domain, string $email): array { + $staging = (App::isProduction()) ? '' : ' --dry-run'; + + $stdout = ''; + $stderr = ''; + + $staging = (App::isProduction()) ? '' : ' --dry-run'; + $exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}" + . " --email " . $email + . " -w " . APP_STORAGE_CERTIFICATES + . " -d {$domain}", '', $stdout, $stderr); + + // Unexpected error, usually 5XX, API limits, ... + if ($exit !== 0) { + throw new Exception('Failed to issue a certificate with message: ' . $stderr); + } + + return [ + 'stdout' => $stdout, + 'stderr' => $stderr + ]; + } + + /** + * Read new renew date from certificate file generated by Let's Encrypt + * + * @param string $domain Domain which certificate was generated for + * + * @return int + */ + private function getRenewDate(string $domain): int { + $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; + $certData = openssl_x509_parse(file_get_contents($certPath)); + $validTo = $certData['validTo_time_t'] ?? 0; + $expiryInAdvance = (60*60*24*30); // 30 days + return $validTo - $expiryInAdvance; + } + + /** + * Method to take files from Let's Encrypt, and put it into Traefik. + * + * @param string $domain Domain which certificate was generated for + * + * @return void + */ + private function applyCertificateFiles(string $domain): void { + // Prepare folder in storage for domain + $path = APP_STORAGE_CERTIFICATES . '/' . $domain; + if (!\is_readable($path)) { + if (!\mkdir($path, 0755, true)) { + throw new Exception('Failed to create path for certificate.'); + } + } + + // Move generated files from certbot into our storage + if(!@\rename('/etc/letsencrypt/live/'.$domain.'/cert.pem', APP_STORAGE_CERTIFICATES.'/'.$domain.'/cert.pem')) { + throw new Exception('Failed to rename certificate cert.pem.'); + } + + if (!@\rename('/etc/letsencrypt/live/' . $domain . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) { + throw new Exception('Failed to rename certificate chain.pem.'); + } + + if (!@\rename('/etc/letsencrypt/live/' . $domain . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) { + throw new Exception('Failed to rename certificate fullchain.pem.'); + } + + if (!@\rename('/etc/letsencrypt/live/' . $domain . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) { + throw new Exception('Failed to rename certificate privkey.pem.'); + } + + $config = + "tls:" . + " certificates:" . + " - certFile: /storage/certificates/{$domain}/fullchain.pem" . + " keyFile: /storage/certificates/{$domain}/privkey.pem"; + + // Save configuration into Traefik using our new cert files + if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain . '.yml', $config)) { + throw new Exception('Failed to save Traefik configuration.'); + } + } + + /** + * Method to make sure information about error is delivered to admnistrator. + * + * @param string $domain Domain that caused the error + * @param string $errorMessage Verbose error message + * @param int $attempt How many times it failed already + * + * @return void + */ + private function notifyError(string $domain, string $errorMessage, int $attempt): void { + // Log error into console + Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage); + + // Send mail to administratore mail + Resque::enqueue(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME, [ + 'from' => 'console', + 'project' => 'console', + 'name' => 'Appwrite Administrator', + 'recipient' => App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'), + 'url' => 'https://' . $domain, + 'locale' => App::getEnv('_APP_LOCALE', 'en'), + 'type' => MAIL_TYPE_CERTIFICATE, + + 'domain' => $domain, + 'error' => $errorMessage, + 'attempt' => $attempt + ]); + } + + /** + * Update all existing domain documents so they have relation to correct certificate document. + * This solved issues: + * - when adding a domain for which there is already a certificate + * - when renew creates new document? It might? + * - overall makes it more reliable + * + * @param string $certificateId ID of a new or updated certificate document + * @param string $domain Domain that is affected by new certificate + * + * @return void + */ + private function updateDomainDocuments(string $certificateId, string $domain): void { + $domains = $this->dbForConsole->find('domains', [ + new Query('domain', Query::TYPE_EQUAL, [$domain]) + ], 1000); + + foreach ($domains as $domainDocument) { + $domainDocument->setAttribute('updated', \time()); + $domainDocument->setAttribute('certificateId', $certificateId); + + $this->dbForConsole->updateDocument('domains', $domainDocument->getId(), $domainDocument); + $this->dbForConsole->deleteCachedDocument('projects', $domainDocument->getAttribute('projectId')); + } + } } diff --git a/composer.lock b/composer.lock index c82900e1bb..4d3b65e932 100644 --- a/composer.lock +++ b/composer.lock @@ -3550,16 +3550,16 @@ }, { "name": "matthiasmullie/minify", - "version": "1.3.67", + "version": "1.3.68", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9" + "reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", - "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297", + "reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297", "shasum": "" }, "require": { @@ -3608,7 +3608,7 @@ ], "support": { "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.67" + "source": "https://github.com/matthiasmullie/minify/tree/1.3.68" }, "funding": [ { @@ -3616,7 +3616,7 @@ "type": "github" } ], - "time": "2022-03-24T08:54:59+00:00" + "time": "2022-04-19T08:28:56+00:00" }, { "name": "matthiasmullie/path-converter", @@ -5710,16 +5710,16 @@ }, { "name": "symfony/console", - "version": "v6.0.7", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e" + "reference": "0d00aa289215353aa8746a31d101f8e60826285c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e", - "reference": "70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e", + "url": "https://api.github.com/repos/symfony/console/zipball/0d00aa289215353aa8746a31d101f8e60826285c", + "reference": "0d00aa289215353aa8746a31d101f8e60826285c", "shasum": "" }, "require": { @@ -5785,7 +5785,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.7" + "source": "https://github.com/symfony/console/tree/v6.0.8" }, "funding": [ { @@ -5801,7 +5801,7 @@ "type": "tidelift" } ], - "time": "2022-03-31T17:18:25+00:00" + "time": "2022-04-20T15:01:42+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -6135,16 +6135,16 @@ }, { "name": "symfony/string", - "version": "v6.0.3", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2" + "reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/522144f0c4c004c80d56fa47e40e17028e2eefc2", - "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2", + "url": "https://api.github.com/repos/symfony/string/zipball/ac0aa5c2282e0de624c175b68d13f2c8f2e2649d", + "reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d", "shasum": "" }, "require": { @@ -6200,7 +6200,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.0.3" + "source": "https://github.com/symfony/string/tree/v6.0.8" }, "funding": [ { @@ -6216,7 +6216,7 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2022-04-22T08:18:02+00:00" }, { "name": "textalk/websocket", From 9a3b226781c435c98f72c1a995d7d2d38bbc8931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 11 May 2022 14:09:30 +0000 Subject: [PATCH 75/87] PR review changes --- app/workers/certificates.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 0b6a45f0c3..f1df74972c 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -23,7 +23,7 @@ class CertificatesV1 extends Worker * * @var Database */ - private $dbForConsole; + private Database $dbForConsole; public function getName(): string { return "certificates"; @@ -103,7 +103,7 @@ class CertificatesV1 extends Worker } // Generate certificate files using Let's Encrypt - $letsEncryptData = $this->letsEncryptAction($domain->get(), $email); + $letsEncryptData = $this->issueCertificate($domain->get(), $email); // Command succeeded, store all data into document // We store stderr too, because it may include warnings @@ -135,7 +135,7 @@ class CertificatesV1 extends Worker // Save all changes we made to certificate document into database $this->saveCertificateDocument($domain->get(), $certificate); - + Authorization::reset(); } } @@ -152,7 +152,7 @@ class CertificatesV1 extends Worker * * @return void */ - private function saveCertificateDocument(string $domain, Document $certificate) { + private function saveCertificateDocument(string $domain, Document $certificate): void { // Check if update or insert required $certificateDocument = $this->dbForConsole->findOne('certificates', [ new Query('domain', Query::TYPE_EQUAL, [$domain]) ]); if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { @@ -175,15 +175,15 @@ class CertificatesV1 extends Worker */ private function getMainDomain(): ?string { if (!empty(App::getEnv('_APP_DOMAIN', ''))) { - $mainDomain = App::getEnv('_APP_DOMAIN', ''); + return App::getEnv('_APP_DOMAIN', ''); } else { $domainDocument = $this->dbForConsole->findOne('domains', [], 0, ['_id'], ['ASC']); if($domainDocument) { - $mainDomain = $domainDocument->getAttribute('domain'); + return $domainDocument->getAttribute('domain'); } } - return $mainDomain; + return null; } /** @@ -262,7 +262,7 @@ class CertificatesV1 extends Worker * * @return array Named array with keys 'stdout' and 'stderr', both string */ - private function letsEncryptAction(string $domain, string $email): array { + private function issueCertificate(string $domain, string $email): array { $staging = (App::isProduction()) ? '' : ' --dry-run'; $stdout = ''; From cc8ffad56a91dbdb3ef7df68fb240c0ab1a42054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 11 May 2022 14:13:02 +0000 Subject: [PATCH 76/87] Fix lockfile --- composer.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/composer.lock b/composer.lock index b50ce747c3..c2b51c52d9 100644 --- a/composer.lock +++ b/composer.lock @@ -2299,25 +2299,25 @@ }, { "name": "utopia-php/image", - "version": "0.5.3", + "version": "0.5.4", "source": { "type": "git", "url": "https://github.com/utopia-php/image.git", - "reference": "4a8429b62dcf56562b038d6712375f75166f0c02" + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/image/zipball/4a8429b62dcf56562b038d6712375f75166f0c02", - "reference": "4a8429b62dcf56562b038d6712375f75166f0c02", + "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", "shasum": "" }, "require": { "ext-imagick": "*", - "php": ">=7.4" + "php": ">=8.0" }, "require-dev": { "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" + "vimeo/psalm": "4.13.1" }, "type": "library", "autoload": { @@ -2345,9 +2345,9 @@ ], "support": { "issues": "https://github.com/utopia-php/image/issues", - "source": "https://github.com/utopia-php/image/tree/0.5.3" + "source": "https://github.com/utopia-php/image/tree/0.5.4" }, - "time": "2021-11-02T05:47:16+00:00" + "time": "2022-05-11T12:30:41+00:00" }, { "name": "utopia-php/locale", @@ -3551,16 +3551,16 @@ }, { "name": "matthiasmullie/minify", - "version": "1.3.67", + "version": "1.3.68", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9" + "reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", - "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297", + "reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297", "shasum": "" }, "require": { @@ -3609,7 +3609,7 @@ ], "support": { "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.67" + "source": "https://github.com/matthiasmullie/minify/tree/1.3.68" }, "funding": [ { @@ -3617,7 +3617,7 @@ "type": "github" } ], - "time": "2022-03-24T08:54:59+00:00" + "time": "2022-04-19T08:28:56+00:00" }, { "name": "matthiasmullie/path-converter", From f0bb300bd63c48f20747ef4eb48e0d539319029e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 12 May 2022 10:31:42 +0000 Subject: [PATCH 77/87] Import fix --- app/workers/certificates.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index f1df74972c..d07a678f98 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -5,6 +5,7 @@ use Appwrite\Network\Validator\CNAME; use Appwrite\Resque\Worker; use Utopia\App; use Utopia\CLI\Console; +use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; From 1c2fdab9eaaec050e91150925897a5c79c5c4d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 12 May 2022 10:38:48 +0000 Subject: [PATCH 78/87] Bug fix --- app/workers/certificates.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index d07a678f98..7a4719aec9 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -397,7 +397,10 @@ class CertificatesV1 extends Worker $domainDocument->setAttribute('certificateId', $certificateId); $this->dbForConsole->updateDocument('domains', $domainDocument->getId(), $domainDocument); - $this->dbForConsole->deleteCachedDocument('projects', $domainDocument->getAttribute('projectId')); + + if($domainDocument->getAttribute('projectId')) { + $this->dbForConsole->deleteCachedDocument('projects', $domainDocument->getAttribute('projectId')); + } } } } From 3efb90415177bb89ff400cfdce27461904032a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 12 May 2022 10:56:25 +0000 Subject: [PATCH 79/87] Prevent unnecessary SSL jobs --- app/controllers/general.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 540c087dae..9bc1d789ee 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -99,13 +99,13 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons ]); $domainDocument = $dbForConsole->createDocument('domains', $domainDocument); - } - Console::info('Issuing a TLS certificate for the main domain (' . $domain->get() . ') in a few seconds...'); - - Resque::enqueue(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, [ - 'domain' => $domain->get() - ]); + Console::info('Issuing a TLS certificate for the main domain (' . $domain->get() . ') in a few seconds...'); + + Resque::enqueue(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, [ + 'domain' => $domain->get() + ]); + } } $domains[$domain->get()] = true; From 55d934381e27c3a5f83f16b486db14facb23b4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 12 May 2022 11:40:32 +0000 Subject: [PATCH 80/87] Fix Traefik config file text --- app/workers/certificates.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 7a4719aec9..d8d5a98707 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -334,11 +334,12 @@ class CertificatesV1 extends Worker throw new Exception('Failed to rename certificate privkey.pem.'); } - $config = - "tls:" . - " certificates:" . - " - certFile: /storage/certificates/{$domain}/fullchain.pem" . - " keyFile: /storage/certificates/{$domain}/privkey.pem"; + $config = \implode('\n', [ + "tls:", + " certificates:", + " - certFile: /storage/certificates/{$domain}/fullchain.pem", + " keyFile: /storage/certificates/{$domain}/privkey.pem" + ]); // Save configuration into Traefik using our new cert files if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain . '.yml', $config)) { From 417502338a23be0cbfa9f53903368d04355fffaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 12 May 2022 11:50:56 +0000 Subject: [PATCH 81/87] Proper line break symbol --- app/workers/certificates.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index d8d5a98707..9d4f8ac4e9 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -334,7 +334,7 @@ class CertificatesV1 extends Worker throw new Exception('Failed to rename certificate privkey.pem.'); } - $config = \implode('\n', [ + $config = \implode(PHP_EOL, [ "tls:", " certificates:", " - certFile: /storage/certificates/{$domain}/fullchain.pem", From a1e0390ae77554fe4284c4cff3b9d37ef4ce86b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 12 May 2022 12:08:50 +0000 Subject: [PATCH 82/87] Imrpove var names, logs --- app/config/locale/translations/en.json | 4 ++-- app/tasks/ssl.php | 2 +- app/workers/certificates.php | 31 ++++++++++++++------------ 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/app/config/locale/translations/en.json b/app/config/locale/translations/en.json index e533d82795..ab765fe47e 100644 --- a/app/config/locale/translations/en.json +++ b/app/config/locale/translations/en.json @@ -29,8 +29,8 @@ "emails.invitation.signature": "{{project}} team", "emails.certificate.subject": "Certificate failure for %s", "emails.certificate.hello": "Hello", - "emails.certificate.body": "Certificate for your domain '{{domain}}' could not be renewed. This is attempt no. {{attempt}}, and the failure was caused by: {{error}}", - "emails.certificate.footer": "Certificate will still be valid for 30 days since first failure. We highly recommend investigating the case, otherwise your domain will end up without a secure certificate.", + "emails.certificate.body": "Certificate for your domain '{{domain}}' could not be generated. This is attempt no. {{attempt}}, and the failure was caused by: {{error}}", + "emails.certificate.footer": "Your previous certificate willl be valid for 30 days since the first failure. We highly recommend investigating this case, otherwise your domain will end up without a valid SSL communication.", "emails.certificate.thanks": "Thanks", "emails.certificate.signature": "{{project}} team", "locale.country.unknown": "Unknown", diff --git a/app/tasks/ssl.php b/app/tasks/ssl.php index f28c0b8532..b6dae6f9f7 100644 --- a/app/tasks/ssl.php +++ b/app/tasks/ssl.php @@ -17,6 +17,6 @@ $cli // Scheduje a job Resque::enqueue(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, [ 'domain' => $domain, - 'skipRenewCheck' => true + 'skipCheck' => true ]); }); \ No newline at end of file diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 9d4f8ac4e9..502d4498ec 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -74,7 +74,7 @@ class CertificatesV1 extends Worker try { // Read arguments $domain = $this->args['domain']; // String of domain (hostname) - $skipRenewCheck = $this->args['skipRenewCheck'] ?? false; // If true, we won't double-check expiry from cert file + $skipCheck = $this->args['skipCheck'] ?? false; // If true, we won't double-check expiry from cert file $domain = new Domain((!empty($domain)) ? $domain : ''); @@ -93,13 +93,15 @@ class CertificatesV1 extends Worker throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate.'); } - $mainDomain = $this->getMainDomain(); - $isMainDomain = !isset($mainDomain) || $domain->get() === $mainDomain; - $this->validateDomain($domain, $isMainDomain); + // Validate domain and DNS records. Skip if job is forced + if(!$skipCheck) { + $mainDomain = $this->getMainDomain(); + $isMainDomain = !isset($mainDomain) || $domain->get() === $mainDomain; + $this->validateDomain($domain, $isMainDomain); + } - // If certificate exists already, double-check expiry date - // If asked to skip, we won't - if(!$skipRenewCheck && !$this->isRenewRequired($domain->get())) { + // If certificate exists already, double-check expiry date. Skip if job is forced + if(!$skipCheck && !$this->isRenewRequired($domain->get())) { throw new Exception('Renew isn\'t required.'); } @@ -114,7 +116,7 @@ class CertificatesV1 extends Worker ])); // Give certificates to Traefik - $this->applyCertificateFiles($domain->get()); + $this->applyCertificateFiles($domain->get(), $letsEncryptData); // Update certificate info stored in database $certificate->setAttribute('renewDate', $this->getRenewDate($domain->get())); @@ -305,10 +307,11 @@ class CertificatesV1 extends Worker * Method to take files from Let's Encrypt, and put it into Traefik. * * @param string $domain Domain which certificate was generated for + * @param array $letsEncryptData Let's Encrypt logs to use for additional info when throwing error * * @return void */ - private function applyCertificateFiles(string $domain): void { + private function applyCertificateFiles(string $domain, array $letsEncryptData): void { // Prepare folder in storage for domain $path = APP_STORAGE_CERTIFICATES . '/' . $domain; if (!\is_readable($path)) { @@ -318,20 +321,20 @@ class CertificatesV1 extends Worker } // Move generated files from certbot into our storage - if(!@\rename('/etc/letsencrypt/live/'.$domain.'/cert.pem', APP_STORAGE_CERTIFICATES.'/'.$domain.'/cert.pem')) { - throw new Exception('Failed to rename certificate cert.pem.'); + if(!@\rename('/etc/letsencrypt/live/'.$domain.'/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) { + throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr']); } if (!@\rename('/etc/letsencrypt/live/' . $domain . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) { - throw new Exception('Failed to rename certificate chain.pem.'); + throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr']); } if (!@\rename('/etc/letsencrypt/live/' . $domain . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) { - throw new Exception('Failed to rename certificate fullchain.pem.'); + throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr']); } if (!@\rename('/etc/letsencrypt/live/' . $domain . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) { - throw new Exception('Failed to rename certificate privkey.pem.'); + throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr']); } $config = \implode(PHP_EOL, [ From 762c1cfe730666542b0c7c79b9a87c8b5cd72784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 12 May 2022 12:13:47 +0000 Subject: [PATCH 83/87] Improve logs --- app/tasks/ssl.php | 2 +- app/workers/certificates.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/tasks/ssl.php b/app/tasks/ssl.php index b6dae6f9f7..743be92a96 100644 --- a/app/tasks/ssl.php +++ b/app/tasks/ssl.php @@ -12,7 +12,7 @@ $cli ->desc('Validate server certificates') ->param('domain', App::getEnv('_APP_DOMAIN', ''), new Hostname(), 'Domain to generate certificate for. If empty, main domain will be used.', true) ->action(function ($domain) { - Console::success('Scheduling a job to issue a TLS certificate for domain:' . $domain); + Console::success('Scheduling a job to issue a TLS certificate for domain: ' . $domain); // Scheduje a job Resque::enqueue(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, [ diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 502d4498ec..c52c93a540 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -322,19 +322,19 @@ class CertificatesV1 extends Worker // Move generated files from certbot into our storage if(!@\rename('/etc/letsencrypt/live/'.$domain.'/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) { - throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr']); + throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . '; ' . $letsEncryptData['stdout']); } if (!@\rename('/etc/letsencrypt/live/' . $domain . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) { - throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr']); + throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . '; ' . $letsEncryptData['stdout']); } if (!@\rename('/etc/letsencrypt/live/' . $domain . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) { - throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr']); + throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . '; ' . $letsEncryptData['stdout']); } if (!@\rename('/etc/letsencrypt/live/' . $domain . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) { - throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr']); + throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . '; ' . $letsEncryptData['stdout']); } $config = \implode(PHP_EOL, [ From 88813296cf4ad5a232ba48cd57bc04c7ecb3a9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 12 May 2022 12:14:59 +0000 Subject: [PATCH 84/87] Verbose log improvement --- app/workers/certificates.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index c52c93a540..f932e4b8f8 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -322,19 +322,19 @@ class CertificatesV1 extends Worker // Move generated files from certbot into our storage if(!@\rename('/etc/letsencrypt/live/'.$domain.'/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) { - throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . '; ' . $letsEncryptData['stdout']); + throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); } if (!@\rename('/etc/letsencrypt/live/' . $domain . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) { - throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . '; ' . $letsEncryptData['stdout']); + throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); } if (!@\rename('/etc/letsencrypt/live/' . $domain . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) { - throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . '; ' . $letsEncryptData['stdout']); + throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); } if (!@\rename('/etc/letsencrypt/live/' . $domain . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) { - throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . '; ' . $letsEncryptData['stdout']); + throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); } $config = \implode(PHP_EOL, [ From 613fee0fee658084fcda1a5164397041e556578f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 12 May 2022 12:32:13 +0000 Subject: [PATCH 85/87] Empty commit for CI/CD From 05c37c8a28b976c756d90a473ecc1252992faeac Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 12 May 2022 17:56:20 +0200 Subject: [PATCH 86/87] fix: improve typing and nullish values --- src/Appwrite/Auth/OAuth2.php | 20 +++--- src/Appwrite/Auth/OAuth2/Amazon.php | 58 ++++++--------- src/Appwrite/Auth/OAuth2/Apple.php | 66 ++++++++--------- src/Appwrite/Auth/OAuth2/Auth0.php | 81 +++++++++------------ src/Appwrite/Auth/OAuth2/Bitbucket.php | 59 ++++++---------- src/Appwrite/Auth/OAuth2/Bitly.php | 54 ++++++-------- src/Appwrite/Auth/OAuth2/Box.php | 50 +++++-------- src/Appwrite/Auth/OAuth2/Discord.php | 30 +++----- src/Appwrite/Auth/OAuth2/Dropbox.php | 56 ++++++--------- src/Appwrite/Auth/OAuth2/Facebook.php | 61 +++++++--------- src/Appwrite/Auth/OAuth2/Github.php | 67 +++++++----------- src/Appwrite/Auth/OAuth2/Gitlab.php | 40 +++++------ src/Appwrite/Auth/OAuth2/Google.php | 42 ++++------- src/Appwrite/Auth/OAuth2/Linkedin.php | 75 ++++++++------------ src/Appwrite/Auth/OAuth2/Microsoft.php | 58 +++++++-------- src/Appwrite/Auth/OAuth2/Mock.php | 76 +++++++++----------- src/Appwrite/Auth/OAuth2/Notion.php | 66 +++++++---------- src/Appwrite/Auth/OAuth2/Okta.php | 82 ++++++++++------------ src/Appwrite/Auth/OAuth2/Paypal.php | 48 ++++++------- src/Appwrite/Auth/OAuth2/PaypalSandbox.php | 2 +- src/Appwrite/Auth/OAuth2/Salesforce.php | 60 +++++++--------- src/Appwrite/Auth/OAuth2/Slack.php | 59 ++++++---------- src/Appwrite/Auth/OAuth2/Spotify.php | 58 ++++++--------- src/Appwrite/Auth/OAuth2/Stripe.php | 79 +++++++++------------ src/Appwrite/Auth/OAuth2/Tradeshift.php | 30 ++++---- src/Appwrite/Auth/OAuth2/TradeshiftBox.php | 2 +- src/Appwrite/Auth/OAuth2/Twitch.php | 62 +++++++--------- src/Appwrite/Auth/OAuth2/WordPress.php | 60 +++++++--------- src/Appwrite/Auth/OAuth2/Yahoo.php | 58 ++++++--------- src/Appwrite/Auth/OAuth2/Yammer.php | 50 +++++-------- src/Appwrite/Auth/OAuth2/Yandex.php | 56 ++++++--------- src/Appwrite/Auth/OAuth2/Zoom.php | 53 +++++++------- 32 files changed, 712 insertions(+), 1006 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2.php b/src/Appwrite/Auth/OAuth2.php index 10a81d7b6c..99b40019ac 100644 --- a/src/Appwrite/Auth/OAuth2.php +++ b/src/Appwrite/Auth/OAuth2.php @@ -7,27 +7,27 @@ abstract class OAuth2 /** * @var string */ - protected $appID; + protected string $appID; /** * @var string */ - protected $appSecret; + protected string $appSecret; /** * @var string */ - protected $callback; + protected string $callback; /** * @var array */ - protected $state; + protected array $state; /** * @var array */ - protected $scopes; + protected array $scopes; /** * OAuth2 constructor. @@ -74,7 +74,7 @@ abstract class OAuth2 abstract public function refreshTokens(string $refreshToken): array; /** - * @param $accessToken + * @param string $accessToken * * @return string */ @@ -83,14 +83,14 @@ abstract class OAuth2 /** * Check if the OAuth email is verified * - * @param $accessToken + * @param string $accessToken * * @return bool */ abstract public function isEmailVerified(string $accessToken): bool; /** - * @param $accessToken + * @param string $accessToken * * @return string */ @@ -107,6 +107,7 @@ abstract class OAuth2 if (!\in_array($scope, $this->scopes)) { $this->scopes[] = $scope; } + return $this; } @@ -126,6 +127,7 @@ abstract class OAuth2 public function getAccessToken(string $code): string { $tokens = $this->getTokens($code); + return $tokens['access_token'] ?? ''; } @@ -137,6 +139,7 @@ abstract class OAuth2 public function getRefreshToken(string $code): string { $tokens = $this->getTokens($code); + return $tokens['refresh_token'] ?? ''; } @@ -148,6 +151,7 @@ abstract class OAuth2 public function getAccessTokenExpiry(string $code): string { $tokens = $this->getTokens($code); + return $tokens['expires_in'] ?? ''; } diff --git a/src/Appwrite/Auth/OAuth2/Amazon.php b/src/Appwrite/Auth/OAuth2/Amazon.php index edace5df6b..2e72975fd1 100644 --- a/src/Appwrite/Auth/OAuth2/Amazon.php +++ b/src/Appwrite/Auth/OAuth2/Amazon.php @@ -14,17 +14,17 @@ class Amazon extends OAuth2 /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ "profile" ]; @@ -37,7 +37,7 @@ class Amazon extends OAuth2 } /** - * @param $state + * @param string $state * * @return array */ @@ -52,13 +52,13 @@ class Amazon extends OAuth2 */ public function getLoginURL(): string { - return 'https://www.amazon.com/ap/oa?'.\http_build_query([ - 'response_type' => 'code', - 'client_id' => $this->appID, - 'scope' => \implode(' ', $this->getScopes()), - 'state' => \json_encode($this->state), - 'redirect_uri' => $this->callback - ]); + return 'https://www.amazon.com/ap/oa?' . \http_build_query([ + 'response_type' => 'code', + 'client_id' => $this->appID, + 'scope' => \implode(' ', $this->getScopes()), + 'state' => \json_encode($this->state), + 'redirect_uri' => $this->callback + ]); } /** @@ -68,7 +68,7 @@ class Amazon extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8']; $this->tokens = \json_decode($this->request( 'POST', @@ -92,7 +92,7 @@ class Amazon extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8']; $this->tokens = \json_decode($this->request( @@ -107,7 +107,7 @@ class Amazon extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -123,11 +123,7 @@ class Amazon extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['user_id'])) { - return $user['user_id']; - } - - return ''; + return $user['user_id'] ?? ''; } /** @@ -139,11 +135,7 @@ class Amazon extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** @@ -151,7 +143,7 @@ class Amazon extends OAuth2 * * If present, the email is verified. This was verfied through a manual Amazon sign up process * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -171,11 +163,7 @@ class Amazon extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + return $user['name'] ?? ''; } /** @@ -186,7 +174,7 @@ class Amazon extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $user = $this->request('GET', 'https://api.amazon.com/user/profile?access_token='.\urlencode($accessToken)); + $user = $this->request('GET', 'https://api.amazon.com/user/profile?access_token=' . \urlencode($accessToken)); $this->user = \json_decode($user, true); } return $this->user; diff --git a/src/Appwrite/Auth/OAuth2/Apple.php b/src/Appwrite/Auth/OAuth2/Apple.php index 7c6436f3cb..1ea352e685 100644 --- a/src/Appwrite/Auth/OAuth2/Apple.php +++ b/src/Appwrite/Auth/OAuth2/Apple.php @@ -13,17 +13,17 @@ class Apple extends OAuth2 /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ "name", "email" ]; @@ -31,7 +31,7 @@ class Apple extends OAuth2 /** * @var array */ - protected $claims = []; + protected array $claims = []; /** * @return string @@ -40,13 +40,13 @@ class Apple extends OAuth2 { return 'apple'; } - + /** * @return string */ public function getLoginURL(): string { - return 'https://appleid.apple.com/auth/authorize?'.\http_build_query([ + return 'https://appleid.apple.com/auth/authorize?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, 'state' => \json_encode($this->state), @@ -63,7 +63,7 @@ class Apple extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( 'POST', @@ -90,7 +90,7 @@ class Apple extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( @@ -105,7 +105,7 @@ class Apple extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -122,11 +122,7 @@ class Apple extends OAuth2 */ public function getUserID(string $accessToken): string { - if (isset($this->claims['sub']) && !empty($this->claims['sub'])) { - return $this->claims['sub']; - } - - return ''; + return $this->claims['sub'] ?? ''; } /** @@ -136,11 +132,7 @@ class Apple extends OAuth2 */ public function getUserEmail(string $accessToken): string { - if (isset($this->claims['email']) && !empty($this->claims['email'])) { - return $this->claims['email']; - } - - return ''; + return $this->claims['email'] ?? ''; } /** @@ -148,13 +140,13 @@ class Apple extends OAuth2 * * @link https://developer.apple.com/forums/thread/121411 * - * @param $accessToken + * @param string $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { - if (isset($this->claims['email_verified']) && $this->claims['email_verified'] === 'true') { + if ($this->claims['email_verified'] ?? false) { return true; } @@ -168,17 +160,19 @@ class Apple extends OAuth2 */ public function getUserName(string $accessToken): string { - if (isset($this->claims['email']) && + if ( + isset($this->claims['email']) && !empty($this->claims['email']) && isset($this->claims['email_verified']) && - $this->claims['email_verified'] === 'true') { + $this->claims['email_verified'] === 'true' + ) { return $this->claims['email']; } return ''; } - protected function getAppSecret():string + protected function getAppSecret(): string { try { $secret = \json_decode($this->appSecret, true); @@ -195,18 +189,18 @@ class Apple extends OAuth2 'alg' => 'ES256', 'kid' => $keyID, ]; - + $claims = [ 'iss' => $teamID, 'iat' => \time(), - 'exp' => \time() + 86400*180, + 'exp' => \time() + 86400 * 180, 'aud' => 'https://appleid.apple.com', 'sub' => $bundleID, ]; $pkey = \openssl_pkey_get_private($keyfile); - $payload = $this->encode(\json_encode($headers)).'.'.$this->encode(\json_encode($claims)); + $payload = $this->encode(\json_encode($headers)) . '.' . $this->encode(\json_encode($claims)); $signature = ''; @@ -216,7 +210,7 @@ class Apple extends OAuth2 return ''; } - return $payload.'.'.$this->encode($this->fromDER($signature, 64)); + return $payload . '.' . $this->encode($this->fromDER($signature, 64)); } /** @@ -245,10 +239,10 @@ class Apple extends OAuth2 * @param string $der * @param int $partLength */ - protected function fromDER(string $der, int $partLength):string + protected function fromDER(string $der, int $partLength): string { $hex = \unpack('H*', $der)[1]; - + if ('30' !== \mb_substr($hex, 0, 2, '8bit')) { // SEQUENCE throw new \RuntimeException(); } @@ -267,7 +261,7 @@ class Apple extends OAuth2 $R = \str_pad($R, $partLength, '0', STR_PAD_LEFT); $hex = \mb_substr($hex, 4 + $Rl * 2, null, '8bit'); - + if ('02' !== \mb_substr($hex, 0, 2, '8bit')) { // INTEGER throw new \RuntimeException(); } @@ -276,6 +270,6 @@ class Apple extends OAuth2 $S = $this->retrievePositiveInteger(\mb_substr($hex, 4, $Sl * 2, '8bit')); $S = \str_pad($S, $partLength, '0', STR_PAD_LEFT); - return \pack('H*', $R.$S); + return \pack('H*', $R . $S); } } diff --git a/src/Appwrite/Auth/OAuth2/Auth0.php b/src/Appwrite/Auth/OAuth2/Auth0.php index ed16b04c08..4775139a0b 100644 --- a/src/Appwrite/Auth/OAuth2/Auth0.php +++ b/src/Appwrite/Auth/OAuth2/Auth0.php @@ -8,27 +8,27 @@ use Appwrite\Auth\OAuth2; // https://auth0.com/docs/api/authentication class Auth0 extends OAuth2 -{ - /** +{ + /** * @var array */ - protected $scopes = [ + protected array $scopes = [ 'openid', 'profile', 'email', 'offline_access' ]; - + /** * @var array */ - protected $user = []; - + protected array $user = []; + /** * @var array */ - protected $tokens = []; - + protected array $tokens = []; + /** * @return string */ @@ -42,11 +42,11 @@ class Auth0 extends OAuth2 */ public function getLoginURL(): string { - return 'https://'.$this->getAuth0Domain().'/authorize?'.\http_build_query([ + return 'https://' . $this->getAuth0Domain() . '/authorize?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, - 'state'=> \json_encode($this->state), - 'scope'=> \implode(' ', $this->getScopes()), + 'state' => \json_encode($this->state), + 'scope' => \implode(' ', $this->getScopes()), 'response_type' => 'code' ]); } @@ -58,11 +58,11 @@ class Auth0 extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( 'POST', - 'https://'.$this->getAuth0Domain().'/oauth/token', + 'https://' . $this->getAuth0Domain() . '/oauth/token', $headers, \http_build_query([ 'code' => $code, @@ -77,8 +77,8 @@ class Auth0 extends OAuth2 return $this->tokens; } - - + + /** * @param string $refreshToken * @@ -89,7 +89,7 @@ class Auth0 extends OAuth2 $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( 'POST', - 'https://'.$this->getAuth0Domain().'/oauth/token', + 'https://' . $this->getAuth0Domain() . '/oauth/token', $headers, \http_build_query([ 'refresh_token' => $refreshToken, @@ -99,7 +99,7 @@ class Auth0 extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -114,12 +114,8 @@ class Auth0 extends OAuth2 public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - - if (isset($user['sub'])) { - return $user['sub']; - } - - return ''; + + return $user['sub'] ?? ''; } /** @@ -130,12 +126,8 @@ class Auth0 extends OAuth2 public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); - - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + + return $user['email'] ?? ''; } /** @@ -143,7 +135,7 @@ class Auth0 extends OAuth2 * * @link https://auth0.com/docs/api/authentication?javascript#user-profile * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -151,7 +143,7 @@ class Auth0 extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email_verified']) && $user['email_verified'] === true) { + if ($user['email_verified'] ?? false) { return true; } @@ -166,15 +158,11 @@ class Auth0 extends OAuth2 public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + + return $user['name'] ?? ''; } - - /** + + /** * @param string $accessToken * * @return array @@ -182,8 +170,8 @@ class Auth0 extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $headers = ['Authorization: Bearer '. \urlencode($accessToken)]; - $user = $this->request('GET', 'https://'.$this->getAuth0Domain().'/userinfo', $headers); + $headers = ['Authorization: Bearer ' . \urlencode($accessToken)]; + $user = $this->request('GET', 'https://' . $this->getAuth0Domain() . '/userinfo', $headers); $this->user = \json_decode($user, true); } @@ -199,10 +187,10 @@ class Auth0 extends OAuth2 { $secret = $this->getAppSecret(); - return (isset($secret['clientSecret'])) ? $secret['clientSecret'] : ''; + return $secret['clientSecret'] ?? ''; } - /** + /** * Extracts the Auth0 Domain from the JSON stored in appSecret * * @return string @@ -210,7 +198,8 @@ class Auth0 extends OAuth2 protected function getAuth0Domain(): string { $secret = $this->getAppSecret(); - return (isset($secret['auth0Domain'])) ? $secret['auth0Domain'] : ''; + + return $secret['auth0Domain'] ?? ''; } /** @@ -219,7 +208,7 @@ class Auth0 extends OAuth2 * @return array */ protected function getAppSecret(): array - { + { try { $secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR); } catch (\Throwable $th) { @@ -227,4 +216,4 @@ class Auth0 extends OAuth2 } return $secret; } -} \ No newline at end of file +} diff --git a/src/Appwrite/Auth/OAuth2/Bitbucket.php b/src/Appwrite/Auth/OAuth2/Bitbucket.php index 36d55fc3ce..bbefe144c1 100644 --- a/src/Appwrite/Auth/OAuth2/Bitbucket.php +++ b/src/Appwrite/Auth/OAuth2/Bitbucket.php @@ -12,17 +12,17 @@ class Bitbucket extends OAuth2 /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = []; + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = []; /** * @return string @@ -37,12 +37,12 @@ class Bitbucket extends OAuth2 */ public function getLoginURL(): string { - return 'https://bitbucket.org/site/oauth2/authorize?'.\http_build_query([ - 'response_type' => 'code', - 'client_id' => $this->appID, - 'scope' => \implode(' ', $this->getScopes()), - 'state' => \json_encode($this->state), - ]); + return 'https://bitbucket.org/site/oauth2/authorize?' . \http_build_query([ + 'response_type' => 'code', + 'client_id' => $this->appID, + 'scope' => \implode(' ', $this->getScopes()), + 'state' => \json_encode($this->state), + ]); } /** @@ -52,7 +52,7 @@ class Bitbucket extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { // Required as per Bitbucket Spec. $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( @@ -76,7 +76,7 @@ class Bitbucket extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( @@ -91,7 +91,7 @@ class Bitbucket extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -107,11 +107,7 @@ class Bitbucket extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['uuid'])) { - return $user['uuid']; - } - - return ''; + return $user['uuid'] ?? ''; } /** @@ -123,17 +119,13 @@ class Bitbucket extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** * Check if the OAuth email is verified * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -141,7 +133,7 @@ class Bitbucket extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['is_confirmed']) && $user['is_confirmed'] === true) { + if ($user['is_confirmed'] ?? false) { return true; } @@ -157,11 +149,7 @@ class Bitbucket extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['display_name'])) { - return $user['display_name']; - } - - return ''; + return $user['display_name'] ?? ''; } /** @@ -172,10 +160,10 @@ class Bitbucket extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $user = $this->request('GET', 'https://api.bitbucket.org/2.0/user?access_token='.\urlencode($accessToken)); + $user = $this->request('GET', 'https://api.bitbucket.org/2.0/user?access_token=' . \urlencode($accessToken)); $this->user = \json_decode($user, true); - $emails = $this->request('GET', 'https://api.bitbucket.org/2.0/user/emails?access_token='.\urlencode($accessToken)); + $emails = $this->request('GET', 'https://api.bitbucket.org/2.0/user/emails?access_token=' . \urlencode($accessToken)); $emails = \json_decode($emails, true); if (isset($emails['values'])) { foreach ($emails['values'] as $email) { @@ -186,7 +174,6 @@ class Bitbucket extends OAuth2 } } } - } return $this->user; } diff --git a/src/Appwrite/Auth/OAuth2/Bitly.php b/src/Appwrite/Auth/OAuth2/Bitly.php index 6feec88d1e..08350bea6f 100644 --- a/src/Appwrite/Auth/OAuth2/Bitly.php +++ b/src/Appwrite/Auth/OAuth2/Bitly.php @@ -3,7 +3,6 @@ namespace Appwrite\Auth\OAuth2; use Appwrite\Auth\OAuth2; -use Utopia\Exception; // Reference Material // https://dev.bitly.com/v4_documentation.html @@ -14,32 +13,32 @@ class Bitly extends OAuth2 /** * @var string */ - private $endpoint = 'https://bitly.com/oauth/'; + private string $endpoint = 'https://bitly.com/oauth/'; /** * @var string */ - private $resourceEndpoint = 'https://api-ssl.bitly.com/'; + private string $resourceEndpoint = 'https://api-ssl.bitly.com/'; /** * @var array */ - protected $scopes = []; + protected array $scopes = []; /** * @var array */ - protected $user = []; - + protected array $user = []; + /** * @var array */ - protected $tokens = []; + protected array $tokens = []; /** * @return string */ - public function getName():string + public function getName(): string { return 'bitly'; } @@ -47,9 +46,9 @@ class Bitly extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { - return $this->endpoint . 'authorize?'. + return $this->endpoint . 'authorize?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, @@ -64,7 +63,7 @@ class Bitly extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $response = $this->request( 'POST', $this->resourceEndpoint . 'oauth/access_token', @@ -91,7 +90,7 @@ class Bitly extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $response = $this->request( 'POST', @@ -109,7 +108,7 @@ class Bitly extends OAuth2 \parse_str($response, $output); $this->tokens = $output; - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -117,27 +116,23 @@ class Bitly extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['login'])) { - return $user['login']; - } - - return ''; + return $user['login'] ?? ''; } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); @@ -157,7 +152,7 @@ class Bitly extends OAuth2 * * @link https://dev.bitly.com/api-reference#getUser * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -167,19 +162,15 @@ class Bitly extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + return $user['name'] ?? ''; } /** @@ -190,7 +181,7 @@ class Bitly extends OAuth2 protected function getUser(string $accessToken) { $headers = [ - 'Authorization: Bearer '. \urlencode($accessToken), + 'Authorization: Bearer ' . \urlencode($accessToken), "Accept: application/json" ]; @@ -198,7 +189,6 @@ class Bitly extends OAuth2 $this->user = \json_decode($this->request('GET', $this->resourceEndpoint . "v4/user", $headers), true); } - return $this->user; } } diff --git a/src/Appwrite/Auth/OAuth2/Box.php b/src/Appwrite/Auth/OAuth2/Box.php index 6e2f893496..da925eed1a 100644 --- a/src/Appwrite/Auth/OAuth2/Box.php +++ b/src/Appwrite/Auth/OAuth2/Box.php @@ -12,27 +12,27 @@ class Box extends OAuth2 /** * @var string */ - private $endpoint = 'https://account.box.com/api/oauth2/'; + private string $endpoint = 'https://account.box.com/api/oauth2/'; /** * @var string */ - private $resourceEndpoint = 'https://api.box.com/2.0/'; + private string $resourceEndpoint = 'https://api.box.com/2.0/'; /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ 'manage_app_users', ]; @@ -49,7 +49,7 @@ class Box extends OAuth2 */ public function getLoginURL(): string { - $url = $this->endpoint . 'authorize?'. + $url = $this->endpoint . 'authorize?' . \http_build_query([ 'response_type' => 'code', 'client_id' => $this->appID, @@ -68,7 +68,7 @@ class Box extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( 'POST', @@ -93,7 +93,7 @@ class Box extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( @@ -108,7 +108,7 @@ class Box extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -124,11 +124,7 @@ class Box extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** @@ -140,11 +136,7 @@ class Box extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['login'])) { - return $user['login']; - } - - return ''; + return $user['login'] ?? ''; } /** @@ -152,7 +144,7 @@ class Box extends OAuth2 * * If present, the email is verified. This was verfied through a manual Box sign up process * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -162,7 +154,7 @@ class Box extends OAuth2 return !empty($email); } - + /** * @param string $accessToken * @@ -172,11 +164,7 @@ class Box extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + return $user['name'] ?? ''; } /** @@ -187,7 +175,7 @@ class Box extends OAuth2 protected function getUser(string $accessToken): array { $header = [ - 'Authorization: Bearer '.\urlencode($accessToken), + 'Authorization: Bearer ' . \urlencode($accessToken), ]; if (empty($this->user)) { $user = $this->request( diff --git a/src/Appwrite/Auth/OAuth2/Discord.php b/src/Appwrite/Auth/OAuth2/Discord.php index 631f4a3f7e..7cf2ef1b7b 100644 --- a/src/Appwrite/Auth/OAuth2/Discord.php +++ b/src/Appwrite/Auth/OAuth2/Discord.php @@ -12,22 +12,22 @@ class Discord extends OAuth2 /** * @var string */ - private $endpoint = 'https://discordapp.com/api'; + private string $endpoint = 'https://discordapp.com/api'; /** * @var array */ - protected $user = []; + protected array $user = []; /** * @var array */ - protected $tokens = []; + protected array $tokens = []; /** * @var array */ - protected $scopes = [ + protected array $scopes = [ 'identify', 'email' ]; @@ -118,11 +118,7 @@ class Discord extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** @@ -134,11 +130,7 @@ class Discord extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** @@ -146,7 +138,7 @@ class Discord extends OAuth2 * * @link https://discord.com/developers/docs/resources/user * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -154,7 +146,7 @@ class Discord extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['verified']) && $user['verified'] === true) { + if ($user['verified'] ?? false) { return true; } @@ -170,11 +162,7 @@ class Discord extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['username'])) { - return $user['username']; - } - - return ''; + return $user['username'] ?? ''; } /** diff --git a/src/Appwrite/Auth/OAuth2/Dropbox.php b/src/Appwrite/Auth/OAuth2/Dropbox.php index 04a07f486a..5025cb3dc8 100644 --- a/src/Appwrite/Auth/OAuth2/Dropbox.php +++ b/src/Appwrite/Auth/OAuth2/Dropbox.php @@ -13,17 +13,17 @@ class Dropbox extends OAuth2 /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = []; + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = []; /** * @return string @@ -32,17 +32,17 @@ class Dropbox extends OAuth2 { return 'dropbox'; } - + /** * @return string */ public function getLoginURL(): string { - return 'https://www.dropbox.com/oauth2/authorize?'.\http_build_query([ - 'client_id' => $this->appID, - 'redirect_uri' => $this->callback, - 'state' => \json_encode($this->state), - 'response_type' => 'code' + return 'https://www.dropbox.com/oauth2/authorize?' . \http_build_query([ + 'client_id' => $this->appID, + 'redirect_uri' => $this->callback, + 'state' => \json_encode($this->state), + 'response_type' => 'code' ]); } @@ -53,7 +53,7 @@ class Dropbox extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( 'POST', @@ -77,7 +77,7 @@ class Dropbox extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( @@ -92,7 +92,7 @@ class Dropbox extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -108,11 +108,7 @@ class Dropbox extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['account_id'])) { - return $user['account_id']; - } - - return ''; + return $user['account_id'] ?? ''; } /** @@ -124,11 +120,7 @@ class Dropbox extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** @@ -136,7 +128,7 @@ class Dropbox extends OAuth2 * * @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -144,7 +136,7 @@ class Dropbox extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email_verified']) && $user['email_verified'] === true) { + if ($user['email_verified'] ?? false) { return true; } @@ -160,11 +152,7 @@ class Dropbox extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']['display_name']; - } - - return ''; + return $user['name']['display_name'] ?? ''; } /** @@ -175,7 +163,7 @@ class Dropbox extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $headers = ['Authorization: Bearer '. \urlencode($accessToken)]; + $headers = ['Authorization: Bearer ' . \urlencode($accessToken)]; $user = $this->request('POST', 'https://api.dropboxapi.com/2/users/get_current_account', $headers); $this->user = \json_decode($user, true); } diff --git a/src/Appwrite/Auth/OAuth2/Facebook.php b/src/Appwrite/Auth/OAuth2/Facebook.php index 619504207e..555daedc58 100644 --- a/src/Appwrite/Auth/OAuth2/Facebook.php +++ b/src/Appwrite/Auth/OAuth2/Facebook.php @@ -3,36 +3,35 @@ namespace Appwrite\Auth\OAuth2; use Appwrite\Auth\OAuth2; -use Utopia\Exception; class Facebook extends OAuth2 { /** * @var string */ - protected $version = 'v2.8'; + protected string $version = 'v2.8'; /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ 'email' ]; /** * @return string */ - public function getName():string + public function getName(): string { return 'facebook'; } @@ -40,10 +39,10 @@ class Facebook extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { - return 'https://www.facebook.com/'.$this->version.'/dialog/oauth?'.\http_build_query([ - 'client_id'=> $this->appID, + return 'https://www.facebook.com/' . $this->version . '/dialog/oauth?' . \http_build_query([ + 'client_id' => $this->appID, 'redirect_uri' => $this->callback, 'scope' => \implode(' ', $this->getScopes()), 'state' => \json_encode($this->state) @@ -57,7 +56,7 @@ class Facebook extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $this->tokens = \json_decode($this->request( 'GET', 'https://graph.facebook.com/' . $this->version . '/oauth/access_token?' . \http_build_query([ @@ -77,7 +76,7 @@ class Facebook extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $this->tokens = \json_decode($this->request( 'GET', @@ -90,7 +89,7 @@ class Facebook extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -102,15 +101,11 @@ class Facebook extends OAuth2 * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** @@ -118,15 +113,11 @@ class Facebook extends OAuth2 * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** @@ -134,7 +125,7 @@ class Facebook extends OAuth2 * * If present, the email is verified. This was verfied through a manual Facebook sign up process * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -150,15 +141,11 @@ class Facebook extends OAuth2 * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + return $user['name'] ?? ''; } /** @@ -166,10 +153,10 @@ class Facebook extends OAuth2 * * @return array */ - protected function getUser(string $accessToken):array + protected function getUser(string $accessToken): array { if (empty($this->user)) { - $user = $this->request('GET', 'https://graph.facebook.com/'.$this->version.'/me?fields=email,name&access_token='.\urlencode($accessToken)); + $user = $this->request('GET', 'https://graph.facebook.com/' . $this->version . '/me?fields=email,name&access_token=' . \urlencode($accessToken)); $this->user = \json_decode($user, true); } diff --git a/src/Appwrite/Auth/OAuth2/Github.php b/src/Appwrite/Auth/OAuth2/Github.php index dbed7ad818..1130134f68 100644 --- a/src/Appwrite/Auth/OAuth2/Github.php +++ b/src/Appwrite/Auth/OAuth2/Github.php @@ -3,31 +3,30 @@ namespace Appwrite\Auth\OAuth2; use Appwrite\Auth\OAuth2; -use Utopia\Exception; class Github extends OAuth2 { /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ 'user:email', ]; /** * @return string */ - public function getName():string + public function getName(): string { return 'github'; } @@ -35,9 +34,9 @@ class Github extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { - return 'https://github.com/login/oauth/authorize?'. \http_build_query([ + return 'https://github.com/login/oauth/authorize?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, 'scope' => \implode(' ', $this->getScopes()), @@ -52,7 +51,7 @@ class Github extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $response = $this->request( 'POST', 'https://github.com/login/oauth/access_token', @@ -78,7 +77,7 @@ class Github extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $response = $this->request( 'POST', @@ -96,7 +95,7 @@ class Github extends OAuth2 \parse_str($response, $output); $this->tokens = $output; - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -104,35 +103,27 @@ class Github extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** @@ -140,7 +131,7 @@ class Github extends OAuth2 * * @link https://docs.github.com/en/rest/users/emails#list-email-addresses-for-the-authenticated-user * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -148,7 +139,7 @@ class Github extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['verified']) && $user['verified'] !== null) { + if ($user['verified'] ?? false) { return true; } @@ -156,19 +147,15 @@ class Github extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + return $user['name'] ?? ''; } /** @@ -179,9 +166,9 @@ class Github extends OAuth2 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); - - $emails = $this->request('GET', 'https://api.github.com/user/emails', ['Authorization: token '.\urlencode($accessToken)]); + $this->user = \json_decode($this->request('GET', 'https://api.github.com/user', ['Authorization: token ' . \urlencode($accessToken)]), true); + + $emails = $this->request('GET', 'https://api.github.com/user/emails', ['Authorization: token ' . \urlencode($accessToken)]); $emails = \json_decode($emails, true); foreach ($emails as $email) { diff --git a/src/Appwrite/Auth/OAuth2/Gitlab.php b/src/Appwrite/Auth/OAuth2/Gitlab.php index ed0515d0ad..fa0a93df92 100644 --- a/src/Appwrite/Auth/OAuth2/Gitlab.php +++ b/src/Appwrite/Auth/OAuth2/Gitlab.php @@ -12,17 +12,17 @@ class Gitlab extends OAuth2 /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ 'read_user' ]; @@ -39,7 +39,7 @@ class Gitlab extends OAuth2 */ public function getLoginURL(): string { - return 'https://gitlab.com/oauth/authorize?'.\http_build_query([ + return 'https://gitlab.com/oauth/authorize?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, 'scope' => \implode(' ', $this->getScopes()), @@ -55,7 +55,7 @@ class Gitlab extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $this->tokens = \json_decode($this->request( 'POST', 'https://gitlab.com/oauth/token?' . \http_build_query([ @@ -76,7 +76,7 @@ class Gitlab extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $this->tokens = \json_decode($this->request( 'POST', @@ -88,7 +88,7 @@ class Gitlab extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -120,11 +120,7 @@ class Gitlab extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** @@ -132,7 +128,7 @@ class Gitlab extends OAuth2 * * @link https://docs.gitlab.com/ee/api/users.html#list-current-user-for-normal-users * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -140,7 +136,7 @@ class Gitlab extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['confirmed_at']) && $user['confirmed_at'] !== null) { + if ($user['confirmed_at'] ?? false) { return true; } @@ -156,11 +152,7 @@ class Gitlab extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + return $user['name'] ?? ''; } /** @@ -171,7 +163,7 @@ class Gitlab extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $user = $this->request('GET', 'https://gitlab.com/api/v4/user?access_token='.\urlencode($accessToken)); + $user = $this->request('GET', 'https://gitlab.com/api/v4/user?access_token=' . \urlencode($accessToken)); $this->user = \json_decode($user, true); } diff --git a/src/Appwrite/Auth/OAuth2/Google.php b/src/Appwrite/Auth/OAuth2/Google.php index 2c78e63b5e..e675a1f861 100644 --- a/src/Appwrite/Auth/OAuth2/Google.php +++ b/src/Appwrite/Auth/OAuth2/Google.php @@ -14,12 +14,12 @@ class Google extends OAuth2 /** * @var string */ - protected $version = 'v4'; + protected string $version = 'v4'; /** * @var array */ - protected $scopes = [ + protected array $scopes = [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'openid' @@ -28,12 +28,12 @@ class Google extends OAuth2 /** * @var array */ - protected $user = []; - + protected array $user = []; + /** * @var array */ - protected $tokens = []; + protected array $tokens = []; /** * @return string @@ -48,7 +48,7 @@ class Google extends OAuth2 */ public function getLoginURL(): string { - return 'https://accounts.google.com/o/oauth2/v2/auth?'. \http_build_query([ + return 'https://accounts.google.com/o/oauth2/v2/auth?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, 'scope' => \implode(' ', $this->getScopes()), @@ -64,7 +64,7 @@ class Google extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $this->tokens = \json_decode($this->request( 'POST', 'https://oauth2.googleapis.com/token?' . \http_build_query([ @@ -86,7 +86,7 @@ class Google extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $this->tokens = \json_decode($this->request( 'POST', @@ -98,7 +98,7 @@ class Google extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -114,11 +114,7 @@ class Google extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** @@ -130,11 +126,7 @@ class Google extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** @@ -142,7 +134,7 @@ class Google extends OAuth2 * * @link https://www.oauth.com/oauth2-servers/signing-in-with-google/verifying-the-user-info/ * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -150,7 +142,7 @@ class Google extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email_verified']) && $user['email_verified'] === true) { + if ($user['email_verified'] ?? false) { return true; } @@ -166,11 +158,7 @@ class Google extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + return $user['name'] ?? ''; } /** @@ -181,7 +169,7 @@ class Google extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $user = $this->request('GET', 'https://www.googleapis.com/oauth2/v3/userinfo?access_token='.\urlencode($accessToken)); + $user = $this->request('GET', 'https://www.googleapis.com/oauth2/v3/userinfo?access_token=' . \urlencode($accessToken)); $this->user = \json_decode($user, true); } diff --git a/src/Appwrite/Auth/OAuth2/Linkedin.php b/src/Appwrite/Auth/OAuth2/Linkedin.php index e13ac7869a..3ada765319 100644 --- a/src/Appwrite/Auth/OAuth2/Linkedin.php +++ b/src/Appwrite/Auth/OAuth2/Linkedin.php @@ -9,17 +9,17 @@ class Linkedin extends OAuth2 /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ 'r_liteprofile', 'r_emailaddress', ]; @@ -40,7 +40,7 @@ class Linkedin extends OAuth2 /** * @return string */ - public function getName():string + public function getName(): string { return 'linkedin'; } @@ -48,15 +48,15 @@ class Linkedin extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { - return 'https://www.linkedin.com/oauth/v2/authorization?'.\http_build_query([ - 'response_type' => 'code', - 'client_id' => $this->appID, - 'redirect_uri' => $this->callback, - 'scope' => \implode(' ', $this->getScopes()), - 'state' => \json_encode($this->state), - ]); + return 'https://www.linkedin.com/oauth/v2/authorization?' . \http_build_query([ + 'response_type' => 'code', + 'client_id' => $this->appID, + 'redirect_uri' => $this->callback, + 'scope' => \implode(' ', $this->getScopes()), + 'state' => \json_encode($this->state), + ]); } /** @@ -66,7 +66,7 @@ class Linkedin extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $this->tokens = \json_decode($this->request( 'POST', 'https://www.linkedin.com/oauth/v2/accessToken', @@ -89,7 +89,7 @@ class Linkedin extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $this->tokens = \json_decode($this->request( 'POST', @@ -104,7 +104,7 @@ class Linkedin extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -112,40 +112,27 @@ class Linkedin extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { - $email = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', ['Authorization: Bearer '.\urlencode($accessToken)]), true); + $email = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', ['Authorization: Bearer ' . \urlencode($accessToken)]), true); - if ( - isset($email['elements']) && - isset($email['elements'][0]) && - isset($email['elements'][0]['handle~']) && - isset($email['elements'][0]['handle~']['emailAddress']) - ) { - return $email['elements'][0]['handle~']['emailAddress']; - } - - return ''; + return $email['elements'][0]['handle~']['emailAddress'] ?? ''; } /** @@ -153,7 +140,7 @@ class Linkedin extends OAuth2 * * If present, the email is verified. This was verfied through a manual Linkedin sign up process * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -165,11 +152,11 @@ class Linkedin extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); $name = ''; @@ -179,7 +166,7 @@ class Linkedin extends OAuth2 } if (isset($user['localizedLastName'])) { - $name = (empty($name)) ? $user['localizedLastName'] : $name.' '.$user['localizedLastName']; + $name = (empty($name)) ? $user['localizedLastName'] : $name . ' ' . $user['localizedLastName']; } return $name; @@ -193,7 +180,7 @@ class Linkedin extends OAuth2 protected function getUser(string $accessToken) { if (empty($this->user)) { - $this->user = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/me', ['Authorization: Bearer '.\urlencode($accessToken)]), true); + $this->user = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/me', ['Authorization: Bearer ' . \urlencode($accessToken)]), true); } return $this->user; diff --git a/src/Appwrite/Auth/OAuth2/Microsoft.php b/src/Appwrite/Auth/OAuth2/Microsoft.php index 38f125336c..84f128a08e 100644 --- a/src/Appwrite/Auth/OAuth2/Microsoft.php +++ b/src/Appwrite/Auth/OAuth2/Microsoft.php @@ -13,17 +13,17 @@ class Microsoft extends OAuth2 /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ 'offline_access', 'user.read' ]; @@ -35,17 +35,17 @@ class Microsoft extends OAuth2 { return 'microsoft'; } - + /** * @return string */ public function getLoginURL(): string { - return 'https://login.microsoftonline.com/'.$this->getTenantID().'/oauth2/v2.0/authorize?'.\http_build_query([ + return 'https://login.microsoftonline.com/' . $this->getTenantID() . '/oauth2/v2.0/authorize?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, - 'state'=> \json_encode($this->state), - 'scope'=> \implode(' ', $this->getScopes()), + 'state' => \json_encode($this->state), + 'scope' => \implode(' ', $this->getScopes()), 'response_type' => 'code', 'response_mode' => 'query' ]); @@ -58,7 +58,7 @@ class Microsoft extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( 'POST', @@ -83,7 +83,7 @@ class Microsoft extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( @@ -98,7 +98,7 @@ class Microsoft extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -114,11 +114,7 @@ class Microsoft extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** @@ -130,11 +126,7 @@ class Microsoft extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['userPrincipalName'])) { - return $user['userPrincipalName']; - } - - return ''; + return $user['userPrincipalName'] ?? ''; } /** @@ -142,7 +134,7 @@ class Microsoft extends OAuth2 * * If present, the email is verified. This was verfied through a manual Microsoft sign up process * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -162,11 +154,7 @@ class Microsoft extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['displayName'])) { - return $user['displayName']; - } - - return ''; + return $user['displayName'] ?? ''; } /** @@ -177,7 +165,7 @@ class Microsoft extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $headers = ['Authorization: Bearer '. \urlencode($accessToken)]; + $headers = ['Authorization: Bearer ' . \urlencode($accessToken)]; $user = $this->request('GET', 'https://graph.microsoft.com/v1.0/me', $headers); $this->user = \json_decode($user, true); } @@ -191,7 +179,7 @@ class Microsoft extends OAuth2 * @return array */ protected function getAppSecret(): array - { + { try { $secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR); } catch (\Throwable $th) { @@ -208,7 +196,8 @@ class Microsoft extends OAuth2 protected function getClientSecret(): string { $secret = $this->getAppSecret(); - return (isset($secret['clientSecret'])) ? $secret['clientSecret'] : ''; + + return $secret['clientSecret'] ?? ''; } /** @@ -219,6 +208,7 @@ class Microsoft extends OAuth2 protected function getTenantID(): string { $secret = $this->getAppSecret(); - return (isset($secret['tenantID'])) ? $secret['tenantID'] : 'common'; + + return $secret['tenantID'] ?? 'common'; } } diff --git a/src/Appwrite/Auth/OAuth2/Mock.php b/src/Appwrite/Auth/OAuth2/Mock.php index 7e8848bdf9..f80287947e 100644 --- a/src/Appwrite/Auth/OAuth2/Mock.php +++ b/src/Appwrite/Auth/OAuth2/Mock.php @@ -10,29 +10,29 @@ class Mock extends OAuth2 /** * @var string */ - protected $version = 'v1'; + protected string $version = 'v1'; /** * @var array */ - protected $scopes = [ + protected array $scopes = [ 'email' ]; /** * @var array */ - protected $user = []; - + protected array $user = []; + /** * @var array */ - protected $tokens = []; + protected array $tokens = []; /** * @return string */ - public function getName():string + public function getName(): string { return 'mock'; } @@ -40,9 +40,9 @@ class Mock extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { - return 'http://localhost/'.$this->version.'/mock/tests/general/oauth2?'. \http_build_query([ + return 'http://localhost/' . $this->version . '/mock/tests/general/oauth2?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, 'scope' => \implode(' ', $this->getScopes()), @@ -57,16 +57,16 @@ class Mock extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $this->tokens = \json_decode($this->request( 'GET', 'http://localhost/' . $this->version . '/mock/tests/general/oauth2/token?' . - \http_build_query([ - 'client_id' => $this->appID, - 'redirect_uri' => $this->callback, - 'client_secret' => $this->appSecret, - 'code' => $code - ]) + \http_build_query([ + 'client_id' => $this->appID, + 'redirect_uri' => $this->callback, + 'client_secret' => $this->appSecret, + 'code' => $code + ]) ), true); } @@ -78,20 +78,20 @@ class Mock extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $this->tokens = \json_decode($this->request( 'GET', 'http://localhost/' . $this->version . '/mock/tests/general/oauth2/token?' . - \http_build_query([ - 'client_id' => $this->appID, - 'client_secret' => $this->appSecret, - 'refresh_token' => $refreshToken, - 'grant_type' => 'refresh_token' - ]) + \http_build_query([ + 'client_id' => $this->appID, + 'client_secret' => $this->appSecret, + 'refresh_token' => $refreshToken, + 'grant_type' => 'refresh_token' + ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -103,15 +103,11 @@ class Mock extends OAuth2 * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** @@ -119,21 +115,17 @@ class Mock extends OAuth2 * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** * Check if the OAuth email is verified * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -147,15 +139,11 @@ class Mock extends OAuth2 * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + return $user['name'] ?? ''; } /** @@ -163,10 +151,10 @@ class Mock extends OAuth2 * * @return array */ - protected function getUser(string $accessToken):array + protected function getUser(string $accessToken): array { if (empty($this->user)) { - $user = $this->request('GET', 'http://localhost/'.$this->version.'/mock/tests/general/oauth2/user?token='.\urlencode($accessToken)); + $user = $this->request('GET', 'http://localhost/' . $this->version . '/mock/tests/general/oauth2/user?token=' . \urlencode($accessToken)); $this->user = \json_decode($user, true); } diff --git a/src/Appwrite/Auth/OAuth2/Notion.php b/src/Appwrite/Auth/OAuth2/Notion.php index eee4ed6403..5d9d75bffd 100644 --- a/src/Appwrite/Auth/OAuth2/Notion.php +++ b/src/Appwrite/Auth/OAuth2/Notion.php @@ -9,32 +9,32 @@ class Notion extends OAuth2 /** * @var string */ - private $endpoint = 'https://api.notion.com/v1'; + private string $endpoint = 'https://api.notion.com/v1'; /** * @var string */ - private $version = '2021-08-16'; + private string $version = '2021-08-16'; /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = []; + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = []; /** * @return string */ - public function getName():string + public function getName(): string { return 'notion'; } @@ -42,9 +42,9 @@ class Notion extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { - return $this->endpoint . '/oauth/authorize?'. \http_build_query([ + return $this->endpoint . '/oauth/authorize?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, 'response_type' => 'code', @@ -60,7 +60,7 @@ class Notion extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)]; $this->tokens = \json_decode($this->request( 'POST', @@ -82,7 +82,7 @@ class Notion extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)]; $this->tokens = \json_decode($this->request( @@ -95,7 +95,7 @@ class Notion extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -103,35 +103,27 @@ class Notion extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $response = $this->getUser($accessToken); - if (isset($response['bot']['owner']['user']['id'])) { - return $response['bot']['owner']['user']['id']; - } - - return ''; + return $response['bot']['owner']['user']['id'] ?? ''; } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { $response = $this->getUser($accessToken); - if(isset($response['bot']['owner']['user']['person']['email'])){ - return $response['bot']['owner']['user']['person']['email']; - } - - return ''; + return $response['bot']['owner']['user']['person']['email'] ?? ''; } /** @@ -139,7 +131,7 @@ class Notion extends OAuth2 * * If present, the email is verified. This was verfied through a manual Notion sign up process * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -151,19 +143,15 @@ class Notion extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $response = $this->getUser($accessToken); - if (isset($response['bot']['owner']['user']['name'])) { - return $response['bot']['owner']['user']['name']; - } - - return ''; + return $response['bot']['owner']['user']['name'] ?? ''; } /** @@ -171,11 +159,11 @@ class Notion extends OAuth2 * * @return array */ - protected function getUser(string $accessToken) + protected function getUser(string $accessToken): array { $headers = [ 'Notion-Version: ' . $this->version, - 'Authorization: Bearer '.\urlencode($accessToken) + 'Authorization: Bearer ' . \urlencode($accessToken) ]; if (empty($this->user)) { diff --git a/src/Appwrite/Auth/OAuth2/Okta.php b/src/Appwrite/Auth/OAuth2/Okta.php index e806e04f31..3de3df96d5 100644 --- a/src/Appwrite/Auth/OAuth2/Okta.php +++ b/src/Appwrite/Auth/OAuth2/Okta.php @@ -8,27 +8,27 @@ use Appwrite\Auth\OAuth2; // https://developer.okta.com/docs/guides/sign-into-web-app-redirect/php/main/ class Okta extends OAuth2 -{ - /** +{ + /** * @var array */ - protected $scopes = [ + protected array $scopes = [ 'openid', 'profile', 'email', 'offline_access' ]; - + /** * @var array */ - protected $user = []; - + protected array $user = []; + /** * @var array */ - protected $tokens = []; - + protected array $tokens = []; + /** * @return string */ @@ -42,11 +42,11 @@ class Okta extends OAuth2 */ public function getLoginURL(): string { - return 'https://'.$this->getOktaDomain().'/oauth2/'.$this->getAuthorizationServerId().'/v1/authorize?'.\http_build_query([ + return 'https://' . $this->getOktaDomain() . '/oauth2/' . $this->getAuthorizationServerId() . '/v1/authorize?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, - 'state'=> \json_encode($this->state), - 'scope'=> \implode(' ', $this->getScopes()), + 'state' => \json_encode($this->state), + 'scope' => \implode(' ', $this->getScopes()), 'response_type' => 'code' ]); } @@ -58,11 +58,11 @@ class Okta extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( 'POST', - 'https://'.$this->getOktaDomain().'/oauth2/'.$this->getAuthorizationServerId().'/v1/token', + 'https://' . $this->getOktaDomain() . '/oauth2/' . $this->getAuthorizationServerId() . '/v1/token', $headers, \http_build_query([ 'code' => $code, @@ -77,8 +77,8 @@ class Okta extends OAuth2 return $this->tokens; } - - + + /** * @param string $refreshToken * @@ -89,7 +89,7 @@ class Okta extends OAuth2 $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( 'POST', - 'https://'.$this->getOktaDomain().'/oauth2/'.$this->getAuthorizationServerId().'/v1/token', + 'https://' . $this->getOktaDomain() . '/oauth2/' . $this->getAuthorizationServerId() . '/v1/token', $headers, \http_build_query([ 'refresh_token' => $refreshToken, @@ -99,7 +99,7 @@ class Okta extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -114,12 +114,8 @@ class Okta extends OAuth2 public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - - if (isset($user['sub'])) { - return $user['sub']; - } - - return ''; + + return $user['sub'] ?? ''; } /** @@ -130,12 +126,8 @@ class Okta extends OAuth2 public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); - - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + + return $user['email'] ?? ''; } /** @@ -143,7 +135,7 @@ class Okta extends OAuth2 * * @link https://developer.okta.com/docs/reference/api/oidc/#userinfo * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -151,7 +143,7 @@ class Okta extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email_verified']) && $user['email_verified'] === true) { + if ($user['email_verified'] ?? false) { return true; } @@ -166,15 +158,11 @@ class Okta extends OAuth2 public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + + return $user['name'] ?? ''; } - - /** + + /** * @param string $accessToken * * @return array @@ -182,8 +170,8 @@ class Okta extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $headers = ['Authorization: Bearer '. \urlencode($accessToken)]; - $user = $this->request('GET', 'https://'.$this->getOktaDomain().'/oauth2/'.$this->getAuthorizationServerId().'/v1/userinfo', $headers); + $headers = ['Authorization: Bearer ' . \urlencode($accessToken)]; + $user = $this->request('GET', 'https://' . $this->getOktaDomain() . '/oauth2/' . $this->getAuthorizationServerId() . '/v1/userinfo', $headers); $this->user = \json_decode($user, true); } @@ -199,10 +187,10 @@ class Okta extends OAuth2 { $secret = $this->getAppSecret(); - return (isset($secret['clientSecret'])) ? $secret['clientSecret'] : ''; + return $secret['clientSecret'] ?? ''; } - /** + /** * Extracts the Okta Domain from the JSON stored in appSecret * * @return string @@ -210,7 +198,8 @@ class Okta extends OAuth2 protected function getOktaDomain(): string { $secret = $this->getAppSecret(); - return (isset($secret['oktaDomain'])) ? $secret['oktaDomain'] : ''; + + return $secret['oktaDomain'] ?? ''; } /** @@ -221,7 +210,8 @@ class Okta extends OAuth2 protected function getAuthorizationServerId(): string { $secret = $this->getAppSecret(); - return (isset($secret['authorizationServerId'])) ? $secret['authorizationServerId'] : 'default'; + + return $secret['authorizationServerId'] ?? 'default'; } /** @@ -230,7 +220,7 @@ class Okta extends OAuth2 * @return array */ protected function getAppSecret(): array - { + { try { $secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR); } catch (\Throwable $th) { diff --git a/src/Appwrite/Auth/OAuth2/Paypal.php b/src/Appwrite/Auth/OAuth2/Paypal.php index d517c8c685..74f4291594 100644 --- a/src/Appwrite/Auth/OAuth2/Paypal.php +++ b/src/Appwrite/Auth/OAuth2/Paypal.php @@ -12,7 +12,7 @@ class Paypal extends OAuth2 /** * @var array */ - private $endpoint = [ + private array $endpoint = [ 'sandbox' => 'https://www.sandbox.paypal.com/', 'live' => 'https://www.paypal.com/', ]; @@ -20,7 +20,7 @@ class Paypal extends OAuth2 /** * @var array */ - private $resourceEndpoint = [ + private array $resourceEndpoint = [ 'sandbox' => 'https://api.sandbox.paypal.com/v1/', 'live' => 'https://api.paypal.com/v1/', ]; @@ -28,22 +28,22 @@ class Paypal extends OAuth2 /** * @var string */ - protected $environment = 'live'; + protected string $environment = 'live'; /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ 'openid', 'profile', 'email' @@ -62,7 +62,7 @@ class Paypal extends OAuth2 */ public function getLoginURL(): string { - $url = $this->endpoint[$this->environment] . 'connect/?'. + $url = $this->endpoint[$this->environment] . 'connect/?' . \http_build_query([ 'flowEntry' => 'static', 'response_type' => 'code', @@ -83,7 +83,7 @@ class Paypal extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $this->tokens = \json_decode($this->request( 'POST', $this->resourceEndpoint[$this->environment] . 'oauth2/token', @@ -103,7 +103,7 @@ class Paypal extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $this->tokens = \json_decode($this->request( 'POST', @@ -115,7 +115,7 @@ class Paypal extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -131,11 +131,7 @@ class Paypal extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['payer_id'])) { - return $user['payer_id']; - } - - return ''; + return $user['payer_id'] ?? ''; } /** @@ -151,7 +147,7 @@ class Paypal extends OAuth2 $email = array_filter($user['emails'], function ($email) { return $email['primary'] === true; }); - + if (!empty($email)) { return $email[0]['value']; } @@ -165,7 +161,7 @@ class Paypal extends OAuth2 * * @link https://developer.paypal.com/docs/api/identity/v1/#userinfo_get * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -173,7 +169,7 @@ class Paypal extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['verified_account']) && $user['verified_account'] === true) { + if ($user['verified_account'] ?? false) { return true; } @@ -189,11 +185,7 @@ class Paypal extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + return $user['name'] ?? ''; } /** @@ -205,7 +197,7 @@ class Paypal extends OAuth2 { $header = [ 'Content-Type: application/json', - 'Authorization: Bearer '.\urlencode($accessToken), + 'Authorization: Bearer ' . \urlencode($accessToken), ]; if (empty($this->user)) { $user = $this->request( diff --git a/src/Appwrite/Auth/OAuth2/PaypalSandbox.php b/src/Appwrite/Auth/OAuth2/PaypalSandbox.php index 82698555d4..0f56a09c21 100644 --- a/src/Appwrite/Auth/OAuth2/PaypalSandbox.php +++ b/src/Appwrite/Auth/OAuth2/PaypalSandbox.php @@ -6,7 +6,7 @@ use Appwrite\Auth\OAuth2\Paypal; class PaypalSandbox extends Paypal { - protected $environment = 'sandbox'; + protected string $environment = 'sandbox'; /** * @return string diff --git a/src/Appwrite/Auth/OAuth2/Salesforce.php b/src/Appwrite/Auth/OAuth2/Salesforce.php index 3181657235..636c3f4c3c 100644 --- a/src/Appwrite/Auth/OAuth2/Salesforce.php +++ b/src/Appwrite/Auth/OAuth2/Salesforce.php @@ -14,17 +14,17 @@ class Salesforce extends OAuth2 /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ "openid" ]; @@ -37,7 +37,7 @@ class Salesforce extends OAuth2 } /** - * @param $state + * @param string $state * * @return array */ @@ -52,13 +52,13 @@ class Salesforce extends OAuth2 */ public function getLoginURL(): string { - return 'https://login.salesforce.com/services/oauth2/authorize?'.\http_build_query([ - 'response_type' => 'code', - 'client_id' => $this->appID, - 'redirect_uri'=> $this->callback, - 'scope'=> \implode(' ', $this->getScopes()), - 'state' => \json_encode($this->state) - ]); + return 'https://login.salesforce.com/services/oauth2/authorize?' . \http_build_query([ + 'response_type' => 'code', + 'client_id' => $this->appID, + 'redirect_uri' => $this->callback, + 'scope' => \implode(' ', $this->getScopes()), + 'state' => \json_encode($this->state) + ]); } /** @@ -68,7 +68,7 @@ class Salesforce extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = [ 'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret), 'Content-Type: application/x-www-form-urlencoded', @@ -93,7 +93,7 @@ class Salesforce extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = [ 'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret), @@ -109,7 +109,7 @@ class Salesforce extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -125,11 +125,7 @@ class Salesforce extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['user_id'])) { - return $user['user_id']; - } - - return ''; + return $user['user_id'] ?? ''; } /** @@ -141,11 +137,7 @@ class Salesforce extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** @@ -153,7 +145,7 @@ class Salesforce extends OAuth2 * * @link https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5 * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -161,7 +153,7 @@ class Salesforce extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email_verified']) && $user['email_verified'] === true) { + if ($user['email_verified'] ?? false) { return true; } @@ -177,11 +169,7 @@ class Salesforce extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + return $user['name'] ?? ''; } /** @@ -192,7 +180,7 @@ class Salesforce extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $user = $this->request('GET', 'https://login.salesforce.com/services/oauth2/userinfo?access_token='.\urlencode($accessToken)); + $user = $this->request('GET', 'https://login.salesforce.com/services/oauth2/userinfo?access_token=' . \urlencode($accessToken)); $this->user = \json_decode($user, true); } return $this->user; diff --git a/src/Appwrite/Auth/OAuth2/Slack.php b/src/Appwrite/Auth/OAuth2/Slack.php index f1c5b6af9c..c8adfdd697 100644 --- a/src/Appwrite/Auth/OAuth2/Slack.php +++ b/src/Appwrite/Auth/OAuth2/Slack.php @@ -3,24 +3,23 @@ namespace Appwrite\Auth\OAuth2; use Appwrite\Auth\OAuth2; -use Utopia\Exception; class Slack extends OAuth2 { /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ 'identity.avatar', 'identity.basic', 'identity.email', @@ -30,7 +29,7 @@ class Slack extends OAuth2 /** * @return string */ - public function getName():string + public function getName(): string { return 'slack'; } @@ -38,11 +37,11 @@ class Slack extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { // https://api.slack.com/docs/oauth#step_1_-_sending_users_to_authorize_and_or_install - return 'https://slack.com/oauth/authorize?'.\http_build_query([ - 'client_id'=> $this->appID, + return 'https://slack.com/oauth/authorize?' . \http_build_query([ + 'client_id' => $this->appID, 'scope' => \implode(' ', $this->getScopes()), 'redirect_uri' => $this->callback, 'state' => \json_encode($this->state) @@ -56,7 +55,7 @@ class Slack extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { // https://api.slack.com/docs/oauth#step_3_-_exchanging_a_verification_code_for_an_access_token $this->tokens = \json_decode($this->request( 'GET', @@ -77,7 +76,7 @@ class Slack extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $this->tokens = \json_decode($this->request( 'GET', @@ -89,7 +88,7 @@ class Slack extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -101,15 +100,11 @@ class Slack extends OAuth2 * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['user']['id'])) { - return $user['user']['id']; - } - - return ''; + return $user['user']['id'] ?? ''; } /** @@ -117,15 +112,11 @@ class Slack extends OAuth2 * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['user']['email'])) { - return $user['user']['email']; - } - - return ''; + return $user['user']['email'] ?? ''; } /** @@ -135,7 +126,7 @@ class Slack extends OAuth2 * * @link https://slack.com/help/articles/207262907-Change-your-email-address * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -151,15 +142,11 @@ class Slack extends OAuth2 * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['user']['name'])) { - return $user['user']['name']; - } - - return ''; + return $user['user']['name'] ?? ''; } /** @@ -169,12 +156,12 @@ class Slack extends OAuth2 * * @return array */ - protected function getUser(string $accessToken):array + protected function getUser(string $accessToken): array { if (empty($this->user)) { $user = $this->request( 'GET', - 'https://slack.com/api/users.identity?token='.\urlencode($accessToken) + 'https://slack.com/api/users.identity?token=' . \urlencode($accessToken) ); $this->user = \json_decode($user, true); diff --git a/src/Appwrite/Auth/OAuth2/Spotify.php b/src/Appwrite/Auth/OAuth2/Spotify.php index 9c541cf8ee..98f4226d84 100644 --- a/src/Appwrite/Auth/OAuth2/Spotify.php +++ b/src/Appwrite/Auth/OAuth2/Spotify.php @@ -13,34 +13,34 @@ class Spotify extends OAuth2 /** * @var string */ - private $endpoint = 'https://accounts.spotify.com/'; + private string $endpoint = 'https://accounts.spotify.com/'; /** * @var string */ - private $resourceEndpoint = 'https://api.spotify.com/v1/'; + private string $resourceEndpoint = 'https://api.spotify.com/v1/'; /** * @var array */ - protected $scopes = [ + protected array $scopes = [ 'user-read-email', ]; /** * @var array */ - protected $user = []; - + protected array $user = []; + /** * @var array */ - protected $tokens = []; + protected array $tokens = []; /** * @return string */ - public function getName():string + public function getName(): string { return 'spotify'; } @@ -48,9 +48,9 @@ class Spotify extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { - return $this->endpoint . 'authorize?'. + return $this->endpoint . 'authorize?' . \http_build_query([ 'response_type' => 'code', 'client_id' => $this->appID, @@ -67,7 +67,7 @@ class Spotify extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)]; $this->tokens = \json_decode($this->request( 'POST', @@ -89,7 +89,7 @@ class Spotify extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)]; $this->tokens = \json_decode($this->request( @@ -102,7 +102,7 @@ class Spotify extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -110,35 +110,27 @@ class Spotify extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** @@ -148,7 +140,7 @@ class Spotify extends OAuth2 * * @link https://developer.spotify.com/documentation/web-api/reference/#/operations/get-current-users-profile * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -158,19 +150,15 @@ class Spotify extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['display_name'])) { - return $user['display_name']; - } - - return ''; + return $user['display_name'] ?? ''; } /** @@ -184,7 +172,7 @@ class Spotify extends OAuth2 $this->user = \json_decode($this->request( 'GET', $this->resourceEndpoint . 'me', - ['Authorization: Bearer '.\urlencode($accessToken)] + ['Authorization: Bearer ' . \urlencode($accessToken)] ), true); } diff --git a/src/Appwrite/Auth/OAuth2/Stripe.php b/src/Appwrite/Auth/OAuth2/Stripe.php index b34937a53e..e2ca3a92ba 100644 --- a/src/Appwrite/Auth/OAuth2/Stripe.php +++ b/src/Appwrite/Auth/OAuth2/Stripe.php @@ -10,38 +10,37 @@ class Stripe extends OAuth2 /** * @var array */ - protected $user = []; - + protected array $user = []; + /** * @var array */ - protected $tokens = []; + protected array $tokens = []; /** * @var string */ - protected $stripeAccountId = ''; + protected string $stripeAccountId = ''; /** * @var array */ - protected $scopes = [ + protected array $scopes = [ 'read_write', ]; - /** - * @return string + /** + * @var array */ - - protected $grantType = [ - 'authorize' => 'authorization_code', - 'refresh' => 'refresh_token', + protected array $grantType = [ + 'authorize' => 'authorization_code', + 'refresh' => 'refresh_token', ]; /** * @return string */ - public function getName():string + public function getName(): string { return 'stripe'; } @@ -49,9 +48,9 @@ class Stripe extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { - return 'https://connect.stripe.com/oauth/authorize?'. \http_build_query([ + return 'https://connect.stripe.com/oauth/authorize?' . \http_build_query([ 'response_type' => 'code', // The only option at the moment is "code." 'client_id' => $this->appID, 'redirect_uri' => $this->callback, @@ -67,7 +66,7 @@ class Stripe extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $this->tokens = \json_decode($this->request( 'POST', 'https://connect.stripe.com/oauth/token', @@ -89,7 +88,7 @@ class Stripe extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $this->tokens = \json_decode($this->request( 'POST', @@ -101,7 +100,7 @@ class Stripe extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -110,32 +109,28 @@ class Stripe extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); - - if(empty($user)) { - return ''; + + if (empty($user)) { + return ''; } return $user['email'] ?? ''; @@ -146,7 +141,7 @@ class Stripe extends OAuth2 * * If present, the email is verified. This was verfied through a manual Stripe sign up process * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -158,19 +153,15 @@ class Stripe extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + return $user['name'] ?? ''; } /** @@ -182,15 +173,13 @@ class Stripe extends OAuth2 { if (empty($this->user) && !empty($this->stripeAccountId)) { $this->user = \json_decode( - $this->request( - 'GET', - 'https://api.stripe.com/v1/accounts/' . $this->stripeAccountId, - ['Authorization: Bearer '.\urlencode($accessToken)] - ), - true + $this->request( + 'GET', + 'https://api.stripe.com/v1/accounts/' . $this->stripeAccountId, + ['Authorization: Bearer ' . \urlencode($accessToken)] + ), + true ); - - } return $this->user; diff --git a/src/Appwrite/Auth/OAuth2/Tradeshift.php b/src/Appwrite/Auth/OAuth2/Tradeshift.php index 142216ce26..8fdcde5f29 100644 --- a/src/Appwrite/Auth/OAuth2/Tradeshift.php +++ b/src/Appwrite/Auth/OAuth2/Tradeshift.php @@ -12,35 +12,37 @@ class Tradeshift extends OAuth2 const TRADESHIFT_SANDBOX_API_DOMAIN = 'api-sandbox.tradeshift.com'; const TRADESHIFT_API_DOMAIN = 'api.tradeshift.com'; - private $apiDomain = [ + private array $apiDomain = [ 'sandbox' => self::TRADESHIFT_SANDBOX_API_DOMAIN, 'live' => self::TRADESHIFT_API_DOMAIN, ]; - private $endpoint = [ + private array $endpoint = [ 'sandbox' => 'https://' . self::TRADESHIFT_SANDBOX_API_DOMAIN . '/tradeshift/', 'live' => 'https://' . self::TRADESHIFT_API_DOMAIN . '/tradeshift/', ]; - private $resourceEndpoint = [ + private array $resourceEndpoint = [ 'sandbox' => 'https://' . self::TRADESHIFT_SANDBOX_API_DOMAIN . '/tradeshift/rest/external/', 'live' => 'https://' . self::TRADESHIFT_API_DOMAIN . '/tradeshift/rest/external/', ]; - protected $environment = 'live'; + protected string $environment = 'live'; /** * @var array */ - protected $user = []; - + protected array $user = []; + /** * @var array */ - protected $tokens = []; + protected array $tokens = []; - - protected $scopes = [ + /** + * @var array + */ + protected array$scopes = [ 'openid', 'offline', ]; @@ -78,7 +80,7 @@ class Tradeshift extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $this->tokens = \json_decode($this->request( 'POST', $this->endpoint[$this->environment] . 'auth/token', @@ -98,7 +100,7 @@ class Tradeshift extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $this->tokens = \json_decode($this->request( 'POST', @@ -109,8 +111,8 @@ class Tradeshift extends OAuth2 'refresh_token' => $refreshToken, ]) ), true); - - if(empty($this->tokens['refresh_token'])) { + + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -146,7 +148,7 @@ class Tradeshift extends OAuth2 * * If present, the email is verified. This was verfied through a manual Tradeshift sign up process * - * @param $accessToken + * @param string $accessToken * * @return bool */ diff --git a/src/Appwrite/Auth/OAuth2/TradeshiftBox.php b/src/Appwrite/Auth/OAuth2/TradeshiftBox.php index 6ba3c29f0a..27a4c0a456 100644 --- a/src/Appwrite/Auth/OAuth2/TradeshiftBox.php +++ b/src/Appwrite/Auth/OAuth2/TradeshiftBox.php @@ -6,7 +6,7 @@ use Appwrite\Auth\OAuth2\Tradeshift; class TradeshiftBox extends Tradeshift { - protected $environment = 'sandbox'; + protected string $environment = 'sandbox'; /** * @return string diff --git a/src/Appwrite/Auth/OAuth2/Twitch.php b/src/Appwrite/Auth/OAuth2/Twitch.php index acb8587ced..04e542ffb9 100644 --- a/src/Appwrite/Auth/OAuth2/Twitch.php +++ b/src/Appwrite/Auth/OAuth2/Twitch.php @@ -13,34 +13,34 @@ class Twitch extends OAuth2 /** * @var string */ - private $endpoint = 'https://id.twitch.tv/oauth2/'; + private string $endpoint = 'https://id.twitch.tv/oauth2/'; /** * @var string */ - private $resourceEndpoint = 'https://api.twitch.tv/helix/users'; + private string $resourceEndpoint = 'https://api.twitch.tv/helix/users'; /** * @var array */ - protected $scopes = [ + protected array $scopes = [ 'user:read:email', ]; /** * @var array */ - protected $user = []; - + protected array $user = []; + /** * @var array */ - protected $tokens = []; + protected array $tokens = []; /** * @return string */ - public function getName():string + public function getName(): string { return 'twitch'; } @@ -48,9 +48,9 @@ class Twitch extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { - return $this->endpoint . 'authorize?'. + return $this->endpoint . 'authorize?' . \http_build_query([ 'response_type' => 'code', 'client_id' => $this->appID, @@ -68,7 +68,7 @@ class Twitch extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $this->tokens = \json_decode($this->request( 'POST', $this->endpoint . 'token?' . \http_build_query([ @@ -89,7 +89,7 @@ class Twitch extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $this->tokens = \json_decode($this->request( 'POST', @@ -101,7 +101,7 @@ class Twitch extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -109,35 +109,27 @@ class Twitch extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** @@ -147,31 +139,27 @@ class Twitch extends OAuth2 * * @link https://dev.twitch.tv/docs/api/reference#get-users * - * @param $accessToken + * @param string $accessToken * * @return bool */ public function isEmailVerified(string $accessToken): bool { $email = $this->getUserEmail($accessToken); - + return !empty($email); } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['display_name'])) { - return $user['display_name']; - } - - return ''; + return $user['display_name'] ?? ''; } /** @@ -186,8 +174,8 @@ class Twitch extends OAuth2 'GET', $this->resourceEndpoint, [ - 'Authorization: Bearer '.\urlencode($accessToken), - 'Client-Id: '. \urlencode($this->appID) + 'Authorization: Bearer ' . \urlencode($accessToken), + 'Client-Id: ' . \urlencode($this->appID) ] ), true); diff --git a/src/Appwrite/Auth/OAuth2/WordPress.php b/src/Appwrite/Auth/OAuth2/WordPress.php index 03c1802834..6c1aade1de 100644 --- a/src/Appwrite/Auth/OAuth2/WordPress.php +++ b/src/Appwrite/Auth/OAuth2/WordPress.php @@ -12,24 +12,24 @@ class WordPress extends OAuth2 /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ 'auth', ]; /** * @return string */ - public function getName():string + public function getName(): string { return 'wordpress'; } @@ -37,9 +37,9 @@ class WordPress extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { - return 'https://public-api.wordpress.com/oauth2/authorize?'. \http_build_query([ + return 'https://public-api.wordpress.com/oauth2/authorize?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, 'response_type' => 'code', @@ -55,7 +55,7 @@ class WordPress extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $this->tokens = \json_decode($this->request( 'POST', 'https://public-api.wordpress.com/oauth2/token', @@ -78,7 +78,7 @@ class WordPress extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $this->tokens = \json_decode($this->request( 'POST', @@ -92,7 +92,7 @@ class WordPress extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -100,32 +100,28 @@ class WordPress extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['ID'])) { - return $user['ID']; - } - - return ''; + return $user['ID'] ?? ''; } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['email']) && $user['verified']) { - return $user['email']; + if ($user['verified']) { + return $user['email'] ?? ''; } return ''; @@ -136,7 +132,7 @@ class WordPress extends OAuth2 * * @link https://developer.wordpress.com/docs/api/1.1/get/me/ * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -144,7 +140,7 @@ class WordPress extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email_verified']) && $user['email_verified'] === true) { + if ($user['email_verified'] ?? false) { return true; } @@ -152,19 +148,15 @@ class WordPress extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['username'])) { - return $user['username']; - } - - return ''; + return $user['username'] ?? ''; } /** @@ -175,7 +167,7 @@ class WordPress extends OAuth2 protected function getUser(string $accessToken) { if (empty($this->user)) { - $this->user = \json_decode($this->request('GET', 'https://public-api.wordpress.com/rest/v1/me', ['Authorization: Bearer '.$accessToken]), true); + $this->user = \json_decode($this->request('GET', 'https://public-api.wordpress.com/rest/v1/me', ['Authorization: Bearer ' . $accessToken]), true); } return $this->user; diff --git a/src/Appwrite/Auth/OAuth2/Yahoo.php b/src/Appwrite/Auth/OAuth2/Yahoo.php index 90e797568f..d8abbbfd69 100644 --- a/src/Appwrite/Auth/OAuth2/Yahoo.php +++ b/src/Appwrite/Auth/OAuth2/Yahoo.php @@ -13,17 +13,17 @@ class Yahoo extends OAuth2 /** * @var string */ - private $endpoint = 'https://api.login.yahoo.com/oauth2/'; + private string $endpoint = 'https://api.login.yahoo.com/oauth2/'; /** * @var string */ - private $resourceEndpoint = 'https://api.login.yahoo.com/openid/v1/userinfo'; + private string $resourceEndpoint = 'https://api.login.yahoo.com/openid/v1/userinfo'; /** * @var array */ - protected $scopes = [ + protected array $scopes = [ 'sdct-r', 'sdpp-w', ]; @@ -31,17 +31,17 @@ class Yahoo extends OAuth2 /** * @var array */ - protected $user = []; - + protected array $user = []; + /** * @var array */ - protected $tokens = []; + protected array $tokens = []; /** * @return string */ - public function getName():string + public function getName(): string { return 'yahoo'; } @@ -60,9 +60,9 @@ class Yahoo extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { - return $this->endpoint . 'request_auth?'. + return $this->endpoint . 'request_auth?' . \http_build_query([ 'response_type' => 'code', 'client_id' => $this->appID, @@ -79,7 +79,7 @@ class Yahoo extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = [ 'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret), 'Content-Type: application/x-www-form-urlencoded', @@ -105,7 +105,7 @@ class Yahoo extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = [ 'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret), @@ -122,7 +122,7 @@ class Yahoo extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -130,35 +130,27 @@ class Yahoo extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['sub'])) { - return $user['sub']; - } - - return ''; + return $user['sub'] ?? ''; } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** @@ -166,7 +158,7 @@ class Yahoo extends OAuth2 * * If present, the email is verified. This was verfied through a manual Yahoo sign up process * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -178,19 +170,15 @@ class Yahoo extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - if (isset($user['name'])) { - return $user['name']; - } - - return ''; + return $user['name'] ?? ''; } /** @@ -204,7 +192,7 @@ class Yahoo extends OAuth2 $this->user = \json_decode($this->request( 'GET', $this->resourceEndpoint, - ['Authorization: Bearer '.\urlencode($accessToken)] + ['Authorization: Bearer ' . \urlencode($accessToken)] ), true); } diff --git a/src/Appwrite/Auth/OAuth2/Yammer.php b/src/Appwrite/Auth/OAuth2/Yammer.php index 9ec9b56eea..80bd44b244 100644 --- a/src/Appwrite/Auth/OAuth2/Yammer.php +++ b/src/Appwrite/Auth/OAuth2/Yammer.php @@ -12,17 +12,17 @@ class Yammer extends OAuth2 /** * @var string */ - private $endpoint = 'https://www.yammer.com/oauth2/'; + private string $endpoint = 'https://www.yammer.com/oauth2/'; /** * @var array */ - protected $user = []; - + protected array $user = []; + /** * @var array */ - protected $tokens = []; + protected array $tokens = []; /** * @return string @@ -37,13 +37,13 @@ class Yammer extends OAuth2 */ public function getLoginURL(): string { - return $this->endpoint . 'oauth2/authorize?'. - \http_build_query([ - 'client_id' => $this->appID, - 'response_type' => 'code', - 'redirect_uri' => $this->callback, - 'state' => \json_encode($this->state) - ]); + return $this->endpoint . 'oauth2/authorize?' . + \http_build_query([ + 'client_id' => $this->appID, + 'response_type' => 'code', + 'redirect_uri' => $this->callback, + 'state' => \json_encode($this->state) + ]); } /** @@ -53,7 +53,7 @@ class Yammer extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( 'POST', @@ -76,7 +76,7 @@ class Yammer extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( @@ -91,7 +91,7 @@ class Yammer extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -107,11 +107,7 @@ class Yammer extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** @@ -123,11 +119,7 @@ class Yammer extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['email'])) { - return $user['email']; - } - - return ''; + return $user['email'] ?? ''; } /** @@ -135,7 +127,7 @@ class Yammer extends OAuth2 * * If present, the email is verified. This was verfied through a manual Yammer sign up process * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -155,11 +147,7 @@ class Yammer extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['full_name'])) { - return $user['full_name']; - } - - return ''; + return $user['full_name'] ?? ''; } /** @@ -170,7 +158,7 @@ class Yammer extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $headers = ['Authorization: Bearer '. \urlencode($accessToken)]; + $headers = ['Authorization: Bearer ' . \urlencode($accessToken)]; $user = $this->request('GET', 'https://www.yammer.com/api/v1/users/current.json', $headers); $this->user = \json_decode($user, true); } diff --git a/src/Appwrite/Auth/OAuth2/Yandex.php b/src/Appwrite/Auth/OAuth2/Yandex.php index 385127ca74..efab408275 100644 --- a/src/Appwrite/Auth/OAuth2/Yandex.php +++ b/src/Appwrite/Auth/OAuth2/Yandex.php @@ -14,17 +14,17 @@ class Yandex extends OAuth2 /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = []; + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = []; /** * @return string @@ -35,7 +35,7 @@ class Yandex extends OAuth2 } /** - * @param $state + * @param string $state * * @return array */ @@ -50,12 +50,12 @@ class Yandex extends OAuth2 */ public function getLoginURL(): string { - return 'https://oauth.yandex.com/authorize?'.\http_build_query([ - 'response_type' => 'code', - 'client_id' => $this->appID, - 'scope'=> \implode(' ', $this->getScopes()), - 'state' => \json_encode($this->state) - ]); + return 'https://oauth.yandex.com/authorize?' . \http_build_query([ + 'response_type' => 'code', + 'client_id' => $this->appID, + 'scope' => \implode(' ', $this->getScopes()), + 'state' => \json_encode($this->state) + ]); } /** @@ -65,7 +65,7 @@ class Yandex extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = [ 'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret), 'Content-Type: application/x-www-form-urlencoded', @@ -89,7 +89,7 @@ class Yandex extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = [ 'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret), @@ -105,7 +105,7 @@ class Yandex extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -121,11 +121,7 @@ class Yandex extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['id'])) { - return $user['id']; - } - - return ''; + return $user['id'] ?? ''; } /** @@ -137,17 +133,13 @@ class Yandex extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['default_email'])) { - return $user['default_email']; - } - - return ''; + return $user['default_email'] ?? ''; } /** * Check if the OAuth email is verified * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -165,11 +157,7 @@ class Yandex extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['display_name'])) { - return $user['display_name']; - } - - return ''; + return $user['display_name'] ?? ''; } /** @@ -180,7 +168,7 @@ class Yandex extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $user = $this->request('GET', 'https://login.yandex.ru/info?'.\http_build_query([ + $user = $this->request('GET', 'https://login.yandex.ru/info?' . \http_build_query([ 'format' => 'json', 'oauth_token' => $accessToken ])); diff --git a/src/Appwrite/Auth/OAuth2/Zoom.php b/src/Appwrite/Auth/OAuth2/Zoom.php index a36c3e44c1..ee4185934c 100644 --- a/src/Appwrite/Auth/OAuth2/Zoom.php +++ b/src/Appwrite/Auth/OAuth2/Zoom.php @@ -9,34 +9,34 @@ class Zoom extends OAuth2 /** * @var string */ - private $endpoint = 'https://zoom.us'; + private string $endpoint = 'https://zoom.us'; /** * @var string */ - private $version = '2022-03-26'; + private string $version = '2022-03-26'; /** * @var array */ - protected $user = []; - - /** - * @var array - */ - protected $tokens = []; + protected array $user = []; /** * @var array */ - protected $scopes = [ + protected array $tokens = []; + + /** + * @var array + */ + protected array $scopes = [ 'user_profile' ]; /** * @return string */ - public function getName():string + public function getName(): string { return 'zoom'; } @@ -44,9 +44,9 @@ class Zoom extends OAuth2 /** * @return string */ - public function getLoginURL():string + public function getLoginURL(): string { - return $this->endpoint . '/oauth/authorize?'. \http_build_query([ + return $this->endpoint . '/oauth/authorize?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, 'response_type' => 'code', @@ -62,7 +62,7 @@ class Zoom extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret), 'Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( 'POST', @@ -84,7 +84,7 @@ class Zoom extends OAuth2 * * @return array */ - public function refreshTokens(string $refreshToken):array + public function refreshTokens(string $refreshToken): array { $headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret), 'Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( @@ -97,7 +97,7 @@ class Zoom extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -105,24 +105,26 @@ class Zoom extends OAuth2 } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserID(string $accessToken):string + public function getUserID(string $accessToken): string { $response = $this->getUser($accessToken); + return $response['id'] ?? ''; } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserEmail(string $accessToken):string + public function getUserEmail(string $accessToken): string { $response = $this->getUser($accessToken); + return $response['email'] ?? ''; } @@ -131,7 +133,7 @@ class Zoom extends OAuth2 * * @link https://marketplace.zoom.us/docs/api-reference/zoom-api/methods/#operation/user * - * @param $accessToken + * @param string $accessToken * * @return bool */ @@ -139,21 +141,22 @@ class Zoom extends OAuth2 { $user = $this->getUser($accessToken); - if (isset($user['verified']) && $user['verified'] === 1) { + if (($user['verified'] ?? false) === 1) { return true; } - + return false; } /** - * @param $accessToken + * @param string $accessToken * * @return string */ - public function getUserName(string $accessToken):string + public function getUserName(string $accessToken): string { $response = $this->getUser($accessToken); + return ($response['first_name'] ?? '') . ' ' . ($response['last_name'] ?? ''); } @@ -165,7 +168,7 @@ class Zoom extends OAuth2 protected function getUser(string $accessToken) { $headers = [ - 'Authorization: Bearer '.\urlencode($accessToken) + 'Authorization: Bearer ' . \urlencode($accessToken) ]; if (empty($this->user)) { From 47ef1c9038be1983070b5916d57310c6dde0cc78 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 12 May 2022 18:25:36 +0200 Subject: [PATCH 87/87] whitespace fixes --- app/controllers/api/account.php | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 13f391b290..8b9ceebadb 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -167,7 +167,10 @@ App::post('/v1/account/sessions') $email = \strtolower($email); $protocol = $request->getProtocol(); - $profile = $dbForProject->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address + $profile = $dbForProject->findOne('users', [ + new Query('deleted', Query::TYPE_EQUAL, [false]), + new Query('email', Query::TYPE_EQUAL, [$email])] + ); if (!$profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) { $audits @@ -479,14 +482,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $isVerified = $oauth2->isEmailVerified($accessToken); if ($isVerified === true) { - $user = $dbForProject->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address + // Get user by email address + $user = $dbForProject->findOne('users', [ + new Query('deleted', Query::TYPE_EQUAL, [false]), + new Query('email', Query::TYPE_EQUAL, [$email])] + ); } - + if ($user === false || $user->isEmpty()) { // Last option -> create the user, generate random password $limit = $project->getAttribute('auths', [])['limit'] ?? 0; if ($limit !== 0) { - $total = $dbForProject->count('users', [ new Query('deleted', Query::TYPE_EQUAL, [false]),], APP_LIMIT_USERS); + $total = $dbForProject->count('users', [new Query('deleted', Query::TYPE_EQUAL, [false])], APP_LIMIT_USERS); if ($total >= $limit) { throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED); @@ -1883,7 +1890,11 @@ App::post('/v1/account/recovery') $isAppUser = Auth::isAppUser($roles); $email = \strtolower($email); - $profile = $dbForProject->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address + + $profile = $dbForProject->findOne('users', [ + new Query('deleted', Query::TYPE_EQUAL, [false]), + new Query('email', Query::TYPE_EQUAL, [$email]) + ]); if (!$profile) { throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);