From f672269a8c44da7acdc4d0bb59f745abc329e86b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 16 Aug 2022 17:54:44 +1200 Subject: [PATCH] Handle aggregates per resource type at the controller level --- app/controllers/api/databases.php | 25 +++++++- app/controllers/api/storage.php | 25 ++++++++ composer.json | 6 +- composer.lock | 35 +++++----- .../Permissions/PermissionsProcessor.php | 64 +++++++++++++++++++ 5 files changed, 135 insertions(+), 20 deletions(-) create mode 100644 src/Appwrite/Permissions/PermissionsProcessor.php diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 340547d17a..b0288deb28 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -525,6 +525,12 @@ App::post('/v1/databases/:databaseId/collections') $collectionId = $collectionId == 'unique()' ? ID::unique() : $collectionId; + /** + * Map aggregate permissions into the multiple permissions they represent, + * accounting for the resource type given that some types not allowed specific permissions. + */ + $permissions = PermissionsProcessor::aggregate($permissions, 'collection'); + try { $dbForProject->createDocument('database_' . $database->getInternalId(), new Document([ '$id' => $collectionId, @@ -792,7 +798,12 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') } $permissions ??= $collection->getPermissions() ?? []; - + + /** + * Map aggregate permissions into the multiple permissions they represent. + */ + $permissions = PermissionsProcessor::aggregate($permissions, 'collection'); + $enabled ??= $collection->getAttribute('enabled', true); try { @@ -1933,6 +1944,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); } + /** + * Map aggregate permissions into the multiple permissions they represent, + * accounting for the resource type given that some types not allowed specific permissions. + */ + $permissions = PermissionsProcessor::aggregate($permissions, 'document'); + /** * Add permissions for current the user for any missing types * from the allowed permissions for this resource type. @@ -2363,6 +2380,12 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum } } + /** + * Map aggregate permissions into the multiple permissions they represent, + * accounting for the resource type given that some types not allowed specific permissions. + */ + $permissions = PermissionsProcessor::aggregate($permissions, 'document'); + if (\is_null($permissions)) { $permissions = $document->getPermissions() ?? []; } diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 0cc140aeef..0b19f9c1a4 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -74,6 +74,13 @@ App::post('/v1/storage/buckets') ->action(function (string $bucketId, string $name, ?array $permissions, string $fileSecurity, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { $bucketId = $bucketId === 'unique()' ? ID::unique() : $bucketId; + + /** + * Map aggregate permissions into the multiple permissions they represent, + * accounting for the resource type given that some types not allowed specific permissions. + */ + $permissions = PermissionsProcessor::aggregate($permissions, 'bucket'); + try { $files = Config::getParam('collections', [])['files'] ?? []; if (empty($files)) { @@ -261,6 +268,12 @@ App::put('/v1/storage/buckets/:bucketId') $encryption ??= $bucket->getAttribute('encryption', true); $antivirus ??= $bucket->getAttribute('antivirus', true); + /** + * Map aggregate permissions into the multiple permissions they represent, + * accounting for the resource type given that some types not allowed specific permissions. + */ + $permissions = PermissionsProcessor::aggregate($permissions, 'bucket'); + $bucket = $dbForProject->updateDocument('buckets', $bucket->getId(), $bucket ->setAttribute('name', $name) ->setAttribute('$permissions', $permissions) @@ -375,6 +388,12 @@ App::post('/v1/storage/buckets/:bucketId/files') throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); } + /** + * Map aggregate permissions into the multiple permissions they represent, + * accounting for the resource type given that some types not allowed specific permissions. + */ + $permissions = PermissionsProcessor::aggregate($permissions, 'file'); + /** * Add permissions for current the user for any missing types * from the allowed permissions for this resource type. @@ -1323,6 +1342,12 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') } } + /** + * Map aggregate permissions into the multiple permissions they represent, + * accounting for the resource type given that some types not allowed specific permissions. + */ + $permissions = PermissionsProcessor::aggregate($permissions, 'file'); + $file->setAttribute('$permissions', $permissions); $file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file); diff --git a/composer.json b/composer.json index 685a6d1197..0d8a28e91b 100644 --- a/composer.json +++ b/composer.json @@ -45,13 +45,13 @@ "appwrite/php-runtimes": "0.10.*", "utopia-php/framework": "0.20.*", "utopia-php/logger": "0.3.*", - "utopia-php/abuse": "0.9.*", + "utopia-php/abuse": "dev-refactor-permissions", "utopia-php/analytics": "0.2.*", - "utopia-php/audit": "0.10.*", + "utopia-php/audit": "dev-refactor-permissions", "utopia-php/cache": "0.6.*", "utopia-php/cli": "0.13.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.20.*", + "utopia-php/database": "dev-refactor-permissions", "utopia-php/locale": "0.4.*", "utopia-php/registry": "0.5.*", "utopia-php/preloader": "0.2.*", diff --git a/composer.lock b/composer.lock index ce62bbb373..27f29b8ff7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ec0e0c2072927a82fc801a757777ad73", + "content-hash": "d49d34cbdc70504ea61c5548bd60278a", "packages": [ { "name": "adhocore/jwt", @@ -1733,17 +1733,17 @@ }, { "name": "utopia-php/abuse", - "version": "0.9.0", + "version": "dev-refactor-permissions", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "34156bb5292d704bb8bc8141bb5151126ed4830a" + "reference": "496cba1f2e7f50a6faebdb77b9178a99d755b49a" }, "require": { "ext-curl": "*", "ext-pdo": "*", "php": ">=8.0", - "utopia-php/database": "0.20.0" + "utopia-php/database": "dev-refactor-permissions" }, "require-dev": { "phpunit/phpunit": "^9.4", @@ -1772,7 +1772,7 @@ "upf", "utopia" ], - "time": "2022-08-15T07:35:56+00:00" + "time": "2022-08-16T05:16:56+00:00" }, { "name": "utopia-php/analytics", @@ -1831,16 +1831,16 @@ }, { "name": "utopia-php/audit", - "version": "0.10.0", + "version": "dev-refactor-permissions", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "458da3e60ea222bf9791f4891591d7f2ee16e4bb" + "reference": "cd3f0f2fd4716e32333397fedd8e7f07932361ec" }, "require": { "ext-pdo": "*", "php": ">=8.0", - "utopia-php/database": "0.20.0" + "utopia-php/database": "dev-refactor-permissions" }, "require-dev": { "phpunit/phpunit": "^9.3", @@ -1869,7 +1869,7 @@ "upf", "utopia" ], - "time": "2022-08-14T19:59:21+00:00" + "time": "2022-08-16T05:16:47+00:00" }, { "name": "utopia-php/cache", @@ -2030,11 +2030,11 @@ }, { "name": "utopia-php/database", - "version": "0.20.0", + "version": "dev-refactor-permissions", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "cd89b41564223cddf7d87a41bbaf736a0c89f327" + "reference": "a1f4c0dbe387cde1d26916d6d1cfddbfe54f3e82" }, "require": { "ext-mongodb": "*", @@ -2084,7 +2084,7 @@ "upf", "utopia" ], - "time": "2022-08-14T15:22:34+00:00" + "time": "2022-08-16T05:14:16+00:00" }, { "name": "utopia-php/domains", @@ -2805,12 +2805,12 @@ "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "409e9a6f24a2ad4be7fd45bc424ff5d845c7eb51" + "reference": "807e7890aed597ef47e01be8ad11c3b561b7c1e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/409e9a6f24a2ad4be7fd45bc424ff5d845c7eb51", - "reference": "409e9a6f24a2ad4be7fd45bc424ff5d845c7eb51", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/807e7890aed597ef47e01be8ad11c3b561b7c1e8", + "reference": "807e7890aed597ef47e01be8ad11c3b561b7c1e8", "shasum": "" }, "require": { @@ -2848,7 +2848,7 @@ "issues": "https://github.com/appwrite/sdk-generator/issues", "source": "https://github.com/appwrite/sdk-generator/tree/master" }, - "time": "2022-08-10T12:49:03+00:00" + "time": "2022-08-15T22:33:25+00:00" }, { "name": "doctrine/instantiator", @@ -5330,6 +5330,9 @@ ], "minimum-stability": "stable", "stability-flags": { + "utopia-php/abuse": 20, + "utopia-php/audit": 20, + "utopia-php/database": 20, "appwrite/sdk-generator": 20 }, "prefer-stable": false, diff --git a/src/Appwrite/Permissions/PermissionsProcessor.php b/src/Appwrite/Permissions/PermissionsProcessor.php new file mode 100644 index 0000000000..ffbfeb3249 --- /dev/null +++ b/src/Appwrite/Permissions/PermissionsProcessor.php @@ -0,0 +1,64 @@ + $permission) { + $permission = Permission::parse($permission); + foreach ($aggregates as $type => $subTypes) { + if ($permission->getPermission() != $type) { + continue; + } + foreach ($subTypes as $subType) { + $permissions[] = (new Permission( + $subType, + $permission->getRole(), + $permission->getIdentifier(), + $permission->getDimension() + ))->toString(); + } + unset($permissions[$i]); + } + } + return $permissions; + } + + private static function getAggregates($resource): array + { + $aggregates = []; + + switch ($resource) { + case 'document': + case 'file': + $aggregates['write'] = [ + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE + ]; + break; + case 'collection': + case 'bucket': + $aggregates['write'] = [ + Database::PERMISSION_CREATE, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE + ]; + break; + } + + return $aggregates; + } + +} \ No newline at end of file