diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 75ec760abf..41fb64c87e 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -263,13 +263,25 @@ Http::init() throw new Exception(Exception::USER_UNAUTHORIZED); } + $projectId = $project->getId(); + if ($projectId === 'console' && str_starts_with($route->getPath(), '/v1/projects/:projectId')) { + $uri = $request->getURI(); + $projectId = explode('/', $uri)[3]; + } + $scopes = []; // Reset scope if admin foreach ($adminRoles as $role) { - if (str_starts_with($role, 'project-')) { - $role = substr($role, strrpos($role, '-') + 1); + $isTeamWideRole = !str_starts_with($role, 'project-'); + $isProjectSpecificRole = $projectId !== 'console' && str_starts_with($role, 'project-' . $projectId); + + if ($isTeamWideRole || $isProjectSpecificRole) { + $role = match (str_starts_with($role, 'project-')) { + true => substr($role, strrpos($role, '-') + 1), + false => $role, + }; + $scopes = \array_merge($scopes, $roles[$role]['scopes']); + $authorization->addRole($role); } - $scopes = \array_merge($scopes, $roles[$role]['scopes']); - $authorization->addRole($role); } /** diff --git a/tests/e2e/Services/Projects/ProjectsBase.php b/tests/e2e/Services/Projects/ProjectsBase.php index eb580afe05..28e22ed432 100644 --- a/tests/e2e/Services/Projects/ProjectsBase.php +++ b/tests/e2e/Services/Projects/ProjectsBase.php @@ -4,6 +4,7 @@ namespace Tests\E2E\Services\Projects; use Tests\E2E\Client; use Utopia\Database\Helpers\ID; +use Utopia\Database\Helpers\Role; trait ProjectsBase { @@ -151,4 +152,29 @@ trait ProjectsBase $this->assertEquals(200, $response['headers']['status-code']); } + + protected function setupFunction(string $projectId, string $functionId, string $token): void + { + $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-mode' => 'admin', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $token, + ]), [ + 'functionId' => $functionId, + 'name' => 'Test function', + 'execute' => [Role::any()->toString()], + 'runtime' => 'node-22', + 'entrypoint' => 'index.js', + 'events' => [ + 'users.*.create', + 'users.*.delete', + ], + 'schedule' => '0 0 1 1 *', + 'timeout' => 10, + ]); + $this->assertEquals(201, $function['headers']['status-code']); + $this->assertNotEmpty($function['body']['$id']); + } } diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 6bd3636f3f..bc9aba09f3 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -6438,7 +6438,7 @@ class ProjectsConsoleClientTest extends Scope } /** - * Test project specific permissions for project resources, in this case 'variables'. + * Test project specific permissions for project resources, in this case 'function variables'. */ public function testProjectSpecificPermissionsForProjectResources(): void { @@ -6511,12 +6511,17 @@ class ProjectsConsoleClientTest extends Scope ]); $token = $session['cookies']['a_session_' . $this->getProject()['$id']]; + // Setup functions + $functionId = ID::unique(); + $this->setupFunction($projectIdA, $functionId, $token); + $this->setupFunction($projectIdB, $functionId, $token); + foreach ($testCases as $testCase) { $this->updateMembershipRole($teamId, $testUserMembershipId, $testCase['roles']); foreach ($testCase['successProjectIds'] as $projectId) { $variableId = ID::unique(); - $response = $this->client->call(Client::METHOD_POST, '/project/variables', [ + $response = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, @@ -6535,7 +6540,7 @@ class ProjectsConsoleClientTest extends Scope foreach ($testCase['failureProjectIds'] as $projectId) { $variableId = ID::unique(); - $response = $this->client->call(Client::METHOD_POST, '/project/variables', [ + $response = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $projectId,