From ebe3d05bb582fd1e46dd158835058775a56b737d Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 7 Jul 2020 10:14:24 +0300 Subject: [PATCH 01/12] Fixed paypal auto loading --- src/Appwrite/Auth/OAuth2/PaypalSandbox.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/PaypalSandbox.php b/src/Appwrite/Auth/OAuth2/PaypalSandbox.php index 7ca628f6bb..8c4baee6ba 100644 --- a/src/Appwrite/Auth/OAuth2/PaypalSandbox.php +++ b/src/Appwrite/Auth/OAuth2/PaypalSandbox.php @@ -7,7 +7,7 @@ use Appwrite\Auth\OAuth2\Paypal; class PaypalSandbox extends Paypal { - protected environment = 'sandbox'; + protected $environment = 'sandbox'; /** * @return string From bce2412b70a69e2e800baf98172fc537816f5b0b Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 8 Jul 2020 01:11:02 +0300 Subject: [PATCH 02/12] Custom domain validation regex is invalid Fixes #470 --- app/controllers/api/projects.php | 2 +- app/views/console/settings/index.phtml | 2 +- src/Appwrite/Network/Validator/Domain.php | 54 ++++++++++++++++++++ tests/unit/Network/Validators/DomianTest.php | 46 +++++++++++++++++ 4 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 src/Appwrite/Network/Validator/Domain.php create mode 100644 tests/unit/Network/Validators/DomianTest.php diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index bd49422398..cf81df66ba 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -6,7 +6,6 @@ use Utopia\Exception; use Utopia\Response; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; -use Utopia\Validator\Domain as DomainValidator; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; use Utopia\Validator\URL; @@ -19,6 +18,7 @@ use Appwrite\Database\Document; use Appwrite\Database\Validator\UID; use Appwrite\OpenSSL\OpenSSL; use Appwrite\Network\Validator\CNAME; +use Appwrite\Network\Validator\Domain as DomainValidator; use Cron\CronExpression; $scopes = include __DIR__.'/../../../app/config/scopes.php'; diff --git a/app/views/console/settings/index.phtml b/app/views/console/settings/index.phtml index a16812a63e..332f8faf6c 100644 --- a/app/views/console/settings/index.phtml +++ b/app/views/console/settings/index.phtml @@ -361,7 +361,7 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false); - +
diff --git a/src/Appwrite/Network/Validator/Domain.php b/src/Appwrite/Network/Validator/Domain.php new file mode 100644 index 0000000000..848e338d60 --- /dev/null +++ b/src/Appwrite/Network/Validator/Domain.php @@ -0,0 +1,54 @@ +domain = new Domain(); + } + + public function tearDown() + { + $this->domain = null; + } + + public function testIsValid() + { + // Assertions + $this->assertEquals(true, $this->domain->isValid('example.com')); + $this->assertEquals(true, $this->domain->isValid('subdomain.example.com')); + $this->assertEquals(true, $this->domain->isValid('subdomain.example-app.com')); + $this->assertEquals(true, $this->domain->isValid('subdomain.example_app.com')); + $this->assertEquals(true, $this->domain->isValid('subdomain-new.example.com')); + $this->assertEquals(true, $this->domain->isValid('subdomain_new.example.com')); + $this->assertEquals(true, $this->domain->isValid('localhost')); + $this->assertEquals(true, $this->domain->isValid('appwrite.io')); + $this->assertEquals(true, $this->domain->isValid('appwrite.org')); + $this->assertEquals(true, $this->domain->isValid('appwrite.org')); + $this->assertEquals(false, $this->domain->isValid(false)); + $this->assertEquals(false, $this->domain->isValid('.')); + $this->assertEquals(false, $this->domain->isValid('..')); + $this->assertEquals(false, $this->domain->isValid('')); + $this->assertEquals(false, $this->domain->isValid(['string', 'string'])); + $this->assertEquals(false, $this->domain->isValid(1)); + $this->assertEquals(false, $this->domain->isValid(1.2)); + } +} \ No newline at end of file From b8948d8e184c052227e82cf1cead4569cb97b5b7 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 8 Jul 2020 01:12:45 +0300 Subject: [PATCH 03/12] Fixed regex syntax --- app/views/console/settings/index.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/console/settings/index.phtml b/app/views/console/settings/index.phtml index 332f8faf6c..d4c2c359fe 100644 --- a/app/views/console/settings/index.phtml +++ b/app/views/console/settings/index.phtml @@ -361,7 +361,7 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false); - +
From f67fae9ea32b913f56b8b20262d47c658acd8d24 Mon Sep 17 00:00:00 2001 From: Armino Popp Date: Thu, 9 Jul 2020 11:45:50 +0300 Subject: [PATCH 04/12] Corrected filename from DomianTest.php to DomainTest.php --- tests/unit/Network/Validators/DomainTest.php | 46 ++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/unit/Network/Validators/DomainTest.php diff --git a/tests/unit/Network/Validators/DomainTest.php b/tests/unit/Network/Validators/DomainTest.php new file mode 100644 index 0000000000..b04f86d0da --- /dev/null +++ b/tests/unit/Network/Validators/DomainTest.php @@ -0,0 +1,46 @@ +domain = new Domain(); + } + + public function tearDown() + { + $this->domain = null; + } + + public function testIsValid() + { + // Assertions + $this->assertEquals(true, $this->domain->isValid('example.com')); + $this->assertEquals(true, $this->domain->isValid('subdomain.example.com')); + $this->assertEquals(true, $this->domain->isValid('subdomain.example-app.com')); + $this->assertEquals(true, $this->domain->isValid('subdomain.example_app.com')); + $this->assertEquals(true, $this->domain->isValid('subdomain-new.example.com')); + $this->assertEquals(true, $this->domain->isValid('subdomain_new.example.com')); + $this->assertEquals(true, $this->domain->isValid('localhost')); + $this->assertEquals(true, $this->domain->isValid('appwrite.io')); + $this->assertEquals(true, $this->domain->isValid('appwrite.org')); + $this->assertEquals(true, $this->domain->isValid('appwrite.org')); + $this->assertEquals(false, $this->domain->isValid(false)); + $this->assertEquals(false, $this->domain->isValid('.')); + $this->assertEquals(false, $this->domain->isValid('..')); + $this->assertEquals(false, $this->domain->isValid('')); + $this->assertEquals(false, $this->domain->isValid(['string', 'string'])); + $this->assertEquals(false, $this->domain->isValid(1)); + $this->assertEquals(false, $this->domain->isValid(1.2)); + } +} \ No newline at end of file From 822a0e929749f613c1d60eccd63f3a10b9c0a7c7 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 9 Jul 2020 12:20:21 +0300 Subject: [PATCH 05/12] Removed old test --- tests/unit/Network/Validators/DomianTest.php | 46 -------------------- 1 file changed, 46 deletions(-) delete mode 100644 tests/unit/Network/Validators/DomianTest.php diff --git a/tests/unit/Network/Validators/DomianTest.php b/tests/unit/Network/Validators/DomianTest.php deleted file mode 100644 index b04f86d0da..0000000000 --- a/tests/unit/Network/Validators/DomianTest.php +++ /dev/null @@ -1,46 +0,0 @@ -domain = new Domain(); - } - - public function tearDown() - { - $this->domain = null; - } - - public function testIsValid() - { - // Assertions - $this->assertEquals(true, $this->domain->isValid('example.com')); - $this->assertEquals(true, $this->domain->isValid('subdomain.example.com')); - $this->assertEquals(true, $this->domain->isValid('subdomain.example-app.com')); - $this->assertEquals(true, $this->domain->isValid('subdomain.example_app.com')); - $this->assertEquals(true, $this->domain->isValid('subdomain-new.example.com')); - $this->assertEquals(true, $this->domain->isValid('subdomain_new.example.com')); - $this->assertEquals(true, $this->domain->isValid('localhost')); - $this->assertEquals(true, $this->domain->isValid('appwrite.io')); - $this->assertEquals(true, $this->domain->isValid('appwrite.org')); - $this->assertEquals(true, $this->domain->isValid('appwrite.org')); - $this->assertEquals(false, $this->domain->isValid(false)); - $this->assertEquals(false, $this->domain->isValid('.')); - $this->assertEquals(false, $this->domain->isValid('..')); - $this->assertEquals(false, $this->domain->isValid('')); - $this->assertEquals(false, $this->domain->isValid(['string', 'string'])); - $this->assertEquals(false, $this->domain->isValid(1)); - $this->assertEquals(false, $this->domain->isValid(1.2)); - } -} \ No newline at end of file From edf6a5fd04288822d97892629b6016f45f8db78a Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 9 Jul 2020 13:27:11 +0300 Subject: [PATCH 06/12] Added server mode --- app/app.php | 4 ++-- app/controllers/api/teams.php | 2 +- app/init.php | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/app.php b/app/app.php index e92660b5a0..fe51e4a7fd 100644 --- a/app/app.php +++ b/app/app.php @@ -50,7 +50,7 @@ $clients = \array_unique(\array_merge($clientsConsole, \array_map(function ($nod return false; })))); -$utopia->init(function () use ($utopia, $request, $response, &$user, $project, $console, $webhook, $mail, $audit, $usage, $clients) { +$utopia->init(function () use ($utopia, $request, $response, &$user, $project, $console, $webhook, $mail, $audit, $usage, $clients, &$mode) { $route = $utopia->match($request); @@ -160,7 +160,7 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $ $role = Auth::USER_ROLE_APP; $scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', [])); - + $mode = APP_MODE_SERVER; Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. } diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index e0cf3f8d2a..4128fad7b9 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -288,7 +288,7 @@ $utopia->post('/v1/teams/:teamId/memberships') } } - if (!$isOwner && (APP_MODE_ADMIN !== $mode)) { + if (!$isOwner && (APP_MODE_ADMIN !== $mode) && (APP_MODE_SERVER !== $mode)) { throw new Exception('User is not allowed to send invitations for this team', 401); } diff --git a/app/init.php b/app/init.php index 327bfbab67..4a54918a09 100644 --- a/app/init.php +++ b/app/init.php @@ -31,6 +31,7 @@ const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address const APP_EMAIL_SECURITY = 'security@localhost.test'; // Default security email address const APP_USERAGENT = APP_NAME.'-Server v%s. Please report abuse at %s'; const APP_MODE_ADMIN = 'admin'; +const APP_MODE_SERVER = 'server'; const APP_PAGING_LIMIT = 15; const APP_CACHE_BUSTER = 125; const APP_VERSION_STABLE = '0.6.2'; From 5c794dd22bd2fd5f08a4d33f9880adbe557b3013 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 9 Jul 2020 13:44:35 +0300 Subject: [PATCH 07/12] Fix for server mode --- app/controllers/api/teams.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 4128fad7b9..90301656ae 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -219,7 +219,7 @@ $utopia->post('/v1/teams/:teamId/memberships') ->param('roles', [], function () { return new ArrayList(new Text(128)); }, 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions).') ->param('url', '', function () use ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.') // TODO add our own built-in confirm page ->action( - function ($teamId, $email, $name, $roles, $url) use ($response, $mail, $project, $user, $audit, $projectDB, $mode) { + function ($teamId, $email, $name, $roles, $url) use ($response, $mail, $project, $user, $audit, $projectDB, &$mode) { $name = (empty($name)) ? $email : $name; $team = $projectDB->getDocument($teamId); From 7010cc361d6544a698afbbc6d2c0c0bba6c1bbab Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 9 Jul 2020 13:53:46 +0300 Subject: [PATCH 08/12] Fixed membership creation from server --- app/app.php | 4 ++-- app/controllers/api/teams.php | 4 ++-- app/init.php | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/app.php b/app/app.php index fe51e4a7fd..e92660b5a0 100644 --- a/app/app.php +++ b/app/app.php @@ -50,7 +50,7 @@ $clients = \array_unique(\array_merge($clientsConsole, \array_map(function ($nod return false; })))); -$utopia->init(function () use ($utopia, $request, $response, &$user, $project, $console, $webhook, $mail, $audit, $usage, $clients, &$mode) { +$utopia->init(function () use ($utopia, $request, $response, &$user, $project, $console, $webhook, $mail, $audit, $usage, $clients) { $route = $utopia->match($request); @@ -160,7 +160,7 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $ $role = Auth::USER_ROLE_APP; $scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', [])); - $mode = APP_MODE_SERVER; + Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. } diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 90301656ae..8aa6383fd1 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -51,7 +51,7 @@ $utopia->post('/v1/teams') throw new Exception('Failed saving team to DB', 500); } - if ($mode !== APP_MODE_ADMIN && $user->getId()) { // Don't add user on server mode + if ($mode !== APP_MODE_ADMIN && $user->getId()) { // Don't add user on app/server mode $membership = new Document([ '$collection' => Database::SYSTEM_COLLECTION_MEMBERSHIPS, '$permissions' => [ @@ -288,7 +288,7 @@ $utopia->post('/v1/teams/:teamId/memberships') } } - if (!$isOwner && (APP_MODE_ADMIN !== $mode) && (APP_MODE_SERVER !== $mode)) { + if (!$isOwner && (APP_MODE_ADMIN !== $mode) && $user->getId()) { // Not owner, not admin, not app (server) throw new Exception('User is not allowed to send invitations for this team', 401); } diff --git a/app/init.php b/app/init.php index 4a54918a09..327bfbab67 100644 --- a/app/init.php +++ b/app/init.php @@ -31,7 +31,6 @@ const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address const APP_EMAIL_SECURITY = 'security@localhost.test'; // Default security email address const APP_USERAGENT = APP_NAME.'-Server v%s. Please report abuse at %s'; const APP_MODE_ADMIN = 'admin'; -const APP_MODE_SERVER = 'server'; const APP_PAGING_LIMIT = 15; const APP_CACHE_BUSTER = 125; const APP_VERSION_STABLE = '0.6.2'; From e8f755ae112947c3ef8c3ba4b65c3d5590d9342d Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 9 Jul 2020 14:05:45 +0300 Subject: [PATCH 09/12] Update team count --- app/controllers/api/teams.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 8aa6383fd1..d4fc2522ca 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -288,7 +288,7 @@ $utopia->post('/v1/teams/:teamId/memberships') } } - if (!$isOwner && (APP_MODE_ADMIN !== $mode) && $user->getId()) { // Not owner, not admin, not app (server) + if (!$isOwner && APP_MODE_ADMIN !== $mode && $user->getId()) { // Not owner, not admin, not app (server) throw new Exception('User is not allowed to send invitations for this team', 401); } @@ -305,13 +305,18 @@ $utopia->post('/v1/teams/:teamId/memberships') 'roles' => $roles, 'invited' => \time(), 'joined' => 0, - 'confirm' => (APP_MODE_ADMIN === $mode), + 'confirm' => (APP_MODE_ADMIN === $mode || !$user->getId()), 'secret' => Auth::hash($secret), ]); - if (APP_MODE_ADMIN === $mode) { // Allow admin to create membership + if (APP_MODE_ADMIN === $mode || !$user->getId()) { // Allow admin to create membership Authorization::disable(); $membership = $projectDB->createDocument($membership->getArrayCopy()); + + $team = $projectDB->updateDocument(\array_merge($team->getArrayCopy(), [ + 'sum' => $team->getAttribute('sum', 0) + 1, + ])); + Authorization::reset(); } else { $membership = $projectDB->createDocument($membership->getArrayCopy()); @@ -346,7 +351,7 @@ $utopia->post('/v1/teams/:teamId/memberships') ->setParam('{{text-cta}}', '#ffffff') ; - if (APP_MODE_ADMIN !== $mode) { // No need in comfirmation when in admin mode + if (APP_MODE_ADMIN !== $mode && $user->getId()) { // No need in comfirmation when in admin or app mode $mail ->setParam('event', 'teams.membership.create') ->setParam('recipient', $email) From 134bdae348978f110150452ebb6e49f88600150d Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 9 Jul 2020 14:25:33 +0300 Subject: [PATCH 10/12] Added joined date on admin mode --- app/controllers/api/teams.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index d4fc2522ca..c3cf64e41c 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -304,7 +304,7 @@ $utopia->post('/v1/teams/:teamId/memberships') 'teamId' => $team->getId(), 'roles' => $roles, 'invited' => \time(), - 'joined' => 0, + 'joined' => (APP_MODE_ADMIN === $mode || !$user->getId()) ? \time() : 0, 'confirm' => (APP_MODE_ADMIN === $mode || !$user->getId()), 'secret' => Auth::hash($secret), ]); From 2cbf58ea5a81dd718bb3b7a83623f652919b1f45 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Fri, 10 Jul 2020 10:34:01 +0300 Subject: [PATCH 11/12] Fixed cross origin error --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 78b0fd76cc..bde9cb7a31 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "appwrite/php-clamav": "1.0.*", - "utopia-php/framework": "0.4.0", + "utopia-php/framework": "0.4.1", "utopia-php/abuse": "0.2.*", "utopia-php/audit": "0.3.*", "utopia-php/cache": "0.2.*", From f08543d669baf655a0600d37290d47332bcaeb60 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Fri, 10 Jul 2020 12:06:30 +0300 Subject: [PATCH 12/12] Auto-assign membership from console or server API --- app/controllers/api/teams.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index c3cf64e41c..8981695aa3 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -316,6 +316,15 @@ $utopia->post('/v1/teams/:teamId/memberships') $team = $projectDB->updateDocument(\array_merge($team->getArrayCopy(), [ 'sum' => $team->getAttribute('sum', 0) + 1, ])); + + // Attach user to team + $invitee->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND); + + $invitee = $projectDB->updateDocument($invitee->getArrayCopy()); + + if (false === $invitee) { + throw new Exception('Failed saving user to DB', 500); + } Authorization::reset(); } else {