diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index f4115c5308..167e41d91e 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -297,7 +297,12 @@ Http::init() * whether the admin user has necessary permission on the project (sites, functions, etc. don't have permissions associated to them). */ if ($project->getId() !== 'console' && $mode === APP_MODE_ADMIN) { - $input = new Input(Database::PERMISSION_READ, $project->getPermissionsByType(Database::PERMISSION_READ)); + $action = match ($route->getMethod()) { + Request::METHOD_GET => Database::PERMISSION_READ, + Request::METHOD_DELETE => Database::PERMISSION_DELETE, + default => Database::PERMISSION_UPDATE, + }; + $input = new Input($action, $project->getPermissionsByType($action)); $initialStatus = $authorization->getStatus(); $authorization->enable(); if (!$authorization->isValid($input)) { diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index ee5f679330..73768c9500 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -6,6 +6,7 @@ use Appwrite\Event\Build; use Appwrite\Extend\Exception; use Appwrite\Platform\Action; use Appwrite\Platform\Modules\Compute\Validator\Specification as SpecificationValidator; +use Appwrite\Platform\Permission as AppwritePermission; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -22,32 +23,7 @@ use Utopia\VCS\Exception\RepositoryNotFound; class Base extends Action { - /** - * Permissions for resources in this project. - * - * @param string $teamId - * @param string $projectId - * @return string[] - */ - protected function getPermissions(string $teamId, string $projectId): array - { - return [ - // Team-wide permissions - Permission::read(Role::team(ID::custom($teamId), 'owner')), - Permission::read(Role::team(ID::custom($teamId), 'developer')), - Permission::update(Role::team(ID::custom($teamId), 'owner')), - Permission::update(Role::team(ID::custom($teamId), 'developer')), - Permission::delete(Role::team(ID::custom($teamId), 'owner')), - Permission::delete(Role::team(ID::custom($teamId), 'developer')), - // Project-wide permissions - Permission::read(Role::team(ID::custom($teamId), "project-{$projectId}-owner")), - Permission::read(Role::team(ID::custom($teamId), "project-{$projectId}-developer")), - Permission::update(Role::team(ID::custom($teamId), "project-{$projectId}-owner")), - Permission::update(Role::team(ID::custom($teamId), "project-{$projectId}-developer")), - Permission::delete(Role::team(ID::custom($teamId), "project-{$projectId}-owner")), - Permission::delete(Role::team(ID::custom($teamId), "project-{$projectId}-developer")), - ]; - } + use AppwritePermission; /** * Get default specification based on plan and available specifications. diff --git a/src/Appwrite/Platform/Modules/Projects/Http/Projects/Action.php b/src/Appwrite/Platform/Modules/Projects/Http/Projects/Action.php index 21cd108485..db8630ba78 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/Projects/Action.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/Projects/Action.php @@ -3,29 +3,9 @@ namespace Appwrite\Platform\Modules\Projects\Http\Projects; use Appwrite\Platform\Action as AppwriteAction; -use Utopia\Database\Helpers\ID; -use Utopia\Database\Helpers\Permission; -use Utopia\Database\Helpers\Role; +use Appwrite\Platform\Permission as AppwritePermission; class Action extends AppwriteAction { - protected function getPermissions(string $teamId, string $projectId): array - { - return [ - // Team-wide permissions - Permission::read(Role::team(ID::custom($teamId), 'owner')), - Permission::read(Role::team(ID::custom($teamId), 'developer')), - Permission::update(Role::team(ID::custom($teamId), 'owner')), - Permission::update(Role::team(ID::custom($teamId), 'developer')), - Permission::delete(Role::team(ID::custom($teamId), 'owner')), - Permission::delete(Role::team(ID::custom($teamId), 'developer')), - // Project-wide permissions - Permission::read(Role::team(ID::custom($teamId), "project-{$projectId}-owner")), - Permission::read(Role::team(ID::custom($teamId), "project-{$projectId}-developer")), - Permission::update(Role::team(ID::custom($teamId), "project-{$projectId}-owner")), - Permission::update(Role::team(ID::custom($teamId), "project-{$projectId}-developer")), - Permission::delete(Role::team(ID::custom($teamId), "project-{$projectId}-owner")), - Permission::delete(Role::team(ID::custom($teamId), "project-{$projectId}-developer")), - ]; - } + use AppwritePermission; } diff --git a/src/Appwrite/Platform/Permission.php b/src/Appwrite/Platform/Permission.php new file mode 100644 index 0000000000..7f6b000ce8 --- /dev/null +++ b/src/Appwrite/Platform/Permission.php @@ -0,0 +1,37 @@ +setupProject([ + 'projectId' => ID::unique(), + 'name' => 'Project Test A', + 'region' => System::getEnv('_APP_REGION', 'default') + ], $teamId); + $projectIdB = $this->setupProject([ + 'projectId' => ID::unique(), + 'name' => 'Project Test B', + 'region' => System::getEnv('_APP_REGION', 'default') + ], $teamId, false); + $teamOwnerEmail = 'team-' . ID::unique() . '-owner@localhost.test'; + $teamOwnerName = 'Team - owner'; + $teamDeveloperAndProjectAOwnerEmail = 'teamdeveloperandprojecta-' . ID::unique() . '-owner@localhost.test'; + $teamDeveloperAndProjectAOwnerName = 'Team Developer and Project A - owner'; + $projectAOwnerEmail = 'projecta-' . ID::unique() . '-owner@localhost.test'; + $projectAOwnerName = 'Project A - owner'; + $this->setupUserMembership([ + 'teamId' => $teamId, + 'email' => $teamOwnerEmail, + 'name' => $teamOwnerName, + 'roles' => ["owner"], + ]); + $this->setupUserMembership([ + 'teamId' => $teamId, + 'email' => $teamDeveloperAndProjectAOwnerEmail, + 'name' => $teamDeveloperAndProjectAOwnerName, + 'roles' => ["developer", "project-$projectIdA-owner"], + ]); + $this->setupUserMembership([ + 'teamId' => $teamId, + 'email' => $projectAOwnerEmail, + 'name' => $projectAOwnerName, + 'roles' => ["project-$projectIdA-owner"], + ]); + + $testCases = [ + ['userEmail' => $teamOwnerEmail, 'accessibleProjectIds' => [$projectIdA, $projectIdB], 'inaccessibleProjectIds' => []], + ['userEmail' => $teamDeveloperAndProjectAOwnerEmail, 'accessibleProjectIds' => [$projectIdA, $projectIdB], 'inaccessibleProjectIds' => []], + ['userEmail' => $projectAOwnerEmail, 'accessibleProjectIds' => [$projectIdA], 'inaccessibleProjectIds' => [$projectIdB]], + ]; + + foreach ($testCases as $testCase) { + $session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'email' => $testCase['userEmail'], + 'password' => 'password', + ]); + $token = $session['cookies']['a_session_' . $this->getProject()['$id']]; + + foreach ($testCase['accessibleProjectIds'] as $projectId) { + $variableId = ID::unique(); + $response = $this->client->call(Client::METHOD_POST, '/project/variables', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-mode' => 'admin', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $token, + ], [ + 'key' => 'APP_TEST_' . $variableId, + 'value' => 'TESTINGVALUE', + 'secret' => false + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals('APP_TEST_' . $variableId, $response['body']['key']); + $this->assertEquals('TESTINGVALUE', $response['body']['value']); + } + + foreach ($testCase['inaccessibleProjectIds'] as $projectId) { + echo "processing user " . $testCase['userEmail'] . " for project " . $projectId . "\n"; + + $variableId = ID::unique(); + $response = $this->client->call(Client::METHOD_POST, '/project/variables', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-mode' => 'admin', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $token, + ], [ + 'key' => 'APP_TEST_' . $variableId, + 'value' => 'TESTINGVALUE', + 'secret' => false + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + } + } + } }