Merge remote-tracking branch 'origin/refactor-permissions-inc-console-fix' into feat-variables-api
This commit is contained in:
commit
b765ad603d
|
@ -39,7 +39,6 @@ use Appwrite\Network\Validator\IP;
|
|||
use Appwrite\Network\Validator\URL;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Database\Validator\Queries as QueriesValidator;
|
||||
use Appwrite\Utopia\Database\Validator\OrderAttributes;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
|
@ -505,11 +504,8 @@ App::post('/v1/databases/:databaseId/collections')
|
|||
|
||||
$collectionId = $collectionId == 'unique()' ? ID::unique() : $collectionId;
|
||||
|
||||
/**
|
||||
* Map aggregate permissions into the multiple permissions they represent,
|
||||
* accounting for the resource type given that some types not allowed specific permissions.
|
||||
*/
|
||||
$permissions = PermissionsProcessor::aggregate($permissions, 'collection');
|
||||
// Map aggregate permissions into the multiple permissions they represent.
|
||||
$permissions = Permission::aggregate($permissions);
|
||||
|
||||
try {
|
||||
$dbForProject->createDocument('database_' . $database->getInternalId(), new Document([
|
||||
|
@ -525,9 +521,9 @@ App::post('/v1/databases/:databaseId/collections')
|
|||
$collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
|
||||
|
||||
$dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
|
||||
} catch (DuplicateException $th) {
|
||||
} catch (DuplicateException) {
|
||||
throw new Exception(Exception::COLLECTION_ALREADY_EXISTS);
|
||||
} catch (LimitException $th) {
|
||||
} catch (LimitException) {
|
||||
throw new Exception(Exception::COLLECTION_LIMIT_EXCEEDED);
|
||||
}
|
||||
|
||||
|
@ -774,10 +770,8 @@ App::put('/v1/databases/:databaseId/collections/:collectionId')
|
|||
|
||||
$permissions ??= $collection->getPermissions() ?? [];
|
||||
|
||||
/**
|
||||
* Map aggregate permissions into the multiple permissions they represent.
|
||||
*/
|
||||
$permissions = PermissionsProcessor::aggregate($permissions, 'collection');
|
||||
// Map aggregate permissions into the multiple permissions they represent.
|
||||
$permissions = Permission::aggregate($permissions);
|
||||
|
||||
$enabled ??= $collection->getAttribute('enabled', true);
|
||||
|
||||
|
@ -1875,7 +1869,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new documents, try update instead');
|
||||
}
|
||||
|
||||
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
|
||||
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
|
@ -1889,25 +1883,21 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
}
|
||||
}
|
||||
|
||||
$validator = new Authorization('create');
|
||||
$validator = new Authorization(Database::PERMISSION_CREATE);
|
||||
if (!$validator->isValid($collection->getCreate())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map aggregate permissions into the multiple permissions they represent,
|
||||
* accounting for the resource type given that some types not allowed specific permissions.
|
||||
*/
|
||||
$permissions = PermissionsProcessor::aggregate($permissions, 'document');
|
||||
$allowedPermissions = [
|
||||
Database::PERMISSION_READ,
|
||||
Database::PERMISSION_UPDATE,
|
||||
Database::PERMISSION_DELETE,
|
||||
];
|
||||
|
||||
/**
|
||||
* Add permissions for current the user for any missing types
|
||||
* from the allowed permissions for this resource type.
|
||||
*/
|
||||
$allowedPermissions = \array_filter(
|
||||
Database::PERMISSIONS,
|
||||
fn ($permission) => $permission !== Database::PERMISSION_CREATE
|
||||
);
|
||||
// Map aggregate permissions to into the set of individual permissions they represent.
|
||||
$permissions = Permission::aggregate($permissions, $allowedPermissions);
|
||||
|
||||
// Add permissions for current the user if none were provided.
|
||||
if (\is_null($permissions)) {
|
||||
$permissions = [];
|
||||
if (!empty($user->getId())) {
|
||||
|
@ -1915,19 +1905,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
$permissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($allowedPermissions as $permission) {
|
||||
/**
|
||||
* If an allowed permission was not passed in the request,
|
||||
* and there is a current user, add it for the current user.
|
||||
*/
|
||||
if (empty(\preg_grep("#^{$permission}\(.+\)$#", $permissions)) && !empty($user->getId())) {
|
||||
$permissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Users can only manage their own roles, API keys and Admin users can manage any
|
||||
// Users can only manage their own roles, API keys and Admin users can manage any
|
||||
$roles = Authorization::getRoles();
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
|
@ -2022,9 +2001,9 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
}
|
||||
|
||||
$documentSecurity = $collection->getAttribute('documentSecurity', false);
|
||||
$validator = new Authorization('read');
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($collection->getRead());
|
||||
if (!$valid && !$documentSecurity) {
|
||||
if (!$documentSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
|
@ -2046,11 +2025,10 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
}
|
||||
|
||||
if (!empty($cursor)) {
|
||||
if ($documentSecurity) {
|
||||
$cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $cursor);
|
||||
} else {
|
||||
$cursorDocument = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $cursor));
|
||||
}
|
||||
$cursorDocument = $documentSecurity
|
||||
? $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $cursor)
|
||||
: Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $cursor));
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Document '{$cursor}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
@ -2068,7 +2046,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
}
|
||||
}
|
||||
|
||||
if ($documentSecurity) {
|
||||
if ($documentSecurity && !$valid) {
|
||||
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $allQueries);
|
||||
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
|
||||
} else {
|
||||
|
@ -2129,25 +2107,22 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
|
|||
}
|
||||
|
||||
$documentSecurity = $collection->getAttribute('documentSecurity', false);
|
||||
$validator = new Authorization('read');
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($collection->getRead());
|
||||
if (!$valid && !$documentSecurity) {
|
||||
if (!$documentSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
if ($documentSecurity && !$valid) {
|
||||
$document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
} else {
|
||||
$document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId));
|
||||
}
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($documentSecurity) {
|
||||
$valid |= $validator->isValid($document->getRead());
|
||||
if (!$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset $collection attribute to remove prefix.
|
||||
*/
|
||||
|
@ -2303,25 +2278,18 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
|
|||
}
|
||||
|
||||
$documentSecurity = $collection->getAttribute('documentSecurity', false);
|
||||
$validator = new Authorization('update');
|
||||
$validator = new Authorization(Database::PERMISSION_UPDATE);
|
||||
$valid = $validator->isValid($collection->getUpdate());
|
||||
if (!$valid && !$documentSecurity) {
|
||||
if (!$documentSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
$document = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId));
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($documentSecurity) {
|
||||
$valid |= $validator->isValid($document->getUpdate());
|
||||
if (!$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
// Users can only manage their own roles, API keys and Admin users can manage any
|
||||
$roles = Authorization::getRoles();
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles) && !\is_null($permissions)) {
|
||||
|
@ -2343,11 +2311,12 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map aggregate permissions into the multiple permissions they represent,
|
||||
* accounting for the resource type given that some types not allowed specific permissions.
|
||||
*/
|
||||
$permissions = PermissionsProcessor::aggregate($permissions, 'document');
|
||||
// Map aggregate permissions into the multiple permissions they represent.
|
||||
$permissions = Permission::aggregate($permissions, [
|
||||
Database::PERMISSION_READ,
|
||||
Database::PERMISSION_UPDATE,
|
||||
Database::PERMISSION_DELETE,
|
||||
]);
|
||||
|
||||
if (\is_null($permissions)) {
|
||||
$permissions = $document->getPermissions() ?? [];
|
||||
|
@ -2360,14 +2329,16 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
|
|||
$data['$permissions'] = $permissions;
|
||||
|
||||
try {
|
||||
$document = $dbForProject->updateDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $document->getId(), new Document($data));
|
||||
if ($documentSecurity && !$valid) {
|
||||
$document = $dbForProject->updateDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $document->getId(), new Document($data));
|
||||
} else {
|
||||
$document = Authorization::skip(fn() => $dbForProject->updateDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $document->getId(), new Document($data)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset $collection attribute to remove prefix.
|
||||
*/
|
||||
$document->setAttribute('$collection', $collectionId);
|
||||
} catch (AuthorizationException) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
} catch (DuplicateException) {
|
||||
throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS);
|
||||
} catch (StructureException $exception) {
|
||||
|
@ -2430,27 +2401,24 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
|||
}
|
||||
|
||||
$documentSecurity = $collection->getAttribute('documentSecurity', false);
|
||||
$validator = new Authorization('delete');
|
||||
$validator = new Authorization(Database::PERMISSION_DELETE);
|
||||
$valid = $validator->isValid($collection->getDelete());
|
||||
if (!$valid && !$documentSecurity) {
|
||||
if (!$documentSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
$document = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId));
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($documentSecurity) {
|
||||
$valid |= $validator->isValid($document->getDelete());
|
||||
if (!$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
if ($documentSecurity && !$valid) {
|
||||
$dbForProject->deleteDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
} else {
|
||||
Authorization::skip(fn() => $dbForProject->deleteDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId));
|
||||
}
|
||||
|
||||
$dbForProject->deleteDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
|
||||
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,6 @@ use Appwrite\Auth\Auth;
|
|||
use Appwrite\ClamAV\Network;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Permissions\PermissionsProcessor;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Appwrite\Stats\Stats;
|
||||
|
@ -73,11 +72,8 @@ App::post('/v1/storage/buckets')
|
|||
|
||||
$bucketId = $bucketId === 'unique()' ? ID::unique() : $bucketId;
|
||||
|
||||
/**
|
||||
* Map aggregate permissions into the multiple permissions they represent,
|
||||
* accounting for the resource type given that some types not allowed specific permissions.
|
||||
*/
|
||||
$permissions = PermissionsProcessor::aggregate($permissions, 'bucket');
|
||||
// Map aggregate permissions into the multiple permissions they represent.
|
||||
$permissions = Permission::aggregate($permissions);
|
||||
|
||||
try {
|
||||
$files = Config::getParam('collections', [])['files'] ?? [];
|
||||
|
@ -265,7 +261,8 @@ App::put('/v1/storage/buckets/:bucketId')
|
|||
* Map aggregate permissions into the multiple permissions they represent,
|
||||
* accounting for the resource type given that some types not allowed specific permissions.
|
||||
*/
|
||||
$permissions = PermissionsProcessor::aggregate($permissions, 'bucket');
|
||||
// Map aggregate permissions into the multiple permissions they represent.
|
||||
$permissions = Permission::aggregate($permissions);
|
||||
|
||||
$bucket = $dbForProject->updateDocument('buckets', $bucket->getId(), $bucket
|
||||
->setAttribute('name', $name)
|
||||
|
@ -367,25 +364,21 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$validator = new Authorization('create');
|
||||
$validator = new Authorization(Database::PERMISSION_CREATE);
|
||||
if (!$validator->isValid($bucket->getCreate())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map aggregate permissions into the multiple permissions they represent,
|
||||
* accounting for the resource type given that some types not allowed specific permissions.
|
||||
*/
|
||||
$permissions = PermissionsProcessor::aggregate($permissions, 'file');
|
||||
$allowedPermissions = [
|
||||
Database::PERMISSION_READ,
|
||||
Database::PERMISSION_UPDATE,
|
||||
Database::PERMISSION_DELETE,
|
||||
];
|
||||
|
||||
/**
|
||||
* Add permissions for current the user for any missing types
|
||||
* from the allowed permissions for this resource type.
|
||||
*/
|
||||
$allowedPermissions = \array_filter(
|
||||
Database::PERMISSIONS,
|
||||
fn ($permission) => $permission !== Database::PERMISSION_CREATE
|
||||
);
|
||||
// Map aggregate permissions to into the set of individual permissions they represent.
|
||||
$permissions = Permission::aggregate($permissions, $allowedPermissions);
|
||||
|
||||
// Add permissions for current the user if none were provided.
|
||||
if (\is_null($permissions)) {
|
||||
$permissions = [];
|
||||
if (!empty($user->getId())) {
|
||||
|
@ -393,16 +386,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
$permissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($allowedPermissions as $permission) {
|
||||
/**
|
||||
* If an allowed permission was not passed in the request,
|
||||
* and there is a current user, add it for the current user.
|
||||
*/
|
||||
if (empty(\preg_grep("#^{$permission}\(.+\)$#", $permissions)) && !empty($user->getId())) {
|
||||
$permissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Users can only manage their own roles, API keys and Admin users can manage any
|
||||
|
@ -426,14 +409,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
}
|
||||
}
|
||||
|
||||
$file = $request->getFiles('file');
|
||||
|
||||
/**
|
||||
* Validators
|
||||
*/
|
||||
$allowedFileExtensions = $bucket->getAttribute('allowedFileExtensions', []);
|
||||
$fileExt = new FileExt($allowedFileExtensions);
|
||||
|
||||
$maximumFileSize = $bucket->getAttribute('maximumFileSize', 0);
|
||||
if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Maximum bucket file size is larger than _APP_STORAGE_LIMIT');
|
||||
|
@ -700,8 +675,10 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
|||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($bucket->getRead())) {
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
if (!$fileSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
|
@ -716,7 +693,9 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
|||
$queries[] = Query::offset($offset);
|
||||
$queries[] = $orderType === Database::ORDER_ASC ? Query::orderAsc('') : Query::orderDesc('');
|
||||
if (!empty($cursor)) {
|
||||
$cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor);
|
||||
$cursorDocument = $fileSecurity
|
||||
? $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor)
|
||||
: Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor));
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "File '{$cursor}' for the 'cursor' value not found.");
|
||||
|
@ -725,7 +704,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
|||
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
|
||||
}
|
||||
|
||||
if ($bucket->getAttribute('fileSecurity', false)) {
|
||||
if ($fileSecurity && !$valid) {
|
||||
$files = $dbForProject->find('bucket_' . $bucket->getInternalId(), \array_merge($filterQueries, $queries));
|
||||
$total = $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
|
||||
} else {
|
||||
|
@ -771,25 +750,22 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
}
|
||||
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization('read');
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
if (!$valid && !$fileSecurity) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
}
|
||||
|
||||
if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($fileSecurity) {
|
||||
$valid = $validator->isValid($file->getRead());
|
||||
if (!$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
$usage
|
||||
->setParam('storage.files.read', 1)
|
||||
->setParam('bucketId', $bucketId)
|
||||
|
@ -846,9 +822,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
|||
}
|
||||
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization('read');
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
if (!$valid && !$fileSecurity) {
|
||||
if (!$fileSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
|
@ -863,19 +839,16 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
|||
$date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT'; // 45 days cache
|
||||
$key = \md5($fileId . $width . $height . $gravity . $quality . $borderWidth . $borderColor . $borderRadius . $opacity . $rotation . $background . $output);
|
||||
|
||||
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
}
|
||||
|
||||
if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($fileSecurity) {
|
||||
$valid |= $validator->isValid($file->getRead());
|
||||
if (!$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
$path = $file->getAttribute('path');
|
||||
$type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
|
||||
$algorithm = $file->getAttribute('algorithm');
|
||||
|
@ -997,25 +970,22 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
|||
}
|
||||
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization('read');
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
if (!$valid && !$fileSecurity) {
|
||||
if (!$fileSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
}
|
||||
|
||||
if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($fileSecurity) {
|
||||
$valid |= $validator->isValid($file->getRead());
|
||||
if (!$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
$path = $file->getAttribute('path', '');
|
||||
|
||||
if (!$deviceFiles->exists($path)) {
|
||||
|
@ -1135,25 +1105,22 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
|||
}
|
||||
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization('read');
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
if (!$valid && !$fileSecurity) {
|
||||
if (!$fileSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
}
|
||||
|
||||
if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($fileSecurity) {
|
||||
$valid |= !$validator->isValid($file->getRead());
|
||||
if (!$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
$mimes = Config::getParam('storage-mimes');
|
||||
|
||||
$path = $file->getAttribute('path', '');
|
||||
|
@ -1287,26 +1254,22 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
}
|
||||
|
||||
$fileSecurity = $bucket->getAttributes('fileSecurity', false);
|
||||
$validator = new Authorization('update');
|
||||
$validator = new Authorization(Database::PERMISSION_UPDATE);
|
||||
$valid = $validator->isValid($bucket->getUpdate());
|
||||
if (!$valid && !$fileSecurity) {
|
||||
if (!$fileSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
}
|
||||
|
||||
if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($fileSecurity) {
|
||||
$valid |= $validator->isValid($file->getUpdate());
|
||||
if (!$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
// Users can only manage their own roles, API keys and Admin users can manage any
|
||||
// Users can only manage their own roles, API keys and Admin users can manage any
|
||||
$roles = Authorization::getRoles();
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
|
@ -1328,11 +1291,12 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map aggregate permissions into the multiple permissions they represent,
|
||||
* accounting for the resource type given that some types not allowed specific permissions.
|
||||
*/
|
||||
$permissions = PermissionsProcessor::aggregate($permissions, 'file');
|
||||
// Map aggregate permissions into the multiple permissions they represent.
|
||||
$permissions = Permission::aggregate($permissions, [
|
||||
Database::PERMISSION_READ,
|
||||
Database::PERMISSION_UPDATE,
|
||||
Database::PERMISSION_DELETE,
|
||||
]);
|
||||
|
||||
$file->setAttribute('$permissions', $permissions);
|
||||
|
||||
|
@ -1384,23 +1348,20 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
$fileSecurity = $bucket->getAttributes('fileSecurity', false);
|
||||
$validator = new Authorization('delete');
|
||||
$valid = $validator->isValid($bucket->getDelete());
|
||||
if (!$valid && !$fileSecurity) {
|
||||
if (!$fileSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
}
|
||||
|
||||
if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($fileSecurity) {
|
||||
$valid |= $validator->isValid($file->getDelete());
|
||||
if (!$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
$deviceDeleted = false;
|
||||
if ($file->getAttribute('chunksTotal') !== $file->getAttribute('chunksUploaded')) {
|
||||
$deviceDeleted = $deviceFiles->abort(
|
||||
|
|
|
@ -65,8 +65,8 @@ App::post('/v1/teams')
|
|||
'$id' => $teamId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team($teamId)),
|
||||
Permission::update(Role::team($teamId), 'owner'),
|
||||
Permission::delete(Role::team($teamId), 'owner'),
|
||||
Permission::update(Role::team($teamId, 'owner')),
|
||||
Permission::delete(Role::team($teamId, 'owner')),
|
||||
],
|
||||
'name' => $name,
|
||||
'total' => ($isPrivilegedUser || $isAppUser) ? 0 : 1,
|
||||
|
@ -811,14 +811,6 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
|||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force document security
|
||||
*/
|
||||
$validator = new Authorization('delete');
|
||||
if (!$validator->isValid($membership->getDelete())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
try {
|
||||
$dbForProject->deleteDocument('memberships', $membership->getId());
|
||||
} catch (AuthorizationException $exception) {
|
||||
|
|
|
@ -173,6 +173,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
'antivirus' => true,
|
||||
'fileSecurity' => true,
|
||||
'$permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
|
|
|
@ -134,7 +134,7 @@ $cli
|
|||
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_CACHE_BY_TIMESTAMP)
|
||||
->setTimestamp(time() - $interval)
|
||||
->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval))
|
||||
->trigger();
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
"utopia-php/cache": "0.6.*",
|
||||
"utopia-php/cli": "0.13.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.22.*",
|
||||
"utopia-php/database": "dev-refactor-permissions as 0.22.0",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
|
|
21
composer.lock
generated
21
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "5c02453019092f62ceec9490ee7f117b",
|
||||
"content-hash": "175fe4abafd8bde4053b91eea905c328",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
@ -2052,16 +2052,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.22.0",
|
||||
"version": "dev-refactor-permissions",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "22c45ae83612e907203b7571cd8e3115ae3ae4c5"
|
||||
"reference": "44dda6914c7be148eb59ce11847386ce39f7b106"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/22c45ae83612e907203b7571cd8e3115ae3ae4c5",
|
||||
"reference": "22c45ae83612e907203b7571cd8e3115ae3ae4c5",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/44dda6914c7be148eb59ce11847386ce39f7b106",
|
||||
"reference": "44dda6914c7be148eb59ce11847386ce39f7b106",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2110,9 +2110,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/0.22.0"
|
||||
"source": "https://github.com/utopia-php/database/tree/refactor-permissions"
|
||||
},
|
||||
"time": "2022-08-17T12:55:37+00:00"
|
||||
"time": "2022-08-24T10:22:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
|
@ -5354,10 +5354,17 @@
|
|||
"version": "9999999-dev",
|
||||
"alias": "0.19.5",
|
||||
"alias_normalized": "0.19.5.0"
|
||||
},
|
||||
{
|
||||
"package": "utopia-php/database",
|
||||
"version": "dev-refactor-permissions",
|
||||
"alias": "0.22.0",
|
||||
"alias_normalized": "0.22.0.0"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"utopia-php/database": 20,
|
||||
"appwrite/sdk-generator": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
<?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 OpenAPI3 extends Format
|
||||
|
@ -338,6 +340,14 @@ class OpenAPI3 extends Format
|
|||
$node['schema']['items'] = [
|
||||
'type' => 'string',
|
||||
];
|
||||
$node['schema']['x-example'] = '["' . Permission::read(Role::any()) . '"]';
|
||||
break;
|
||||
case 'Utopia\Database\Validator\Roles':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['items'] = [
|
||||
'type' => 'string',
|
||||
];
|
||||
$node['schema']['x-example'] = '["' . Role::any()->toString() . '"]';
|
||||
break;
|
||||
case 'Appwrite\Auth\Validator\Password':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
|
|
|
@ -48,13 +48,13 @@ trait DatabasesBase
|
|||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Movies',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $movies['headers']['status-code']);
|
||||
|
@ -101,18 +101,20 @@ trait DatabasesBase
|
|||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(404, $responseCreateDocument['headers']['status-code']);
|
||||
|
||||
$responseListDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(404, $responseListDocument['headers']['status-code']);
|
||||
|
||||
$responseGetDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/someID', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(404, $responseCreateDocument['headers']['status-code']);
|
||||
$this->assertEquals(404, $responseListDocument['headers']['status-code']);
|
||||
$this->assertEquals(404, $responseGetDocument['headers']['status-code']);
|
||||
}
|
||||
|
||||
|
@ -2222,7 +2224,7 @@ trait DatabasesBase
|
|||
$this->assertEquals([], $document['body']['$permissions']);
|
||||
}
|
||||
|
||||
// Updated and Inherit Permissions
|
||||
// Updated Permissions
|
||||
|
||||
$document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -2234,7 +2236,8 @@ trait DatabasesBase
|
|||
'actors' => [],
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id']))
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -2245,8 +2248,11 @@ trait DatabasesBase
|
|||
// This differs from the old permissions model because we don't inherit
|
||||
// existing document permissions on update, unless none were supplied,
|
||||
// so that specific types can be removed if wanted.
|
||||
$this->assertCount(1, $document['body']['$permissions']);
|
||||
$this->assertContains(Permission::read(Role::any()), $document['body']['$permissions']);
|
||||
$this->assertCount(2, $document['body']['$permissions']);
|
||||
$this->assertEquals([
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
], $document['body']['$permissions']);
|
||||
|
||||
$document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -2257,11 +2263,11 @@ trait DatabasesBase
|
|||
$this->assertEquals($document['body']['title'], 'Captain America 2');
|
||||
$this->assertEquals($document['body']['releaseYear'], 1945);
|
||||
|
||||
// This differs from the old permissions model because we don't inherit
|
||||
// existing document permissions on update, unless none were supplied,
|
||||
// so that specific types can be removed if wanted.
|
||||
$this->assertCount(1, $document['body']['$permissions']);
|
||||
$this->assertContains(Permission::read(Role::any()), $document['body']['$permissions']);
|
||||
$this->assertCount(2, $document['body']['$permissions']);
|
||||
$this->assertEquals([
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
], $document['body']['$permissions']);
|
||||
|
||||
// Reset Permissions
|
||||
|
||||
|
@ -2283,10 +2289,18 @@ trait DatabasesBase
|
|||
$this->assertCount(0, $document['body']['$permissions']);
|
||||
$this->assertEquals([], $document['body']['$permissions']);
|
||||
|
||||
// Check user can still read document due to collection permissions of read("any")
|
||||
$document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function testEnforceDocumentPermissions(): void
|
||||
public function testEnforceCollectionAndDocumentPermissions(): void
|
||||
{
|
||||
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -2294,10 +2308,10 @@ trait DatabasesBase
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'EnforceCollectionPermissions',
|
||||
'name' => 'EnforceCollectionAndDocumentPermissions',
|
||||
]);
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$this->assertEquals('EnforceCollectionPermissions', $database['body']['name']);
|
||||
$this->assertEquals('EnforceCollectionAndDocumentPermissions', $database['body']['name']);
|
||||
|
||||
$databaseId = $database['body']['$id'];
|
||||
$user = $this->getUser()['$id'];
|
||||
|
@ -2307,7 +2321,7 @@ trait DatabasesBase
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'enforceCollectionPermissions',
|
||||
'name' => 'enforceCollectionAndDocumentPermissions',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($user)),
|
||||
|
@ -2318,7 +2332,7 @@ trait DatabasesBase
|
|||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertEquals($collection['body']['name'], 'enforceCollectionPermissions');
|
||||
$this->assertEquals($collection['body']['name'], 'enforceCollectionAndDocumentPermissions');
|
||||
$this->assertEquals($collection['body']['documentSecurity'], true);
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
@ -2412,22 +2426,16 @@ trait DatabasesBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
switch ($this->getSide()) {
|
||||
case 'client':
|
||||
$this->assertEquals(2, $documentsUser1['body']['total']);
|
||||
$this->assertCount(2, $documentsUser1['body']['documents']);
|
||||
break;
|
||||
case 'server':
|
||||
$this->assertEquals(3, $documentsUser1['body']['total']);
|
||||
$this->assertCount(3, $documentsUser1['body']['documents']);
|
||||
break;
|
||||
}
|
||||
// Current user has read permission on the collection so can get any document
|
||||
$this->assertEquals(3, $documentsUser1['body']['total']);
|
||||
$this->assertCount(3, $documentsUser1['body']['documents']);
|
||||
|
||||
$document3GetWithCollectionRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document3['body']['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
// Current user has read permission on the collection so can get any document
|
||||
$this->assertEquals(200, $document3GetWithCollectionRead['headers']['status-code']);
|
||||
|
||||
$email = uniqid() . 'user@localhost.test';
|
||||
|
@ -2460,6 +2468,7 @@ trait DatabasesBase
|
|||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2,
|
||||
]);
|
||||
|
||||
// Current user has no collection permissions but has read permission for this document
|
||||
$this->assertEquals(200, $document3GetWithDocumentRead['headers']['status-code']);
|
||||
|
||||
$document2GetFailure = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document2['body']['$id'], [
|
||||
|
@ -2469,7 +2478,8 @@ trait DatabasesBase
|
|||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2,
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $document2GetFailure['headers']['status-code']);
|
||||
// Current user has no collection or document permissions for this document
|
||||
$this->assertEquals(404, $document2GetFailure['headers']['status-code']);
|
||||
|
||||
$documentsUser2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [
|
||||
'origin' => 'http://localhost',
|
||||
|
@ -2478,6 +2488,210 @@ trait DatabasesBase
|
|||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2,
|
||||
]);
|
||||
|
||||
// Current user has no collection permissions but has read permission for one document
|
||||
$this->assertEquals(1, $documentsUser2['body']['total']);
|
||||
$this->assertCount(1, $documentsUser2['body']['documents']);
|
||||
}
|
||||
|
||||
public function testEnforceCollectionPermissions()
|
||||
{
|
||||
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'EnforceCollectionPermissions',
|
||||
]);
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$this->assertEquals('EnforceCollectionPermissions', $database['body']['name']);
|
||||
|
||||
$databaseId = $database['body']['$id'];
|
||||
$user = $this->getUser()['$id'];
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'enforceCollectionPermissions',
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($user)),
|
||||
Permission::create(Role::user($user)),
|
||||
Permission::update(Role::user($user)),
|
||||
Permission::delete(Role::user($user)),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertEquals($collection['body']['name'], 'enforceCollectionPermissions');
|
||||
$this->assertEquals($collection['body']['documentSecurity'], false);
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
sleep(2);
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'attribute',
|
||||
'size' => 64,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $attribute['headers']['status-code'], 202);
|
||||
$this->assertEquals('attribute', $attribute['body']['key']);
|
||||
|
||||
// wait for db to add attribute
|
||||
sleep(2);
|
||||
|
||||
$index = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'key_attribute',
|
||||
'type' => 'key',
|
||||
'attributes' => [$attribute['body']['key']],
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $index['headers']['status-code']);
|
||||
$this->assertEquals('key_attribute', $index['body']['key']);
|
||||
|
||||
// wait for db to add attribute
|
||||
sleep(2);
|
||||
|
||||
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'attribute' => 'one',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($user)),
|
||||
Permission::update(Role::user($user)),
|
||||
Permission::delete(Role::user($user)),
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $document1['headers']['status-code']);
|
||||
|
||||
$document2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'attribute' => 'one',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::update(Role::user($user)),
|
||||
Permission::delete(Role::user($user)),
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $document2['headers']['status-code']);
|
||||
|
||||
$document3 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'attribute' => 'one',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user(ID::custom('other2'))),
|
||||
Permission::update(Role::user(ID::custom('other2'))),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $document3['headers']['status-code']);
|
||||
|
||||
$documentsUser1 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
// Current user has read permission on the collection so can get any document
|
||||
$this->assertEquals(3, $documentsUser1['body']['total']);
|
||||
$this->assertCount(3, $documentsUser1['body']['documents']);
|
||||
|
||||
$document3GetWithCollectionRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document3['body']['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
// Current user has read permission on the collection so can get any document
|
||||
$this->assertEquals(200, $document3GetWithCollectionRead['headers']['status-code']);
|
||||
|
||||
$email = uniqid() . 'user2@localhost.test';
|
||||
$password = 'password';
|
||||
$name = 'User Name';
|
||||
$this->client->call(Client::METHOD_POST, '/account', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'userId' => ID::custom('other2'),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
]);
|
||||
$session2 = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
]);
|
||||
$session2 = $this->client->parseCookie((string)$session2['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
||||
|
||||
$document3GetWithDocumentRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document3['body']['$id'], [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2,
|
||||
]);
|
||||
|
||||
// Current user has no collection permissions and document permissions are disabled
|
||||
$this->assertEquals(401, $document3GetWithDocumentRead['headers']['status-code']);
|
||||
|
||||
$documentsUser2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2,
|
||||
]);
|
||||
|
||||
// Current user has no collection permissions and document permissions are disabled
|
||||
$this->assertEquals(401, $documentsUser2['headers']['status-code']);
|
||||
|
||||
|
||||
// Enable document permissions
|
||||
$collection = $this->client->call(CLient::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $collectionId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'name' => $collection['body']['name'],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$documentsUser2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2,
|
||||
]);
|
||||
|
||||
// Current user has no collection permissions read access to one document
|
||||
$this->assertEquals(1, $documentsUser2['body']['total']);
|
||||
$this->assertCount(1, $documentsUser2['body']['documents']);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use Tests\E2E\Scopes\SideClient;
|
|||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
||||
class DatabasesPermissionsGuestTest extends Scope
|
||||
{
|
||||
|
@ -88,19 +89,19 @@ class DatabasesPermissionsGuestTest extends Scope
|
|||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
Authorization::cleanRoles();
|
||||
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]);
|
||||
|
||||
foreach ($documents['body']['documents'] as $document) {
|
||||
foreach ($document['$permissions'] as $permission) {
|
||||
$permission = Permission::parse($permission);
|
||||
if ($permission->getPermission() != 'read') {
|
||||
continue;
|
||||
}
|
||||
$this->assertEquals($permission->getRole(), Role::any()->toString());
|
||||
}
|
||||
$this->assertEquals(1, $documents['body']['total']);
|
||||
$this->assertEquals($permissions, $documents['body']['documents'][0]['$permissions']);
|
||||
|
||||
foreach ($roles as $role) {
|
||||
Authorization::setRole($role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
namespace Tests\E2E\Services\Databases;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
|
@ -29,16 +29,72 @@ class DatabasesPermissionsMemberTest extends Scope
|
|||
public function permissionsProvider(): array
|
||||
{
|
||||
return [
|
||||
[[Permission::read(Role::any())]],
|
||||
[[Permission::read(Role::users())]],
|
||||
[[Permission::read(Role::user(ID::custom('random')))]],
|
||||
[[Permission::read(Role::user(ID::custom('lorem'))), Permission::update(Role::user('lorem')), Permission::delete(Role::user('lorem'))]],
|
||||
[[Permission::read(Role::user(ID::custom('dolor'))), Permission::update(Role::user('dolor')), Permission::delete(Role::user('dolor'))]],
|
||||
[[Permission::read(Role::user(ID::custom('dolor'))), Permission::read(Role::user('lorem')), Permission::update(Role::user('dolor')), Permission::delete(Role::user('dolor'))]],
|
||||
[[Permission::update(Role::any()), Permission::delete(Role::any())]],
|
||||
[[Permission::read(Role::any()), Permission::update(Role::any()), Permission::delete(Role::any())]],
|
||||
[[Permission::read(Role::users()), Permission::update(Role::users()), Permission::delete(Role::users())]],
|
||||
[[Permission::read(Role::any()), Permission::update(Role::users()), Permission::delete(Role::users())]],
|
||||
[
|
||||
'permissions' => [Permission::read(Role::any())],
|
||||
'any' => 1,
|
||||
'users' => 1,
|
||||
'doconly' => 1,
|
||||
],
|
||||
[
|
||||
'permissions' => [Permission::read(Role::users())],
|
||||
'any' => 2,
|
||||
'users' => 2,
|
||||
'doconly' => 2,
|
||||
],
|
||||
[
|
||||
'permissions' => [Permission::read(Role::user(ID::custom('random')))],
|
||||
'any' => 3,
|
||||
'users' => 3,
|
||||
'doconly' => 2,
|
||||
],
|
||||
[
|
||||
'permissions' => [Permission::read(Role::user(ID::custom('lorem'))), Permission::update(Role::user('lorem')), Permission::delete(Role::user('lorem'))],
|
||||
'any' => 4,
|
||||
'users' => 4,
|
||||
'doconly' => 2,
|
||||
],
|
||||
[
|
||||
'permissions' => [Permission::read(Role::user(ID::custom('dolor'))), Permission::update(Role::user('dolor')), Permission::delete(Role::user('dolor'))],
|
||||
'any' => 5,
|
||||
'users' => 5,
|
||||
'doconly' => 2,
|
||||
],
|
||||
[
|
||||
'permissions' => [Permission::read(Role::user(ID::custom('dolor'))), Permission::read(Role::user('lorem')), Permission::update(Role::user('dolor')), Permission::delete(Role::user('dolor'))],
|
||||
'any' => 6,
|
||||
'users' => 6,
|
||||
'doconly' => 2,
|
||||
],
|
||||
[
|
||||
'permissions' => [Permission::update(Role::any()), Permission::delete(Role::any())],
|
||||
'any' => 7,
|
||||
'users' => 7,
|
||||
'doconly' => 2,
|
||||
],
|
||||
[
|
||||
'permissions' => [Permission::read(Role::any()), Permission::update(Role::any()), Permission::delete(Role::any())],
|
||||
'any' => 8,
|
||||
'users' => 8,
|
||||
'doconly' => 3,
|
||||
],
|
||||
[
|
||||
'permissions' => [Permission::read(Role::any()), Permission::update(Role::users()), Permission::delete(Role::users())],
|
||||
'any' => 9,
|
||||
'users' => 9,
|
||||
'doconly' => 4,
|
||||
],
|
||||
[
|
||||
'permissions' => [Permission::read(Role::user(ID::custom('user1')))],
|
||||
'any' => 10,
|
||||
'users' => 10,
|
||||
'doconly' => 5,
|
||||
],
|
||||
[
|
||||
'permissions' => [Permission::read(Role::user(ID::custom('user1'))), Permission::read(Role::user(ID::custom('user1')))],
|
||||
'any' => 11,
|
||||
'users' => 11,
|
||||
'doconly' => 6,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -74,7 +130,6 @@ class DatabasesPermissionsMemberTest extends Scope
|
|||
'documentSecurity' => true,
|
||||
]);
|
||||
$this->assertEquals(201, $public['headers']['status-code']);
|
||||
|
||||
$this->collections = ['public' => $public['body']['$id']];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $this->collections['public'] . '/attributes/string', $this->getServerHeader(), [
|
||||
|
@ -96,10 +151,25 @@ class DatabasesPermissionsMemberTest extends Scope
|
|||
'documentSecurity' => true,
|
||||
]);
|
||||
$this->assertEquals(201, $private['headers']['status-code']);
|
||||
|
||||
$this->collections['private'] = $private['body']['$id'];
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $this->collections['private'] . '/attributes/string', $this->getServerHeader(), [
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $this->collections['private'] . '/attributes/string', $this->getServerHeader(), [
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
$this->assertEquals(202, $response['headers']['status-code']);
|
||||
|
||||
$doconly = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Document Only Movies',
|
||||
'permissions' => [],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
$this->assertEquals(201, $private['headers']['status-code']);
|
||||
$this->collections['doconly'] = $doconly['body']['$id'];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $this->collections['doconly'] . '/attributes/string', $this->getServerHeader(), [
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
|
@ -118,9 +188,9 @@ class DatabasesPermissionsMemberTest extends Scope
|
|||
/**
|
||||
* Data provider params are passed before test dependencies
|
||||
* @dataProvider permissionsProvider
|
||||
* @depends testSetupDatabase
|
||||
* @depends testSetupDatabase
|
||||
*/
|
||||
public function testReadDocuments($permissions, $data)
|
||||
public function testReadDocuments($permissions, $anyCount, $usersCount, $docOnlyCount, $data)
|
||||
{
|
||||
$users = $data['users'];
|
||||
$collections = $data['collections'];
|
||||
|
@ -144,66 +214,52 @@ class DatabasesPermissionsMemberTest extends Scope
|
|||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collections['doconly'] . '/documents', $this->getServerHeader(), [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Lorem',
|
||||
],
|
||||
'permissions' => $permissions
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Check "any" collection
|
||||
* Check "any" permission collection
|
||||
*/
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collections['public'] . '/documents', [
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collections['public'] . '/documents', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users['user1']['session'],
|
||||
]);
|
||||
|
||||
foreach ($documents['body']['documents'] as $document) {
|
||||
$hasPermissions = \array_reduce([
|
||||
Role::any()->toString(),
|
||||
Role::users()->toString(),
|
||||
Role::user($users['user1']['$id'])->toString(),
|
||||
], function (bool $carry, string $role) use ($document) {
|
||||
if ($carry) {
|
||||
return true;
|
||||
}
|
||||
foreach ($document['$permissions'] as $permission) {
|
||||
$permission = Permission::parse($permission);
|
||||
if ($permission->getPermission() == 'read' && $permission->getRole() == $role) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}, false);
|
||||
|
||||
$this->assertTrue($hasPermissions);
|
||||
}
|
||||
$this->assertEquals(200, $documents['headers']['status-code']);
|
||||
$this->assertEquals($anyCount, $documents['body']['total']);
|
||||
|
||||
/**
|
||||
* Check role:member collection
|
||||
* Check "users" permission collection
|
||||
*/
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collections['private'] . '/documents', [
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collections['private'] . '/documents', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users['user1']['session'],
|
||||
]);
|
||||
|
||||
foreach ($documents['body']['documents'] as $document) {
|
||||
$hasPermissions = \array_reduce([
|
||||
Role::any()->toString(),
|
||||
Role::users()->toString(),
|
||||
Role::user($users['user1']['$id'])->toString(),
|
||||
], function (bool $carry, string $role) use ($document) {
|
||||
if ($carry) {
|
||||
return true;
|
||||
}
|
||||
foreach ($document['$permissions'] as $permission) {
|
||||
$permission = Permission::parse($permission);
|
||||
if ($permission->getPermission() == 'read' && $permission->getRole() == $role) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}, false);
|
||||
$this->assertEquals(200, $documents['headers']['status-code']);
|
||||
$this->assertEquals($usersCount, $documents['body']['total']);
|
||||
|
||||
$this->assertTrue($hasPermissions);
|
||||
}
|
||||
/**
|
||||
* Check "user:user1" document only permission collection
|
||||
*/
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collections['doconly'] . '/documents', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users['user1']['session'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $documents['headers']['status-code']);
|
||||
$this->assertEquals($docOnlyCount, $documents['body']['total']);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue