Implement project-specific permissions

This commit is contained in:
Hemachandar
2026-01-25 21:55:32 +05:30
parent 0dd2f29a7e
commit 9f5cf5f384
9 changed files with 530 additions and 39 deletions
+96
View File
@@ -0,0 +1,96 @@
<?php
namespace Appwrite\Auth\Validator;
use Utopia\Database\Validator\Roles;
use Utopia\Validator;
class Role extends Validator
{
/**
* @var array
*/
protected array $roles;
/**
* Constructor
*
* Sets the acceptable roles.
*
* @param array $list
* @param string $type of $list items
*/
public function __construct(array $roles)
{
$this->roles = $roles;
}
/**
* Get Description
*
* Returns validator description
*
* @return string
*/
public function getDescription(): string
{
return 'Value must be one of (' . \implode(', ', $this->roles) . ' (or) in the format "project-<projectId>-<role>")';
}
/**
* Is array
*
* Function will return true if object is array.
*
* @return bool
*/
public function isArray(): bool
{
return false;
}
/**
* Get Type
*
* Returns validator type.
*
* @return string
*/
public function getType(): string
{
return self::TYPE_STRING;
}
/**
* Is valid
*
* Validation will pass if $value is in the white list array.
*
* @param mixed $value
* @return bool
*/
public function isValid(mixed $value): bool
{
if (!\is_string($value)) {
return false;
}
$role = $value;
if (str_starts_with($value, "project-")) {
$parts = explode("-", $value);
if (\count($parts) !== 3) {
return false;
}
$role = $parts[2];
}
if (!\in_array($role, $this->roles)) {
return false;
}
return true;
}
}
@@ -7,6 +7,7 @@ use Utopia\Auth\Proofs\Token;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Roles;
class User extends Document
@@ -35,7 +36,7 @@ class User extends Document
*
* @return array<string>
*/
public function getRoles($authorization): array
public function getRoles(Authorization $authorization, string $projectId = '', string $path = ''): array
{
$roles = [];
@@ -60,19 +61,34 @@ class User extends Document
}
foreach ($this->getAttribute('memberships', []) as $node) {
if (!isset($node['confirm']) || !$node['confirm']) {
if (!isset($node['confirm']) || !$node['confirm'] || !isset($node['$id']) || !isset($node['teamId'])) {
continue;
}
if (isset($node['$id']) && isset($node['teamId'])) {
$roles[] = Role::team($node['teamId'])->toString();
$roles[] = Role::member($node['$id'])->toString();
// Role for this membership id.
$roles[] = Role::member($node['$id'])->toString();
if (isset($node['roles'])) {
foreach ($node['roles'] as $nodeRole) { // Set all team roles
$roles[] = Role::team($node['teamId'], $nodeRole)->toString();
}
$nodeRoles = $node['roles'] ?? [];
if ($projectId !== 'console') {
$roles[] = Role::team($node['teamId'])->toString(); // Populate team-wide base role.
} else {
$teamWideRoles = \array_filter($nodeRoles, fn ($role) => !str_starts_with($role, "project-"));
$populateTeamWideRole = !str_starts_with($path, "/v1/projects") || !empty($teamWideRoles);
if ($populateTeamWideRole) {
$roles[] = Role::team($node['teamId'])->toString(); // Populate team-wide base role.
}
$projectSpecificRoles = \array_filter($nodeRoles, fn ($role) => str_starts_with($role, "project-"));
foreach ($projectSpecificRoles as $projectRole) {
$parts = explode("-", $projectRole);
$roles[] = Role::team($node['teamId'], "$parts[0]-$parts[1]")->toString(); // Populate project-wide base role.
}
}
foreach ($nodeRoles as $nodeRole) {
$roles[] = Role::team($node['teamId'], $nodeRole)->toString(); // Set all team roles
}
}