Merge branch 'refactor-permissions-inc-console-fix' of https://github.com/appwrite/appwrite into feat-improve-keys

This commit is contained in:
Christy Jacob
2022-08-19 09:32:22 +00:00
106 changed files with 3356 additions and 2590 deletions
+17 -16
View File
@@ -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();
}
}
}
+16 -10
View File
@@ -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;
+4 -3
View File
@@ -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', []));
+2 -2
View File
@@ -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',
+1 -1
View File
@@ -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;
}
}
+11 -1
View File
@@ -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();
+1 -1
View File
@@ -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'],
+1 -1
View File
@@ -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
+6 -13
View File
@@ -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', [
+3 -10
View File
@@ -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', [
+1 -1
View File
@@ -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', [