mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch 'refactor-permissions-inc-console-fix' of https://github.com/appwrite/appwrite into feat-improve-keys
This commit is contained in:
+17
-16
@@ -11,6 +11,7 @@ use Appwrite\Auth\Hash\Scryptmodified;
|
||||
use Appwrite\Auth\Hash\Sha;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
||||
class Auth
|
||||
@@ -32,13 +33,13 @@ class Auth
|
||||
/**
|
||||
* User Roles.
|
||||
*/
|
||||
public const USER_ROLE_ALL = 'all';
|
||||
public const USER_ROLE_GUEST = 'guest';
|
||||
public const USER_ROLE_MEMBER = 'member';
|
||||
public const USER_ROLE_ANY = 'any';
|
||||
public const USER_ROLE_GUESTS = 'guests';
|
||||
public const USER_ROLE_USERS = 'users';
|
||||
public const USER_ROLE_ADMIN = 'admin';
|
||||
public const USER_ROLE_DEVELOPER = 'developer';
|
||||
public const USER_ROLE_OWNER = 'owner';
|
||||
public const USER_ROLE_APP = 'app';
|
||||
public const USER_ROLE_APPS = 'apps';
|
||||
public const USER_ROLE_SYSTEM = 'system';
|
||||
|
||||
/**
|
||||
@@ -316,7 +317,7 @@ class Auth
|
||||
$token->isSet('expire') &&
|
||||
$token->getAttribute('type') == $type &&
|
||||
$token->getAttribute('secret') === self::hash($secret) &&
|
||||
$token->getAttribute('expire') >= DateTime::now()
|
||||
DateTime::formatTz($token->getAttribute('expire')) >= DateTime::formatTz(DateTime::now())
|
||||
) {
|
||||
return (string)$token->getId();
|
||||
}
|
||||
@@ -335,7 +336,7 @@ class Auth
|
||||
$token->isSet('expire') &&
|
||||
$token->getAttribute('type') == Auth::TOKEN_TYPE_PHONE &&
|
||||
$token->getAttribute('secret') === $secret &&
|
||||
$token->getAttribute('expire') >= DateTime::now()
|
||||
DateTime::formatTz($token->getAttribute('expire')) >= DateTime::formatTz(DateTime::now())
|
||||
) {
|
||||
return (string) $token->getId();
|
||||
}
|
||||
@@ -361,7 +362,7 @@ class Auth
|
||||
$session->isSet('expire') &&
|
||||
$session->isSet('provider') &&
|
||||
$session->getAttribute('secret') === self::hash($secret) &&
|
||||
$session->getAttribute('expire') >= DateTime::now()
|
||||
DateTime::formatTz($session->getAttribute('expire')) >= DateTime::formatTz(DateTime::now())
|
||||
) {
|
||||
return $session->getId();
|
||||
}
|
||||
@@ -380,9 +381,9 @@ class Auth
|
||||
public static function isPrivilegedUser(array $roles): bool
|
||||
{
|
||||
if (
|
||||
in_array('role:' . self::USER_ROLE_OWNER, $roles) ||
|
||||
in_array('role:' . self::USER_ROLE_DEVELOPER, $roles) ||
|
||||
in_array('role:' . self::USER_ROLE_ADMIN, $roles)
|
||||
in_array(self::USER_ROLE_OWNER, $roles) ||
|
||||
in_array(self::USER_ROLE_DEVELOPER, $roles) ||
|
||||
in_array(self::USER_ROLE_ADMIN, $roles)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -399,7 +400,7 @@ class Auth
|
||||
*/
|
||||
public static function isAppUser(array $roles): bool
|
||||
{
|
||||
if (in_array('role:' . self::USER_ROLE_APP, $roles)) {
|
||||
if (in_array(self::USER_ROLE_APPS, $roles)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -418,19 +419,19 @@ class Auth
|
||||
|
||||
if (!self::isPrivilegedUser(Authorization::getRoles()) && !self::isAppUser(Authorization::getRoles())) {
|
||||
if ($user->getId()) {
|
||||
$roles[] = 'user:' . $user->getId();
|
||||
$roles[] = 'role:' . Auth::USER_ROLE_MEMBER;
|
||||
$roles[] = Role::user($user->getId())->toString();
|
||||
$roles[] = Role::users()->toString();
|
||||
} else {
|
||||
return ['role:' . Auth::USER_ROLE_GUEST];
|
||||
return [Role::guests()->toString()];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($user->getAttribute('memberships', []) as $node) {
|
||||
if (isset($node['teamId']) && isset($node['roles'])) {
|
||||
$roles[] = 'team:' . $node['teamId'];
|
||||
$roles[] = Role::team($node['teamId'])->toString();
|
||||
|
||||
foreach ($node['roles'] as $nodeRole) { // Set all team roles
|
||||
$roles[] = 'team:' . $node['teamId'] . '/' . $nodeRole;
|
||||
$roles[] = Role::team($node['teamId'], $nodeRole)->toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Appwrite\Messaging\Adapter;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class Realtime extends Adapter
|
||||
{
|
||||
@@ -187,7 +189,7 @@ class Realtime extends Adapter
|
||||
*/
|
||||
if (
|
||||
\array_key_exists($channel, $this->subscriptions[$event['project']][$role])
|
||||
&& (\in_array($role, $event['roles']) || \in_array('role:all', $event['roles']))
|
||||
&& (\in_array($role, $event['roles']) || \in_array(Role::any()->toString(), $event['roles']))
|
||||
) {
|
||||
/**
|
||||
* Saving all connections that are allowed to receive this event.
|
||||
@@ -256,27 +258,25 @@ class Realtime extends Adapter
|
||||
case 'users':
|
||||
$channels[] = 'account';
|
||||
$channels[] = 'account.' . $parts[1];
|
||||
$roles = ['user:' . $parts[1]];
|
||||
|
||||
$roles = [Role::user(ID::custom($parts[1]))->toString()];
|
||||
break;
|
||||
case 'teams':
|
||||
if ($parts[2] === 'memberships') {
|
||||
$permissionsChanged = $parts[4] ?? false;
|
||||
$channels[] = 'memberships';
|
||||
$channels[] = 'memberships.' . $parts[3];
|
||||
$roles = ['team:' . $parts[1]];
|
||||
} else {
|
||||
$permissionsChanged = $parts[2] === 'create';
|
||||
$channels[] = 'teams';
|
||||
$channels[] = 'teams.' . $parts[1];
|
||||
$roles = ['team:' . $parts[1]];
|
||||
}
|
||||
$roles = [Role::team(ID::custom($parts[1]))->toString()];
|
||||
break;
|
||||
case 'databases':
|
||||
if (in_array($parts[4] ?? [], ['attributes', 'indexes'])) {
|
||||
$channels[] = 'console';
|
||||
$projectId = 'console';
|
||||
$roles = ['team:' . $project->getAttribute('teamId')];
|
||||
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
|
||||
} elseif (($parts[4] ?? '') === 'documents') {
|
||||
if ($database->isEmpty()) {
|
||||
throw new \Exception('Database needs to be passed to Realtime for Document events in the Database.');
|
||||
@@ -289,18 +289,23 @@ class Realtime extends Adapter
|
||||
$channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getCollection() . '.documents';
|
||||
$channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getCollection() . '.documents.' . $payload->getId();
|
||||
|
||||
$roles = ($collection->getAttribute('permission') === 'collection') ? $collection->getRead() : $payload->getRead();
|
||||
$roles = $collection->getAttribute('documentSecurity', false)
|
||||
? \array_merge($collection->getRead(), $payload->getRead())
|
||||
: $collection->getRead();
|
||||
}
|
||||
break;
|
||||
case 'buckets':
|
||||
if ($parts[2] === 'files') {
|
||||
if ($bucket->isEmpty()) {
|
||||
throw new \Exception('Bucket needs to be pased to Realtime for File events in the Storage.');
|
||||
throw new \Exception('Bucket needs to be passed to Realtime for File events in the Storage.');
|
||||
}
|
||||
$channels[] = 'files';
|
||||
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files';
|
||||
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files.' . $payload->getId();
|
||||
$roles = ($bucket->getAttribute('permission') === 'bucket') ? $bucket->getRead() : $payload->getRead();
|
||||
|
||||
$roles = $bucket->getAttribute('fileSecurity', false)
|
||||
? \array_merge($bucket->getRead(), $payload->getRead())
|
||||
: $bucket->getRead();
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -316,7 +321,8 @@ class Realtime extends Adapter
|
||||
}
|
||||
} elseif ($parts[2] === 'deployments') {
|
||||
$channels[] = 'console';
|
||||
$roles = ['team:' . $project->getAttribute('teamId')];
|
||||
|
||||
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -10,6 +10,7 @@ use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Exception;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
||||
abstract class Migration
|
||||
@@ -63,15 +64,15 @@ abstract class Migration
|
||||
Authorization::setDefaultStatus(false);
|
||||
$this->collections = array_merge([
|
||||
'_metadata' => [
|
||||
'$id' => '_metadata',
|
||||
'$id' => ID::custom('_metadata'),
|
||||
'$collection' => Database::METADATA
|
||||
],
|
||||
'audit' => [
|
||||
'$id' => 'audit',
|
||||
'$id' => ID::custom('audit'),
|
||||
'$collection' => Database::METADATA
|
||||
],
|
||||
'abuse' => [
|
||||
'$id' => 'abuse',
|
||||
'$id' => ID::custom('abuse'),
|
||||
'$collection' => Database::METADATA
|
||||
]
|
||||
], Config::getParam('collections', []));
|
||||
|
||||
@@ -158,8 +158,8 @@ class V12 extends Migration
|
||||
|
||||
if (!$this->projectDB->findOne('buckets', [Query::equal('$id', ['default'])])) {
|
||||
$this->projectDB->createDocument('buckets', new Document([
|
||||
'$id' => 'default',
|
||||
'$collection' => 'buckets',
|
||||
'$id' => ID::custom('default'),
|
||||
'$collection' => ID::custom('buckets'),
|
||||
'dateCreated' => \time(),
|
||||
'dateUpdated' => \time(),
|
||||
'name' => 'Default',
|
||||
|
||||
@@ -67,7 +67,7 @@ class V14 extends Migration
|
||||
|
||||
try {
|
||||
$this->projectDB->createDocument('databases', new Document([
|
||||
'$id' => 'default',
|
||||
'$id' => ID::custom('default'),
|
||||
'name' => 'Default',
|
||||
'search' => 'default Default'
|
||||
]));
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Permissions;
|
||||
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Permission;
|
||||
|
||||
class PermissionsProcessor
|
||||
{
|
||||
public static function aggregate(?array $permissions, string $resource): ?array
|
||||
{
|
||||
if (\is_null($permissions)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$aggregates = self::getAggregates($resource);
|
||||
|
||||
foreach ($permissions as $i => $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;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ namespace Appwrite\Specification\Format;
|
||||
use Appwrite\Specification\Format;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Validator;
|
||||
|
||||
class Swagger2 extends Format
|
||||
@@ -334,7 +336,15 @@ class Swagger2 extends Format
|
||||
$node['items'] = [
|
||||
'type' => 'string',
|
||||
];
|
||||
$node['x-example'] = '["role:all"]';
|
||||
$node['x-example'] = '["' . Permission::read(Role::any()) . '"]';
|
||||
break;
|
||||
case 'Utopia\Database\Validator\Roles':
|
||||
$node['type'] = $validator->getType();
|
||||
$node['collectionFormat'] = 'multi';
|
||||
$node['items'] = [
|
||||
'type' => 'string',
|
||||
];
|
||||
$node['x-example'] = '["' . Role::any()->toString() . '"]';
|
||||
break;
|
||||
case 'Appwrite\Auth\Validator\Password':
|
||||
$node['type'] = $validator->getType();
|
||||
|
||||
@@ -278,7 +278,7 @@ class V11 extends Filter
|
||||
$content['rules'] = \array_map(function ($attribute) use ($content) {
|
||||
return [
|
||||
'$id' => $attribute['key'],
|
||||
'$collection' => $content['$id'],
|
||||
'$collection' => ID::custom($content['$id']),
|
||||
'type' => $attribute['type'],
|
||||
'key' => $attribute['key'],
|
||||
'label' => $attribute['key'],
|
||||
|
||||
@@ -12,7 +12,7 @@ abstract class Model
|
||||
public const TYPE_BOOLEAN = 'boolean';
|
||||
public const TYPE_JSON = 'json';
|
||||
public const TYPE_DATETIME = 'datetime';
|
||||
public const TYPE_DATETIME_EXAMPLE = '2020-10-15T06:38:00.000Z';
|
||||
public const TYPE_DATETIME_EXAMPLE = '2020-10-15T06:38:00.000+00:00';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
|
||||
@@ -28,25 +28,18 @@ class Bucket extends Model
|
||||
'default' => '',
|
||||
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||
])
|
||||
->addRule('$read', [
|
||||
->addRule('$permissions', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'File read permissions.',
|
||||
'description' => 'File permissions.',
|
||||
'default' => [],
|
||||
'example' => ['role:all'],
|
||||
'example' => ['read("any")'],
|
||||
'array' => true,
|
||||
])
|
||||
->addRule('$write', [
|
||||
->addRule('fileSecurity', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'File write permissions.',
|
||||
'default' => [],
|
||||
'example' => ['user:608f9da25e7e1'],
|
||||
'array' => true,
|
||||
])
|
||||
->addRule('permission', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Bucket permission model. Possible values: `bucket` or `file`',
|
||||
'description' => 'Whether file-level security is enabled.',
|
||||
'default' => '',
|
||||
'example' => 'file',
|
||||
'example' => true,
|
||||
])
|
||||
->addRule('name', [
|
||||
'type' => self::TYPE_STRING,
|
||||
|
||||
@@ -28,18 +28,11 @@ class Collection extends Model
|
||||
'default' => '',
|
||||
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||
])
|
||||
->addRule('$read', [
|
||||
->addRule('$permissions', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Collection read permissions.',
|
||||
'description' => 'Collection permissions.',
|
||||
'default' => '',
|
||||
'example' => 'role:all',
|
||||
'array' => true
|
||||
])
|
||||
->addRule('$write', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Collection write permissions.',
|
||||
'default' => '',
|
||||
'example' => 'user:608f9da25e7e1',
|
||||
'example' => ['read("any")'],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('databaseId', [
|
||||
@@ -60,11 +53,11 @@ class Collection extends Model
|
||||
'default' => true,
|
||||
'example' => false,
|
||||
])
|
||||
->addRule('permission', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Collection permission model. Possible values: `document` or `collection`',
|
||||
->addRule('documentSecurity', [
|
||||
'type' => self::TYPE_BOOLEAN,
|
||||
'description' => 'Whether document-level permissions are enabled.',
|
||||
'default' => '',
|
||||
'example' => 'document',
|
||||
'example' => true,
|
||||
])
|
||||
->addRule('attributes', [
|
||||
'type' => [
|
||||
|
||||
@@ -54,18 +54,11 @@ class Document extends Any
|
||||
'default' => '',
|
||||
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||
])
|
||||
->addRule('$read', [
|
||||
->addRule('$permissions', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Document read permissions.',
|
||||
'description' => 'Document permissions.',
|
||||
'default' => '',
|
||||
'example' => 'role:all',
|
||||
'array' => true,
|
||||
])
|
||||
->addRule('$write', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Document write permissions.',
|
||||
'default' => '',
|
||||
'example' => 'user:608f9da25e7e1',
|
||||
'example' => ['read("any")'],
|
||||
'array' => true,
|
||||
])
|
||||
;
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class Execution extends Model
|
||||
{
|
||||
@@ -28,11 +29,11 @@ class Execution extends Model
|
||||
'default' => '',
|
||||
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||
])
|
||||
->addRule('$read', [
|
||||
->addRule('$permissions', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Execution read permissions.',
|
||||
'description' => 'Execution roles.',
|
||||
'default' => '',
|
||||
'example' => 'role:all',
|
||||
'example' => [Role::any()->toString()],
|
||||
'array' => true,
|
||||
])
|
||||
->addRule('functionId', [
|
||||
|
||||
@@ -34,18 +34,11 @@ class File extends Model
|
||||
'default' => '',
|
||||
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||
])
|
||||
->addRule('$read', [
|
||||
->addRule('$permissions', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'File read permissions.',
|
||||
'description' => 'File permissions.',
|
||||
'default' => [],
|
||||
'example' => 'role:all',
|
||||
'array' => true,
|
||||
])
|
||||
->addRule('$write', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'File write permissions.',
|
||||
'default' => [],
|
||||
'example' => 'user:608f9da25e7e1',
|
||||
'example' => ['read("any")'],
|
||||
'array' => true,
|
||||
])
|
||||
->addRule('name', [
|
||||
|
||||
@@ -34,7 +34,7 @@ class Func extends Model
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Execution permissions.',
|
||||
'default' => [],
|
||||
'example' => 'role:member',
|
||||
'example' => 'users',
|
||||
'array' => true,
|
||||
])
|
||||
->addRule('name', [
|
||||
|
||||
Reference in New Issue
Block a user