This commit is contained in:
Hemachandar
2026-02-06 22:27:31 +05:30
parent 0d31205e4f
commit ddf1e92dd1
5 changed files with 145 additions and 49 deletions
+6 -1
View File
@@ -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)) {
+2 -26
View File
@@ -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.
@@ -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;
}
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace Appwrite\Platform;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Helpers\Permission as DbPermission;
trait Permission
{
/**
* Permissions for projects & project resources.
*
* @param string $teamId
* @param string $projectId
* @return array
*/
public function getPermissions(string $teamId, string $projectId): array
{
return [
// Team-wide permissions
DbPermission::read(Role::team(ID::custom($teamId), 'owner')),
DbPermission::read(Role::team(ID::custom($teamId), 'developer')),
DbPermission::update(Role::team(ID::custom($teamId), 'owner')),
DbPermission::update(Role::team(ID::custom($teamId), 'developer')),
DbPermission::delete(Role::team(ID::custom($teamId), 'owner')),
DbPermission::delete(Role::team(ID::custom($teamId), 'developer')),
// Project-wide permissions
DbPermission::read(Role::team(ID::custom($teamId), "project-{$projectId}-owner")),
DbPermission::read(Role::team(ID::custom($teamId), "project-{$projectId}-developer")),
DbPermission::update(Role::team(ID::custom($teamId), "project-{$projectId}-owner")),
DbPermission::update(Role::team(ID::custom($teamId), "project-{$projectId}-developer")),
DbPermission::delete(Role::team(ID::custom($teamId), "project-{$projectId}-owner")),
DbPermission::delete(Role::team(ID::custom($teamId), "project-{$projectId}-developer")),
];
}
}
@@ -5941,4 +5941,102 @@ class ProjectsConsoleClientTest extends Scope
}
}
}
/**
* Test project specific permissions for project resources, in this case 'variables'.
*/
public function testProjectSpecificPermissionsForProjectResources(): void
{
$teamId = ID::unique();
$projectIdA = $this->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']);
}
}
}
}