Merge branch 'refactor-permissions-inc-console-fix' of https://github.com/appwrite/appwrite into feat-improve-keys
This commit is contained in:
commit
2a750eaa73
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -9,4 +9,5 @@
|
|||
.php_cs.cache
|
||||
debug/
|
||||
app/sdks
|
||||
dev/yasd_init.php
|
||||
dev/yasd_init.php
|
||||
.phpunit.result.cache
|
||||
|
|
|
@ -142,7 +142,7 @@ Learn more at our [Technology Stack](#technology-stack) section.
|
|||
|
||||
##### Security
|
||||
|
||||
- [Appwrite Auth and ACL](https://github.com/appwrite/appwrite/blob/0.7.x/docs/specs/authentication.drawio.svg)
|
||||
- [Appwrite Auth and ACL](https://github.com/appwrite/appwrite/blob/master/docs/specs/authentication.drawio.svg)
|
||||
- [OAuth](https://en.wikipedia.org/wiki/OAuth)
|
||||
- [Encryption](https://medium.com/searchencrypt/what-is-encryption-how-does-it-work-e8f20e340537#:~:text=Encryption%20is%20a%20process%20that,%2C%20or%20decrypt%2C%20the%20information.)
|
||||
- [Hashing](https://searchsqlserver.techtarget.com/definition/hashing#:~:text=Hashing%20is%20the%20transformation%20of,it%20using%20the%20original%20value.)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -52,8 +52,8 @@ $admins = [
|
|||
];
|
||||
|
||||
return [
|
||||
Auth::USER_ROLE_GUEST => [
|
||||
'label' => 'Guest',
|
||||
Auth::USER_ROLE_GUESTS => [
|
||||
'label' => 'Guests',
|
||||
'scopes' => [
|
||||
'public',
|
||||
'home',
|
||||
|
@ -64,8 +64,8 @@ return [
|
|||
'avatars.read',
|
||||
],
|
||||
],
|
||||
Auth::USER_ROLE_MEMBER => [
|
||||
'label' => 'Member',
|
||||
Auth::USER_ROLE_USERS => [
|
||||
'label' => 'Users',
|
||||
'scopes' => \array_merge($member, []),
|
||||
],
|
||||
Auth::USER_ROLE_ADMIN => [
|
||||
|
@ -80,8 +80,8 @@ return [
|
|||
'label' => 'Owner',
|
||||
'scopes' => \array_merge($member, $admins, []),
|
||||
],
|
||||
Auth::USER_ROLE_APP => [
|
||||
'label' => 'Application',
|
||||
Auth::USER_ROLE_APPS => [
|
||||
'label' => 'Applications',
|
||||
'scopes' => ['health.read'],
|
||||
],
|
||||
];
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -29,7 +29,10 @@ use Utopia\Database\Database;
|
|||
use Utopia\Database\Document;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Locale\Locale;
|
||||
|
@ -95,11 +98,14 @@ App::post('/v1/account')
|
|||
}
|
||||
|
||||
try {
|
||||
$userId = $userId == 'unique()' ? $dbForProject->getId() : $userId;
|
||||
$userId = $userId == 'unique()' ? ID::unique() : $userId;
|
||||
$user = Authorization::skip(fn() => $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:' . $userId],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
|
@ -120,9 +126,9 @@ App::post('/v1/account')
|
|||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
Authorization::unsetRole('role:' . Auth::USER_ROLE_GUEST);
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
Authorization::setRole('role:' . Auth::USER_ROLE_MEMBER);
|
||||
Authorization::unsetRole(Role::guests()->toString());
|
||||
Authorization::setRole(Role::user($user->getId())->toString());
|
||||
Authorization::setRole(Role::users()->toString());
|
||||
|
||||
$usage->setParam('users.create', 1);
|
||||
$events->setParam('userId', $user->getId());
|
||||
|
@ -181,7 +187,7 @@ App::post('/v1/account/sessions/email')
|
|||
$secret = Auth::tokenGenerator();
|
||||
$session = new Document(array_merge(
|
||||
[
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $profile->getId(),
|
||||
'userInternalId' => $profile->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
|
@ -197,7 +203,7 @@ App::post('/v1/account/sessions/email')
|
|||
$detector->getDevice()
|
||||
));
|
||||
|
||||
Authorization::setRole('user:' . $profile->getId());
|
||||
Authorization::setRole(Role::user($profile->getId())->toString());
|
||||
|
||||
// Re-hash if not using recommended algo
|
||||
if ($profile->getAttribute('hash') !== Auth::DEFAULT_ALGO) {
|
||||
|
@ -208,12 +214,15 @@ App::post('/v1/account/sessions/email')
|
|||
$dbForProject->updateDocument('users', $profile->getId(), $profile);
|
||||
}
|
||||
|
||||
$session = $dbForProject->createDocument('sessions', $session
|
||||
->setAttribute('$read', ['user:' . $profile->getId()])
|
||||
->setAttribute('$write', ['user:' . $profile->getId()]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $profile->getId());
|
||||
|
||||
$session = $dbForProject->createDocument('sessions', $session->setAttribute('$permissions', [
|
||||
Permission::read(Role::user($profile->getId())),
|
||||
Permission::update(Role::user($profile->getId())),
|
||||
Permission::delete(Role::user($profile->getId())),
|
||||
]));
|
||||
|
||||
|
||||
if (!Config::getParam('domainVerification')) {
|
||||
$response
|
||||
->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($profile->getId(), $secret)]))
|
||||
|
@ -481,11 +490,14 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
}
|
||||
|
||||
try {
|
||||
$userId = $dbForProject->getId();
|
||||
$userId = ID::unique();
|
||||
$user = Authorization::skip(fn() => $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:' . $userId],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => true,
|
||||
'status' => true, // Email should already be authenticated by OAuth2 provider
|
||||
|
@ -519,7 +531,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_LOGIN_LONG);
|
||||
|
||||
$session = new Document(array_merge([
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => $provider,
|
||||
|
@ -547,13 +559,15 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
->setAttribute('status', true)
|
||||
;
|
||||
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
Authorization::setRole(Role::user($user->getId())->toString());
|
||||
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
$session = $dbForProject->createDocument('sessions', $session
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
$session = $dbForProject->createDocument('sessions', $session->setAttribute('$permissions', [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
|
@ -643,12 +657,15 @@ App::post('/v1/account/sessions/magic-url')
|
|||
}
|
||||
}
|
||||
|
||||
$userId = $userId == 'unique()' ? $dbForProject->getId() : $userId;
|
||||
$userId = $userId == 'unique()' ? ID::unique() : $userId;
|
||||
|
||||
$user = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:' . $userId],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
|
@ -670,7 +687,7 @@ App::post('/v1/account/sessions/magic-url')
|
|||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
|
||||
$token = new Document([
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_MAGIC_URL,
|
||||
|
@ -680,11 +697,14 @@ App::post('/v1/account/sessions/magic-url')
|
|||
'ip' => $request->getIP(),
|
||||
]);
|
||||
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
Authorization::setRole(Role::user($user->getId())->toString());
|
||||
|
||||
$token = $dbForProject->createDocument('tokens', $token
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
->setAttribute('$permissions', [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
|
@ -767,7 +787,7 @@ App::put('/v1/account/sessions/magic-url')
|
|||
|
||||
$session = new Document(array_merge(
|
||||
[
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_MAGIC_URL,
|
||||
|
@ -782,11 +802,14 @@ App::put('/v1/account/sessions/magic-url')
|
|||
$detector->getDevice()
|
||||
));
|
||||
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
Authorization::setRole(Role::user($user->getId())->toString());
|
||||
|
||||
$session = $dbForProject->createDocument('sessions', $session
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
->setAttribute('$permissions', [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
|
@ -881,12 +904,15 @@ App::post('/v1/account/sessions/phone')
|
|||
}
|
||||
}
|
||||
|
||||
$userId = $userId == 'unique()' ? $dbForProject->getId() : $userId;
|
||||
$userId = $userId == 'unique()' ? ID::unique() : $userId;
|
||||
|
||||
$user = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:' . $userId],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
],
|
||||
'email' => null,
|
||||
'phone' => $phone,
|
||||
'emailVerification' => false,
|
||||
|
@ -908,7 +934,7 @@ App::post('/v1/account/sessions/phone')
|
|||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_PHONE);
|
||||
|
||||
$token = new Document([
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_PHONE,
|
||||
|
@ -918,11 +944,14 @@ App::post('/v1/account/sessions/phone')
|
|||
'ip' => $request->getIP(),
|
||||
]);
|
||||
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
Authorization::setRole(Role::user($user->getId())->toString());
|
||||
|
||||
$token = $dbForProject->createDocument('tokens', $token
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
->setAttribute('$permissions', [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
|
@ -990,7 +1019,7 @@ App::put('/v1/account/sessions/phone')
|
|||
|
||||
$session = new Document(array_merge(
|
||||
[
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_PHONE,
|
||||
|
@ -1005,11 +1034,14 @@ App::put('/v1/account/sessions/phone')
|
|||
$detector->getDevice()
|
||||
));
|
||||
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
Authorization::setRole(Role::user($user->getId())->toString());
|
||||
|
||||
$session = $dbForProject->createDocument('sessions', $session
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
->setAttribute('$permissions', [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
|
@ -1103,11 +1135,14 @@ App::post('/v1/account/sessions/anonymous')
|
|||
}
|
||||
}
|
||||
|
||||
$userId = $dbForProject->getId();
|
||||
$userId = ID::unique();
|
||||
$user = Authorization::skip(fn() => $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:' . $userId],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
],
|
||||
'email' => null,
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
|
@ -1134,7 +1169,7 @@ App::post('/v1/account/sessions/anonymous')
|
|||
|
||||
$session = new Document(array_merge(
|
||||
[
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_ANONYMOUS,
|
||||
|
@ -1149,11 +1184,13 @@ App::post('/v1/account/sessions/anonymous')
|
|||
$detector->getDevice()
|
||||
));
|
||||
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
Authorization::setRole(Role::user($user->getId())->toString());
|
||||
|
||||
$session = $dbForProject->createDocument('sessions', $session
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
$session = $dbForProject->createDocument('sessions', $session-> setAttribute('$permissions', [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
|
@ -1468,7 +1505,7 @@ App::patch('/v1/account/password')
|
|||
->action(function (string $password, string $oldPassword, Response $response, Document $user, Database $dbForProject, Stats $usage, Event $events) {
|
||||
|
||||
// Check old password only if its an existing user.
|
||||
if ($user->getAttribute('passwordUpdate') !== 0 && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))) { // Double check user password
|
||||
if ($user->getAttribute('passwordUpdate') !== null && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))) { // Double check user password
|
||||
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
|
||||
}
|
||||
|
||||
|
@ -1930,7 +1967,7 @@ App::post('/v1/account/recovery')
|
|||
|
||||
$secret = Auth::tokenGenerator();
|
||||
$recovery = new Document([
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $profile->getId(),
|
||||
'userInternalId' => $profile->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
|
@ -1940,11 +1977,14 @@ App::post('/v1/account/recovery')
|
|||
'ip' => $request->getIP(),
|
||||
]);
|
||||
|
||||
Authorization::setRole('user:' . $profile->getId());
|
||||
Authorization::setRole(Role::user($profile->getId())->toString());
|
||||
|
||||
$recovery = $dbForProject->createDocument('tokens', $recovery
|
||||
->setAttribute('$read', ['user:' . $profile->getId()])
|
||||
->setAttribute('$write', ['user:' . $profile->getId()]));
|
||||
->setAttribute('$permissions', [
|
||||
Permission::read(Role::user($profile->getId())),
|
||||
Permission::update(Role::user($profile->getId())),
|
||||
Permission::delete(Role::user($profile->getId())),
|
||||
]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $profile->getId());
|
||||
|
||||
|
@ -2022,7 +2062,7 @@ App::put('/v1/account/recovery')
|
|||
throw new Exception(Exception::USER_INVALID_TOKEN);
|
||||
}
|
||||
|
||||
Authorization::setRole('user:' . $profile->getId());
|
||||
Authorization::setRole(Role::user($profile->getId())->toString());
|
||||
|
||||
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile
|
||||
->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS))
|
||||
|
@ -2088,7 +2128,7 @@ App::post('/v1/account/verification')
|
|||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
|
||||
$verification = new Document([
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_VERIFICATION,
|
||||
|
@ -2098,11 +2138,14 @@ App::post('/v1/account/verification')
|
|||
'ip' => $request->getIP(),
|
||||
]);
|
||||
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
Authorization::setRole(Role::user($user->getId())->toString());
|
||||
|
||||
$verification = $dbForProject->createDocument('tokens', $verification
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
->setAttribute('$permissions', [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
|
@ -2174,7 +2217,7 @@ App::put('/v1/account/verification')
|
|||
throw new Exception(Exception::USER_INVALID_TOKEN);
|
||||
}
|
||||
|
||||
Authorization::setRole('user:' . $profile->getId());
|
||||
Authorization::setRole(Role::user($profile->getId())->toString());
|
||||
|
||||
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true));
|
||||
|
||||
|
@ -2237,7 +2280,7 @@ App::post('/v1/account/verification/phone')
|
|||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
|
||||
$verification = new Document([
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_PHONE,
|
||||
|
@ -2247,11 +2290,14 @@ App::post('/v1/account/verification/phone')
|
|||
'ip' => $request->getIP(),
|
||||
]);
|
||||
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
Authorization::setRole(Role::user($user->getId())->toString());
|
||||
|
||||
$verification = $dbForProject->createDocument('tokens', $verification
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
->setAttribute('$permissions', [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
|
@ -2315,7 +2361,7 @@ App::put('/v1/account/verification/phone')
|
|||
throw new Exception(Exception::USER_INVALID_TOKEN);
|
||||
}
|
||||
|
||||
Authorization::setRole('user:' . $profile->getId());
|
||||
Authorization::setRole(Role::user($profile->getId())->toString());
|
||||
|
||||
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('phoneVerification', true));
|
||||
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Permissions\PermissionsProcessor;
|
||||
use Utopia\App;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Database\Validator\DatetimeValidator;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\FloatValidator;
|
||||
use Utopia\Validator\Integer;
|
||||
|
@ -94,7 +98,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att
|
|||
|
||||
try {
|
||||
$attribute = new Document([
|
||||
'$id' => $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key,
|
||||
'$id' => ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key),
|
||||
'key' => $key,
|
||||
'databaseInternalId' => $db->getInternalId(),
|
||||
'databaseId' => $db->getId(),
|
||||
|
@ -168,7 +172,7 @@ App::post('/v1/databases')
|
|||
->inject('events')
|
||||
->action(function (string $databaseId, string $name, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
|
||||
$databaseId = $databaseId == 'unique()' ? $dbForProject->getId() : $databaseId;
|
||||
$databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId;
|
||||
|
||||
try {
|
||||
$dbForProject->createDocument('databases', new Document([
|
||||
|
@ -340,7 +344,7 @@ App::get('/v1/databases/:databaseId/logs')
|
|||
|
||||
$output[$i] = new Document([
|
||||
'event' => $log['event'],
|
||||
'userId' => $log['userId'],
|
||||
'userId' => ID::custom($log['userId']),
|
||||
'userEmail' => $log['data']['userEmail'] ?? null,
|
||||
'userName' => $log['data']['userName'] ?? null,
|
||||
'mode' => $log['data']['mode'] ?? null,
|
||||
|
@ -485,14 +489,13 @@ App::post('/v1/databases/:databaseId/collections')
|
|||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.')
|
||||
->param('permission', null, new WhiteList(['document', 'collection']), 'Specifies the permissions model used in this collection, which accepts either \'collection\' or \'document\'. For \'collection\' level permission, the permissions specified in read and write params are applied to all documents in the collection. For \'document\' level permissions, read and write permissions are specified in each document. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
|
||||
->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
|
||||
->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
|
||||
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
|
||||
->param('documentSecurity', false, new Boolean(true), 'Specifies the permissions model used in this collection, which accepts either \'collection\' or \'document\'. For \'collection\' level permission, the permissions specified in read and write params are applied to all documents in the collection. For \'document\' level permissions, read and write permissions are specified in each document. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function (string $databaseId, string $collectionId, string $name, ?string $permission, ?array $read, ?array $write, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
|
||||
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
|
@ -500,16 +503,21 @@ App::post('/v1/databases/:databaseId/collections')
|
|||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$collectionId = $collectionId == 'unique()' ? $dbForProject->getId() : $collectionId;
|
||||
$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');
|
||||
|
||||
try {
|
||||
$dbForProject->createDocument('database_' . $database->getInternalId(), new Document([
|
||||
'$id' => $collectionId,
|
||||
'databaseInternalId' => $database->getInternalId(),
|
||||
'databaseId' => $databaseId,
|
||||
'$read' => $read ?? [], // Collection permissions for collection documents (based on permission model)
|
||||
'$write' => $write ?? [], // Collection permissions for collection documents (based on permission model)
|
||||
'permission' => $permission, // Permissions model type (document vs collection)
|
||||
'$permissions' => $permissions ?? [],
|
||||
'documentSecurity' => $documentSecurity,
|
||||
'enabled' => true,
|
||||
'name' => $name,
|
||||
'search' => implode(' ', [$collectionId, $name]),
|
||||
|
@ -583,7 +591,9 @@ App::get('/v1/databases/:databaseId/collections')
|
|||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Collection '{$cursor}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
||||
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
|
||||
$queries[] = $cursorDirection === Database::CURSOR_AFTER
|
||||
? Query::cursorAfter($cursorDocument)
|
||||
: Query::cursorBefore($cursorDocument);
|
||||
}
|
||||
|
||||
$usage
|
||||
|
@ -620,6 +630,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId')
|
|||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
|
@ -740,40 +751,44 @@ App::put('/v1/databases/:databaseId/collections/:collectionId')
|
|||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID.')
|
||||
->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.')
|
||||
->param('permission', null, new WhiteList(['document', 'collection']), 'Permissions type model to use for reading documents in this collection. You can use collection-level permission set once on the collection using the `read` and `write` params, or you can set document-level permission where each document read and write params will decide who has access to read and write to each document individually. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
|
||||
->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with permissions. By default no user is granted with any permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('documentSecurity', false, new Boolean(true), 'Whether to enable document-level permission where each document\'s permissions parameter will decide who has access to each file individually. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('enabled', true, new Boolean(), 'Is collection enabled?', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function (string $databaseId, string $collectionId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
|
||||
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception(Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$read ??= $collection->getRead() ?? []; // By default inherit read permissions
|
||||
$write ??= $collection->getWrite() ?? []; // By default inherit write permissions
|
||||
$permissions ??= $collection->getPermissions() ?? [];
|
||||
|
||||
/**
|
||||
* Map aggregate permissions into the multiple permissions they represent.
|
||||
*/
|
||||
$permissions = PermissionsProcessor::aggregate($permissions, 'collection');
|
||||
|
||||
$enabled ??= $collection->getAttribute('enabled', true);
|
||||
|
||||
try {
|
||||
$collection = $dbForProject->updateDocument('database_' . $database->getInternalId(), $collectionId, $collection
|
||||
->setAttribute('$write', $write)
|
||||
->setAttribute('$read', $read)
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('permission', $permission)
|
||||
->setAttribute('$permissions', $permissions)
|
||||
->setAttribute('documentSecurity', $documentSecurity)
|
||||
->setAttribute('enabled', $enabled)
|
||||
->setAttribute('search', implode(' ', [$collectionId, $name])));
|
||||
} catch (AuthorizationException $exception) {
|
||||
} catch (AuthorizationException) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
} catch (StructureException $exception) {
|
||||
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Bad structure. ' . $exception->getMessage());
|
||||
|
@ -1619,7 +1634,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
|
|||
|
||||
try {
|
||||
$index = $dbForProject->createDocument('indexes', new Document([
|
||||
'$id' => $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key,
|
||||
'$id' => ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key),
|
||||
'key' => $key,
|
||||
'status' => 'processing', // processing, available, failed, deleting, stuck
|
||||
'databaseInternalId' => $db->getInternalId(),
|
||||
|
@ -1841,21 +1856,15 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
->param('documentId', '', new CustomId(), 'Document ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection). Make sure to define attributes before creating documents.')
|
||||
->param('data', [], new JSON(), 'Document data as JSON object.')
|
||||
->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE]), 'An array of strings with permissions. By default no user is granted with any permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('user')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->inject('mode')
|
||||
->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $read, ?array $write, Response $response, Database $dbForProject, Document $user, Stats $usage, Event $events, string $mode) {
|
||||
->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Stats $usage, Event $events, string $mode) {
|
||||
|
||||
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
||||
|
||||
if (empty($data)) {
|
||||
|
@ -1866,11 +1875,12 @@ 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');
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
|
||||
*
|
||||
* @var Document $collection
|
||||
*/
|
||||
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId));
|
||||
|
||||
if ($collection->isEmpty() || !$collection->getAttribute('enabled')) {
|
||||
|
@ -1879,42 +1889,72 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
}
|
||||
}
|
||||
|
||||
// Check collection permissions when enforced
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('write');
|
||||
if (!$validator->isValid($collection->getWrite())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
$validator = new Authorization('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');
|
||||
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
if (\is_null($permissions)) {
|
||||
$permissions = [];
|
||||
if (!empty($user->getId())) {
|
||||
foreach ($allowedPermissions as $permission) {
|
||||
$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)) {
|
||||
foreach (Database::PERMISSIONS as $type) {
|
||||
foreach ($permissions as $permission) {
|
||||
$permission = Permission::parse($permission);
|
||||
if ($permission->getPermission() != $type) {
|
||||
continue;
|
||||
}
|
||||
$role = (new Role(
|
||||
$permission->getRole(),
|
||||
$permission->getIdentifier(),
|
||||
$permission->getDimension()
|
||||
))->toString();
|
||||
if (!Authorization::isRole($role)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data['$collection'] = $collection->getId(); // Adding this param to make API easier for developers
|
||||
$data['$id'] = $documentId == 'unique()' ? $dbForProject->getId() : $documentId;
|
||||
$data['$read'] = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? []; // By default set read permissions for user
|
||||
$data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $write ?? []; // By default set write permissions for user
|
||||
|
||||
// Users can only add their roles to documents, API keys and Admin users can add any
|
||||
$roles = Authorization::getRoles();
|
||||
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
foreach ($data['$read'] as $read) {
|
||||
if (!Authorization::isRole($read)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')');
|
||||
}
|
||||
}
|
||||
foreach ($data['$write'] as $write) {
|
||||
if (!Authorization::isRole($write)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
$data['$id'] = $documentId == 'unique()' ? ID::unique() : $documentId;
|
||||
$data['$permissions'] = $permissions;
|
||||
|
||||
try {
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document $document */
|
||||
$document = Authorization::skip(fn() => $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), new Document($data)));
|
||||
} else {
|
||||
$document = $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), new Document($data));
|
||||
}
|
||||
$document = $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), new Document($data));
|
||||
$document->setAttribute('$collection', $collectionId);
|
||||
} catch (StructureException $exception) {
|
||||
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage());
|
||||
|
@ -1972,11 +2012,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
/**
|
||||
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
|
||||
*
|
||||
* @var Utopia\Database\Document $collection
|
||||
*/
|
||||
|
||||
$collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId));
|
||||
|
||||
if ($collection->isEmpty() || !$collection->getAttribute('enabled')) {
|
||||
|
@ -1985,12 +2021,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
}
|
||||
}
|
||||
|
||||
// Check collection permissions when enforced
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($collection->getRead())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
$documentSecurity = $collection->getAttribute('documentSecurity', false);
|
||||
$validator = new Authorization('read');
|
||||
$valid = $validator->isValid($collection->getRead());
|
||||
if (!$valid && !$documentSecurity) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$filterQueries = \array_map(function ($query) {
|
||||
|
@ -2011,10 +2046,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
}
|
||||
|
||||
if (!empty($cursor)) {
|
||||
$cursorDocument = $collection->getAttribute('permission') === 'collection'
|
||||
? Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $cursor))
|
||||
: $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $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));
|
||||
}
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Document '{$cursor}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
@ -2032,13 +2068,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
}
|
||||
}
|
||||
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document[] $documents */
|
||||
$documents = Authorization::skip(fn () => $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $allQueries));
|
||||
$total = Authorization::skip(fn () => $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
|
||||
} else {
|
||||
if ($documentSecurity) {
|
||||
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $allQueries);
|
||||
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
|
||||
} else {
|
||||
$documents = Authorization::skip(fn () => $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $allQueries));
|
||||
$total = Authorization::skip(fn () => $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2084,9 +2119,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
|
|||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
/**
|
||||
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
|
||||
*/
|
||||
|
||||
$collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId));
|
||||
|
||||
if ($collection->isEmpty() || !$collection->getAttribute('enabled')) {
|
||||
|
@ -2095,19 +2128,24 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
|
|||
}
|
||||
}
|
||||
|
||||
// Check collection permissions when enforced
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($collection->getRead())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
$documentSecurity = $collection->getAttribute('documentSecurity', false);
|
||||
$validator = new Authorization('read');
|
||||
$valid = $validator->isValid($collection->getRead());
|
||||
if (!$valid && !$documentSecurity) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document $document */
|
||||
$document = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId));
|
||||
} else {
|
||||
$document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
$document = $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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2115,10 +2153,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
|
|||
*/
|
||||
$document->setAttribute('$collection', $collectionId);
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$usage
|
||||
->setParam('databases.documents.read', 1)
|
||||
->setParam('databaseId', $databaseId)
|
||||
|
@ -2240,23 +2274,26 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
|
|||
->param('collectionId', null, new UID(), 'Collection ID.')
|
||||
->param('documentId', null, new UID(), 'Document ID.')
|
||||
->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true)
|
||||
->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with permissions. By default no user is granted with any permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->inject('mode')
|
||||
->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $read, ?array $write, Response $response, Database $dbForProject, Stats $usage, Event $events, string $mode) {
|
||||
->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Stats $usage, Event $events, string $mode) {
|
||||
|
||||
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
||||
|
||||
if (empty($data) && \is_null($permissions)) {
|
||||
throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD);
|
||||
}
|
||||
|
||||
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
/**
|
||||
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
|
||||
*/
|
||||
|
||||
$collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId));
|
||||
|
||||
if ($collection->isEmpty() || !$collection->getAttribute('enabled')) {
|
||||
|
@ -2265,75 +2302,73 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
|
|||
}
|
||||
}
|
||||
|
||||
// Check collection permissions when enforced
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('write');
|
||||
if (!$validator->isValid($collection->getWrite())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$document = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId));
|
||||
} else {
|
||||
$document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
$documentSecurity = $collection->getAttribute('documentSecurity', false);
|
||||
$validator = new Authorization('update');
|
||||
$valid = $validator->isValid($collection->getUpdate());
|
||||
if (!$valid && !$documentSecurity) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
||||
|
||||
if (empty($data) && empty($read) && empty($write)) {
|
||||
throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD, 'Missing payload or read/write permissions');
|
||||
if ($documentSecurity) {
|
||||
$valid |= $validator->isValid($document->getUpdate());
|
||||
if (!$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
if (!\is_array($data)) {
|
||||
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Data param should be a valid JSON object');
|
||||
// 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)) {
|
||||
foreach (Database::PERMISSIONS as $type) {
|
||||
foreach ($permissions as $permission) {
|
||||
$permission = Permission::parse($permission);
|
||||
if ($permission->getPermission() != $type) {
|
||||
continue;
|
||||
}
|
||||
$role = (new Role(
|
||||
$permission->getRole(),
|
||||
$permission->getIdentifier(),
|
||||
$permission->getDimension()
|
||||
))->toString();
|
||||
if (!Authorization::isRole($role)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
||||
if (\is_null($permissions)) {
|
||||
$permissions = $document->getPermissions() ?? [];
|
||||
}
|
||||
|
||||
$data = \array_merge($document->getArrayCopy(), $data);
|
||||
|
||||
$data['$collection'] = $collection->getId(); // Make sure user don't switch collectionID
|
||||
$data['$createdAt'] = $document->getCreatedAt(); // Make sure user don't switch createdAt
|
||||
$data['$id'] = $document->getId(); // Make sure user don't switch document unique ID
|
||||
$data['$read'] = (is_null($read)) ? ($document->getRead() ?? []) : $read; // By default inherit read permissions
|
||||
$data['$write'] = (is_null($write)) ? ($document->getWrite() ?? []) : $write; // By default inherit write permissions
|
||||
|
||||
// Users can only add their roles to documents, API keys and Admin users can add any
|
||||
$roles = Authorization::getRoles();
|
||||
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
if (!is_null($read)) {
|
||||
foreach ($data['$read'] as $read) {
|
||||
if (!Authorization::isRole($read)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_null($write)) {
|
||||
foreach ($data['$write'] as $write) {
|
||||
if (!Authorization::isRole($write)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$data['$collection'] = $collection->getId(); // Make sure user doesn't switch collectionID
|
||||
$data['$createdAt'] = $document->getCreatedAt(); // Make sure user doesn't switch createdAt
|
||||
$data['$id'] = $document->getId(); // Make sure user doesn't switch document unique ID
|
||||
$data['$permissions'] = $permissions;
|
||||
|
||||
try {
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document $document */
|
||||
$document = Authorization::skip(fn() => $dbForProject->updateDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $document->getId(), new Document($data)));
|
||||
} else {
|
||||
$document = $dbForProject->updateDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $document->getId(), new Document($data));
|
||||
}
|
||||
$document = $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 $exception) {
|
||||
} catch (AuthorizationException) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
} catch (DuplicateException $exception) {
|
||||
} catch (DuplicateException) {
|
||||
throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS);
|
||||
} catch (StructureException $exception) {
|
||||
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage());
|
||||
|
@ -2385,9 +2420,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
|||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
/**
|
||||
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
|
||||
*/
|
||||
|
||||
$collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId));
|
||||
|
||||
if ($collection->isEmpty() || !$collection->getAttribute('enabled')) {
|
||||
|
@ -2396,31 +2429,28 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
|||
}
|
||||
}
|
||||
|
||||
// Check collection permissions when enforced
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('write');
|
||||
if (!$validator->isValid($collection->getWrite())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
$documentSecurity = $collection->getAttribute('documentSecurity', false);
|
||||
$validator = new Authorization('delete');
|
||||
$valid = $validator->isValid($collection->getDelete());
|
||||
if (!$valid && !$documentSecurity) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document $document */
|
||||
$document = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId));
|
||||
} else {
|
||||
$document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
}
|
||||
$document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
Authorization::skip(fn() => $dbForProject->deleteDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId));
|
||||
} else {
|
||||
$dbForProject->deleteDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
if ($documentSecurity) {
|
||||
$valid |= $validator->isValid($document->getDelete());
|
||||
if (!$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
$dbForProject->deleteDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
|
||||
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,9 @@ use Appwrite\Event\Func;
|
|||
use Appwrite\Event\Validator\Event as ValidatorEvent;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Appwrite\Stats\Stats;
|
||||
use Utopia\Storage\Device;
|
||||
|
@ -34,7 +37,7 @@ use Utopia\Config\Config;
|
|||
use Cron\CronExpression;
|
||||
use Executor\Executor;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Validator\Permissions;
|
||||
use Utopia\Database\Validator\Roles;
|
||||
use Utopia\Validator\Boolean;
|
||||
|
||||
include_once __DIR__ . '/../shared/api.php';
|
||||
|
@ -54,7 +57,7 @@ App::post('/v1/functions')
|
|||
->label('sdk.response.model', Response::MODEL_FUNCTION)
|
||||
->param('functionId', '', new CustomId(), 'Function ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
|
||||
->param('execute', [], new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each 64 characters long.')
|
||||
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each 64 characters long.')
|
||||
->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.')
|
||||
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
|
||||
->param('events', [], new ArrayList(new ValidatorEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true)
|
||||
|
@ -65,7 +68,7 @@ App::post('/v1/functions')
|
|||
->inject('events')
|
||||
->action(function (string $functionId, string $name, array $execute, string $runtime, array $vars, array $events, string $schedule, int $timeout, Response $response, Database $dbForProject, Event $eventsInstance) {
|
||||
|
||||
$functionId = ($functionId == 'unique()') ? $dbForProject->getId() : $functionId;
|
||||
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
|
||||
$function = $dbForProject->createDocument('functions', new Document([
|
||||
'$id' => $functionId,
|
||||
'execute' => $execute,
|
||||
|
@ -301,7 +304,7 @@ App::put('/v1/functions/:functionId')
|
|||
->label('sdk.response.model', Response::MODEL_FUNCTION)
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
|
||||
->param('execute', [], new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each 64 characters long.')
|
||||
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each 64 characters long.')
|
||||
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
|
||||
->param('events', [], new ArrayList(new ValidatorEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true)
|
||||
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
|
||||
|
@ -508,7 +511,7 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
}
|
||||
|
||||
$contentRange = $request->getHeader('content-range');
|
||||
$deploymentId = $dbForProject->getId();
|
||||
$deploymentId = ID::unique();
|
||||
$chunk = 1;
|
||||
$chunks = 1;
|
||||
|
||||
|
@ -582,8 +585,11 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
if ($deployment->isEmpty()) {
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'resourceId' => $function->getId(),
|
||||
'resourceType' => 'functions',
|
||||
'entrypoint' => $entrypoint,
|
||||
|
@ -611,8 +617,11 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
if ($deployment->isEmpty()) {
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'resourceId' => $function->getId(),
|
||||
'resourceType' => 'functions',
|
||||
'entrypoint' => $entrypoint,
|
||||
|
@ -869,13 +878,12 @@ App::post('/v1/functions/:functionId/executions')
|
|||
throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription());
|
||||
}
|
||||
|
||||
$executionId = $dbForProject->getId();
|
||||
$executionId = ID::unique();
|
||||
|
||||
/** @var Document $execution */
|
||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', new Document([
|
||||
'$id' => $executionId,
|
||||
'$read' => (!$user->isEmpty()) ? ['user:' . $user->getId()] : [],
|
||||
'$write' => [],
|
||||
'$permissions' => !$user->isEmpty() ? [Permission::read(Role::user($user->getId()))] : [],
|
||||
'functionId' => $function->getId(),
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'trigger' => 'http', // http / schedule / event
|
||||
|
|
|
@ -17,8 +17,11 @@ use Utopia\Audit\Audit;
|
|||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\DatetimeValidator;
|
||||
use Utopia\Database\Validator\UID;
|
||||
|
@ -80,7 +83,7 @@ App::post('/v1/projects')
|
|||
$auths[$method['key'] ?? ''] = true;
|
||||
}
|
||||
|
||||
$projectId = ($projectId == 'unique()') ? $dbForConsole->getId() : $projectId;
|
||||
$projectId = ($projectId == 'unique()') ? ID::unique() : $projectId;
|
||||
|
||||
if ($projectId === 'console') {
|
||||
throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project.");
|
||||
|
@ -88,8 +91,13 @@ App::post('/v1/projects')
|
|||
|
||||
$project = $dbForConsole->createDocument('projects', new Document([
|
||||
'$id' => $projectId,
|
||||
'$read' => ['team:' . $teamId],
|
||||
'$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
],
|
||||
'name' => $name,
|
||||
'teamInternalId' => $team->getInternalId(),
|
||||
'teamId' => $team->getId(),
|
||||
|
@ -102,7 +110,7 @@ App::post('/v1/projects')
|
|||
'legalState' => $legalState,
|
||||
'legalCity' => $legalCity,
|
||||
'legalAddress' => $legalAddress,
|
||||
'legalTaxId' => $legalTaxId,
|
||||
'legalTaxId' => ID::custom($legalTaxId),
|
||||
'services' => new stdClass(),
|
||||
'platforms' => null,
|
||||
'authProviders' => [],
|
||||
|
@ -598,9 +606,12 @@ App::post('/v1/projects/:projectId/webhooks')
|
|||
$security = (bool) filter_var($security, FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
$webhook = new Document([
|
||||
'$id' => $dbForConsole->getId(),
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectId' => $project->getId(),
|
||||
'name' => $name,
|
||||
|
@ -843,9 +854,12 @@ App::post('/v1/projects/:projectId/keys')
|
|||
}
|
||||
|
||||
$key = new Document([
|
||||
'$id' => $dbForConsole->getId(),
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectId' => $project->getId(),
|
||||
'name' => $name,
|
||||
|
@ -1042,9 +1056,12 @@ App::post('/v1/projects/:projectId/platforms')
|
|||
}
|
||||
|
||||
$platform = new Document([
|
||||
'$id' => $dbForConsole->getId(),
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectId' => $project->getId(),
|
||||
'type' => $type,
|
||||
|
@ -1255,9 +1272,12 @@ App::post('/v1/projects/:projectId/domains')
|
|||
$domain = new Domain($domain);
|
||||
|
||||
$domain = new Document([
|
||||
'$id' => $dbForConsole->getId(),
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectId' => $project->getId(),
|
||||
'updated' => DateTime::now(),
|
||||
|
|
|
@ -4,6 +4,7 @@ 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;
|
||||
|
@ -16,7 +17,10 @@ use Utopia\Database\DateTime;
|
|||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Exception\Structure as StructureException;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Permissions;
|
||||
use Utopia\Database\Validator\UID;
|
||||
|
@ -54,9 +58,8 @@ App::post('/v1/storage/buckets')
|
|||
->label('sdk.response.model', Response::MODEL_BUCKET)
|
||||
->param('bucketId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', '', new Text(128), 'Bucket name')
|
||||
->param('permission', null, new WhiteList(['file', 'bucket']), 'Permissions type model to use for reading files in this bucket. You can use bucket-level permission set once on the bucket using the `read` and `write` params, or you can set file-level permission where each file read and write params will decide who has access to read and write to each file individually. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with permissions. By default no user is granted with any permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('fileSecurity', false, new Boolean(true), 'Whether to enable file-level permission where each files permissions parameter will decide who has access to each file individually. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true)
|
||||
->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self-hosted setups you can change the max limit by changing the `_APP_STORAGE_LIMIT` environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true)
|
||||
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
|
||||
|
@ -66,9 +69,16 @@ App::post('/v1/storage/buckets')
|
|||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function (string $bucketId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
->action(function (string $bucketId, string $name, ?array $permissions, string $fileSecurity, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
|
||||
$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');
|
||||
|
||||
$bucketId = $bucketId === 'unique()' ? $dbForProject->getId() : $bucketId;
|
||||
try {
|
||||
$files = Config::getParam('collections', [])['files'] ?? [];
|
||||
if (empty($files)) {
|
||||
|
@ -102,25 +112,24 @@ App::post('/v1/storage/buckets')
|
|||
]);
|
||||
}
|
||||
|
||||
$bucket = $dbForProject->createDocument('buckets', new Document([
|
||||
$dbForProject->createDocument('buckets', new Document([
|
||||
'$id' => $bucketId,
|
||||
'$collection' => 'buckets',
|
||||
'$permissions' => $permissions,
|
||||
'name' => $name,
|
||||
'permission' => $permission,
|
||||
'maximumFileSize' => $maximumFileSize,
|
||||
'allowedFileExtensions' => $allowedFileExtensions,
|
||||
'fileSecurity' => (bool) filter_var($fileSecurity, FILTER_VALIDATE_BOOLEAN),
|
||||
'enabled' => (bool) filter_var($enabled, FILTER_VALIDATE_BOOLEAN),
|
||||
'encryption' => (bool) filter_var($encryption, FILTER_VALIDATE_BOOLEAN),
|
||||
'antivirus' => (bool) filter_var($antivirus, FILTER_VALIDATE_BOOLEAN),
|
||||
'$read' => $read ?? [],
|
||||
'$write' => $write ?? [],
|
||||
'search' => implode(' ', [$bucketId, $name]),
|
||||
]));
|
||||
|
||||
$bucket = $dbForProject->getDocument('buckets', $bucketId);
|
||||
|
||||
$dbForProject->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
|
||||
} catch (Duplicate $th) {
|
||||
} catch (Duplicate) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
|
@ -227,9 +236,8 @@ App::put('/v1/storage/buckets/:bucketId')
|
|||
->label('sdk.response.model', Response::MODEL_BUCKET)
|
||||
->param('bucketId', '', new UID(), 'Bucket unique ID.')
|
||||
->param('name', null, new Text(128), 'Bucket name', false)
|
||||
->param('permission', null, new WhiteList(['file', 'bucket']), 'Permissions type model to use for reading files in this bucket. You can use bucket-level permission set once on the bucket using the `read` and `write` params, or you can set file-level permission where each file read and write params will decide who has access to read and write to each file individually. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with permissions. By default no user is granted with any permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('fileSecurity', false, new Boolean(true), 'Whether to enable file-level permission where each files permissions parameter will decide who has access to each file individually. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true)
|
||||
->param('maximumFileSize', null, new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self hosted version you can change the limit by changing _APP_STORAGE_LIMIT environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true)
|
||||
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
|
||||
|
@ -239,30 +247,34 @@ App::put('/v1/storage/buckets/:bucketId')
|
|||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function (string $bucketId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, ?int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
->action(function (string $bucketId, string $name, ?array $permissions, string $fileSecurity, bool $enabled, ?int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
$bucket = $dbForProject->getDocument('buckets', $bucketId);
|
||||
|
||||
if ($bucket->isEmpty()) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$read ??= $bucket->getAttribute('$read', []); // By default inherit read permissions
|
||||
$write ??= $bucket->getAttribute('$write', []); // By default inherit write permissions
|
||||
$permissions ??= $bucket->getPermissions();
|
||||
$maximumFileSize ??= $bucket->getAttribute('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0));
|
||||
$allowedFileExtensions ??= $bucket->getAttribute('allowedFileExtensions', []);
|
||||
$enabled ??= $bucket->getAttribute('enabled', true);
|
||||
$encryption ??= $bucket->getAttribute('encryption', true);
|
||||
$antivirus ??= $bucket->getAttribute('antivirus', true);
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
||||
$bucket = $dbForProject->updateDocument('buckets', $bucket->getId(), $bucket
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('$read', $read)
|
||||
->setAttribute('$write', $write)
|
||||
->setAttribute('$permissions', $permissions)
|
||||
->setAttribute('maximumFileSize', $maximumFileSize)
|
||||
->setAttribute('allowedFileExtensions', $allowedFileExtensions)
|
||||
->setAttribute('fileSecurity', (bool) filter_var($fileSecurity, FILTER_VALIDATE_BOOLEAN))
|
||||
->setAttribute('enabled', (bool) filter_var($enabled, FILTER_VALIDATE_BOOLEAN))
|
||||
->setAttribute('encryption', (bool) filter_var($encryption, FILTER_VALIDATE_BOOLEAN))
|
||||
->setAttribute('permission', $permission)
|
||||
->setAttribute('antivirus', (bool) filter_var($antivirus, FILTER_VALIDATE_BOOLEAN)));
|
||||
|
||||
$events
|
||||
|
@ -336,8 +348,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new CustomId(), 'File ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('file', [], new File(), 'Binary file.', false)
|
||||
->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE]), 'An array of strings with permissions. By default no user is granted with any permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
@ -348,41 +359,69 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
->inject('deviceFiles')
|
||||
->inject('deviceLocal')
|
||||
->inject('deletes')
|
||||
->action(function (string $bucketId, string $fileId, mixed $file, ?array $read, ?array $write, Request $request, Response $response, Database $dbForProject, Document $user, Stats $usage, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal, Delete $deletes) {
|
||||
->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Stats $usage, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal, Delete $deletes) {
|
||||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Check bucket permissions when enforced
|
||||
$permissionBucket = $bucket->getAttribute('permission') === 'bucket';
|
||||
if ($permissionBucket) {
|
||||
$validator = new Authorization('write');
|
||||
if (!$validator->isValid($bucket->getWrite())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
$validator = new Authorization('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');
|
||||
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
if (\is_null($permissions)) {
|
||||
$permissions = [];
|
||||
if (!empty($user->getId())) {
|
||||
foreach ($allowedPermissions as $permission) {
|
||||
$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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$read = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? []; // By default set read permissions for user
|
||||
$write = (is_null($write) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $write ?? [];
|
||||
|
||||
// Users can only add their roles to files, API keys and Admin users can add 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)) {
|
||||
foreach ($read as $role) {
|
||||
if (!Authorization::isRole($role)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400);
|
||||
}
|
||||
}
|
||||
foreach ($write as $role) {
|
||||
if (!Authorization::isRole($role)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400);
|
||||
foreach (Database::PERMISSIONS as $type) {
|
||||
foreach ($permissions as $permission) {
|
||||
$permission = Permission::parse($permission);
|
||||
if ($permission->getPermission() != $type) {
|
||||
continue;
|
||||
}
|
||||
$role = (new Role(
|
||||
$permission->getRole(),
|
||||
$permission->getIdentifier(),
|
||||
$permission->getDimension()
|
||||
))->toString();
|
||||
if (!Authorization::isRole($role)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -411,7 +450,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
|
||||
|
||||
$contentRange = $request->getHeader('content-range');
|
||||
$fileId = $fileId === 'unique()' ? $dbForProject->getId() : $fileId;
|
||||
$fileId = $fileId === 'unique()' ? ID::unique() : $fileId;
|
||||
$chunk = 1;
|
||||
$chunks = 1;
|
||||
|
||||
|
@ -460,13 +499,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
$path = $deviceFiles->getPath($fileId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
$path = str_ireplace($deviceFiles->getRoot(), $deviceFiles->getRoot() . DIRECTORY_SEPARATOR . $bucket->getId(), $path); // Add bucket id to path after root
|
||||
|
||||
if ($permissionBucket) {
|
||||
$file = Authorization::skip(function () use ($dbForProject, $bucket, $fileId) {
|
||||
return $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
});
|
||||
} else {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
}
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
|
||||
$metadata = ['content_type' => $deviceLocal->getFileMimeType($fileTmpName)];
|
||||
if (!$file->isEmpty()) {
|
||||
|
@ -482,8 +515,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed uploading file');
|
||||
}
|
||||
|
||||
$read = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? [];
|
||||
$write = (is_null($write) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $write ?? [];
|
||||
if ($chunksUploaded === $chunks) {
|
||||
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antivirus', true) && $fileSize <= APP_LIMIT_ANTIVIRUS && App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) === Storage::DEVICE_LOCAL) {
|
||||
$antivirus = new Network(
|
||||
|
@ -537,8 +568,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
if ($file->isEmpty()) {
|
||||
$doc = new Document([
|
||||
'$id' => $fileId,
|
||||
'$read' => $read,
|
||||
'$write' => $write,
|
||||
'$permissions' => $permissions,
|
||||
'bucketId' => $bucket->getId(),
|
||||
'name' => $fileName,
|
||||
'path' => $path,
|
||||
|
@ -557,15 +587,11 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
'search' => implode(' ', [$fileId, $fileName]),
|
||||
'metadata' => $metadata,
|
||||
]);
|
||||
if ($permissionBucket) {
|
||||
$file = Authorization::skip(fn () => $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc));
|
||||
} else {
|
||||
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
|
||||
}
|
||||
|
||||
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
|
||||
} else {
|
||||
$file = $file
|
||||
->setAttribute('$read', $read)
|
||||
->setAttribute('$write', $write)
|
||||
->setAttribute('$permissions', $permissions)
|
||||
->setAttribute('signature', $fileHash)
|
||||
->setAttribute('mimeType', $mimeType)
|
||||
->setAttribute('sizeActual', $sizeActual)
|
||||
|
@ -577,15 +603,11 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
->setAttribute('metadata', $metadata)
|
||||
->setAttribute('chunksUploaded', $chunksUploaded);
|
||||
|
||||
if ($permissionBucket) {
|
||||
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
|
||||
} else {
|
||||
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
|
||||
}
|
||||
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
|
||||
}
|
||||
} catch (StructureException $exception) {
|
||||
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage());
|
||||
} catch (DuplicateException $exception) {
|
||||
} catch (DuplicateException) {
|
||||
throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
|
@ -598,9 +620,8 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
try {
|
||||
if ($file->isEmpty()) {
|
||||
$doc = new Document([
|
||||
'$id' => $fileId,
|
||||
'$read' => $read,
|
||||
'$write' => $write,
|
||||
'$id' => ID::custom($fileId),
|
||||
'$permissions' => $permissions,
|
||||
'bucketId' => $bucket->getId(),
|
||||
'name' => $fileName,
|
||||
'path' => $path,
|
||||
|
@ -615,25 +636,18 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
'search' => implode(' ', [$fileId, $fileName]),
|
||||
'metadata' => $metadata,
|
||||
]);
|
||||
if ($permissionBucket) {
|
||||
$file = Authorization::skip(fn () => $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc));
|
||||
} else {
|
||||
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
|
||||
}
|
||||
|
||||
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
|
||||
} else {
|
||||
$file = $file
|
||||
->setAttribute('chunksUploaded', $chunksUploaded)
|
||||
->setAttribute('metadata', $metadata);
|
||||
|
||||
if ($permissionBucket) {
|
||||
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
|
||||
} else {
|
||||
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
|
||||
}
|
||||
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
|
||||
}
|
||||
} catch (StructureException $exception) {
|
||||
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage());
|
||||
} catch (DuplicateException $exception) {
|
||||
} catch (DuplicateException) {
|
||||
throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS);
|
||||
}
|
||||
}
|
||||
|
@ -682,19 +696,13 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
|||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Check bucket permissions when enforced
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($bucket->getRead())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($bucket->getRead())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$filterQueries = [];
|
||||
|
@ -708,11 +716,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
|||
$queries[] = Query::offset($offset);
|
||||
$queries[] = $orderType === Database::ORDER_ASC ? Query::orderAsc('') : Query::orderDesc('');
|
||||
if (!empty($cursor)) {
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor));
|
||||
} else {
|
||||
$cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor);
|
||||
}
|
||||
$cursorDocument = $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.");
|
||||
|
@ -721,12 +725,12 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
|||
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
|
||||
}
|
||||
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), \array_merge($filterQueries, $queries)));
|
||||
$total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
|
||||
} else {
|
||||
if ($bucket->getAttribute('fileSecurity', false)) {
|
||||
$files = $dbForProject->find('bucket_' . $bucket->getInternalId(), \array_merge($filterQueries, $queries));
|
||||
$total = $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
|
||||
} else {
|
||||
$files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), \array_merge($filterQueries, $queries)));
|
||||
$total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
|
||||
}
|
||||
|
||||
$usage
|
||||
|
@ -762,31 +766,30 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Check bucket permissions when enforced
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($bucket->getRead())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization('read');
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
if (!$valid && !$fileSecurity) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
} else {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
}
|
||||
$file = $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)
|
||||
|
@ -838,19 +841,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
|||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Check bucket permissions when enforced
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($bucket->getRead())) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND, 'Unauthorized permissions');
|
||||
}
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization('read');
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
if (!$valid && !$fileSecurity) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ((\strpos($request->getAccept(), 'image/webp') === false) && ('webp' === $output)) { // Fallback webp to jpeg when no browser support
|
||||
|
@ -861,17 +860,22 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
|||
$outputs = Config::getParam('storage-outputs');
|
||||
$fileLogos = Config::getParam('storage-logos');
|
||||
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
// skip authorization
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
} else {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
}
|
||||
$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 ($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');
|
||||
|
@ -988,31 +992,30 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
|||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Check bucket permissions when enforced
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($bucket->getRead())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization('read');
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
if (!$valid && !$fileSecurity) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
} else {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
}
|
||||
$file = $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)) {
|
||||
|
@ -1127,41 +1130,38 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
|||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Check bucket permissions when enforced
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($bucket->getRead())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization('read');
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
if (!$valid && !$fileSecurity) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
} else {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
}
|
||||
|
||||
$mimes = Config::getParam('storage-mimes');
|
||||
$file = $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', '');
|
||||
|
||||
if (!$deviceFiles->exists($path)) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path);
|
||||
}
|
||||
|
||||
$compressor = new GZIP();
|
||||
|
||||
$contentType = 'text/plain';
|
||||
|
||||
if (\in_array($file->getAttribute('mimeType'), $mimes)) {
|
||||
|
@ -1271,71 +1271,73 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
->label('sdk.response.model', Response::MODEL_FILE)
|
||||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new UID(), 'File unique ID.')
|
||||
->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with permissions. By default no user is granted with any permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('user')
|
||||
->inject('usage')
|
||||
->inject('mode')
|
||||
->inject('events')
|
||||
->action(function (string $bucketId, string $fileId, ?array $read, ?array $write, Response $response, Database $dbForProject, Document $user, Stats $usage, string $mode, Event $events) {
|
||||
->action(function (string $bucketId, string $fileId, ?array $permissions, Response $response, Database $dbForProject, Document $user, Stats $usage, string $mode, Event $events) {
|
||||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
$read = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? []; // By default set read permissions for user
|
||||
$write = (is_null($write) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $write ?? [];
|
||||
|
||||
// Users can only add their roles to files, API keys and Admin users can add any
|
||||
$roles = Authorization::getRoles();
|
||||
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
foreach ($read as $role) {
|
||||
if (!Authorization::isRole($role)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400);
|
||||
}
|
||||
}
|
||||
foreach ($write as $role) {
|
||||
if (!Authorization::isRole($role)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Check bucket permissions when enforced
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$validator = new Authorization('write');
|
||||
if (!$validator->isValid($bucket->getWrite())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
$fileSecurity = $bucket->getAttributes('fileSecurity', false);
|
||||
$validator = new Authorization('update');
|
||||
$valid = $validator->isValid($bucket->getUpdate());
|
||||
if (!$valid && !$fileSecurity) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
} else {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
}
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
|
||||
if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$file
|
||||
->setAttribute('$read', $read)
|
||||
->setAttribute('$write', $write)
|
||||
;
|
||||
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
|
||||
} else {
|
||||
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
|
||||
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)) {
|
||||
foreach (Database::PERMISSIONS as $type) {
|
||||
foreach ($permissions as $permission) {
|
||||
$permission = Permission::parse($permission);
|
||||
if ($permission->getPermission() != $type) {
|
||||
continue;
|
||||
}
|
||||
$role = (new Role(
|
||||
$permission->getRole(),
|
||||
$permission->getIdentifier(),
|
||||
$permission->getDimension()
|
||||
))->toString();
|
||||
if (!Authorization::isRole($role)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
||||
$file->setAttribute('$permissions', $permissions);
|
||||
|
||||
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
|
||||
|
||||
$events
|
||||
->setParam('bucketId', $bucket->getId())
|
||||
->setParam('fileId', $file->getId())
|
||||
|
@ -1375,31 +1377,30 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $events, Stats $usage, string $mode, Device $deviceFiles, Delete $deletes) {
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Check bucket permissions when enforced
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$validator = new Authorization('write');
|
||||
if (!$validator->isValid($bucket->getWrite())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
$fileSecurity = $bucket->getAttributes('fileSecurity', false);
|
||||
$validator = new Authorization('delete');
|
||||
$valid = $validator->isValid($bucket->getDelete());
|
||||
if (!$valid && !$fileSecurity) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
} else {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
}
|
||||
$file = $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(
|
||||
|
@ -1416,11 +1417,8 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
->setResource('file/' . $fileId)
|
||||
;
|
||||
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$deleted = Authorization::skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
} else {
|
||||
$deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
}
|
||||
$deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
|
||||
if (!$deleted) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove file from DB');
|
||||
}
|
||||
|
|
|
@ -21,8 +21,11 @@ use Utopia\Database\Database;
|
|||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Authorization as AuthorizationException;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
|
@ -57,22 +60,31 @@ App::post('/v1/teams')
|
|||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
|
||||
$teamId = $teamId == 'unique()' ? $dbForProject->getId() : $teamId;
|
||||
$teamId = $teamId == 'unique()' ? ID::unique() : $teamId;
|
||||
$team = Authorization::skip(fn() => $dbForProject->createDocument('teams', new Document([
|
||||
'$id' => $teamId ,
|
||||
'$read' => ['team:' . $teamId],
|
||||
'$write' => ['team:' . $teamId . '/owner'],
|
||||
'$id' => $teamId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team($teamId)),
|
||||
Permission::update(Role::team($teamId), 'owner'),
|
||||
Permission::delete(Role::team($teamId), 'owner'),
|
||||
],
|
||||
'name' => $name,
|
||||
'total' => ($isPrivilegedUser || $isAppUser) ? 0 : 1,
|
||||
'search' => implode(' ', [$teamId, $name]),
|
||||
])));
|
||||
|
||||
if (!$isPrivilegedUser && !$isAppUser) { // Don't add user on server mode
|
||||
$membershipId = $dbForProject->getId();
|
||||
$membershipId = ID::unique();
|
||||
$membership = new Document([
|
||||
'$id' => $membershipId,
|
||||
'$read' => ['user:' . $user->getId(), 'team:' . $team->getId()],
|
||||
'$write' => ['user:' . $user->getId(), 'team:' . $team->getId() . '/owner'],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::read(Role::team($team->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::update(Role::team($team->getId(), 'owner')),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
Permission::delete(Role::team($team->getId(), 'owner')),
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'teamId' => $team->getId(),
|
||||
|
@ -321,11 +333,15 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
}
|
||||
|
||||
try {
|
||||
$userId = $dbForProject->getId();
|
||||
$userId = ID::unique();
|
||||
$invitee = Authorization::skip(fn() => $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$read' => ['user:' . $userId, 'role:all'],
|
||||
'$write' => ['user:' . $userId],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::read(Role::user($userId)),
|
||||
Permission::update(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
|
@ -360,11 +376,16 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
|
||||
$secret = Auth::tokenGenerator();
|
||||
|
||||
$membershipId = $dbForProject->getId();
|
||||
$membershipId = ID::unique();
|
||||
$membership = new Document([
|
||||
'$id' => $membershipId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:' . $invitee->getId(), 'team:' . $team->getId() . '/owner'],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::user($invitee->getId())),
|
||||
Permission::update(Role::team($team->getId(), 'owner')),
|
||||
Permission::delete(Role::user($invitee->getId())),
|
||||
Permission::delete(Role::team($team->getId(), 'owner')),
|
||||
],
|
||||
'userId' => $invitee->getId(),
|
||||
'userInternalId' => $invitee->getInternalId(),
|
||||
'teamId' => $team->getId(),
|
||||
|
@ -688,14 +709,14 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
|
||||
// Log user in
|
||||
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
Authorization::setRole(Role::user($user->getId())->toString());
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_LOGIN_LONG);
|
||||
$secret = Auth::tokenGenerator();
|
||||
$session = new Document(array_merge([
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
|
@ -708,12 +729,15 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
], $detector->getOS(), $detector->getClient(), $detector->getDevice()));
|
||||
|
||||
$session = $dbForProject->createDocument('sessions', $session
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
->setAttribute('$permissions', [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
Authorization::setRole('user:' . $userId);
|
||||
Authorization::setRole(Role::user($userId)->toString());
|
||||
|
||||
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
|
||||
|
||||
|
@ -787,6 +811,14 @@ 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) {
|
||||
|
|
|
@ -13,6 +13,9 @@ use Appwrite\Utopia\Response;
|
|||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Locale\Locale;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Database\Document;
|
||||
|
@ -40,12 +43,17 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
|||
}
|
||||
|
||||
try {
|
||||
$userId = $userId == 'unique()' ? $dbForProject->getId() : $userId;
|
||||
$userId = $userId == 'unique()'
|
||||
? ID::unique()
|
||||
: ID::custom($userId);
|
||||
|
||||
$user = $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:' . $userId],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'phone' => $phone,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
require_once __DIR__ . '/../init.php';
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Logger\Logger;
|
||||
use Utopia\Logger\Log;
|
||||
|
@ -248,7 +249,9 @@ App::init()
|
|||
/*
|
||||
* ACL Check
|
||||
*/
|
||||
$role = ($user->isEmpty()) ? Auth::USER_ROLE_GUEST : Auth::USER_ROLE_MEMBER;
|
||||
$role = ($user->isEmpty())
|
||||
? Role::guests()->toString()
|
||||
: Role::users()->toString();
|
||||
|
||||
// Add user roles
|
||||
$memberships = $user->find('teamId', $project->getAttribute('teamId', null), 'memberships');
|
||||
|
@ -292,16 +295,15 @@ App::init()
|
|||
'name' => $project->getAttribute('name', 'Untitled'),
|
||||
]);
|
||||
|
||||
$role = Auth::USER_ROLE_APP;
|
||||
$role = Auth::USER_ROLE_APPS;
|
||||
$scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
|
||||
|
||||
$expire = $key->getAttribute('expire');
|
||||
|
||||
if (!empty($expire) && $expire < DateTime::now()) {
|
||||
if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) {
|
||||
throw new AppwriteException(AppwriteException:: PROJECT_KEY_EXPIRED);
|
||||
}
|
||||
|
||||
Authorization::setRole('role:' . Auth::USER_ROLE_APP);
|
||||
Authorization::setRole(Auth::USER_ROLE_APPS);
|
||||
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
|
||||
|
||||
if (time() > $key->getAttribute('accessedAt', 0) + APP_KEY_ACCCESS) {
|
||||
|
@ -327,7 +329,7 @@ App::init()
|
|||
}
|
||||
}
|
||||
|
||||
Authorization::setRole('role:' . $role);
|
||||
Authorization::setRole($role);
|
||||
|
||||
foreach (Auth::getRoles($user) as $authRole) {
|
||||
Authorization::setRole($authRole);
|
||||
|
|
|
@ -283,7 +283,7 @@ App::post('/v1/mock/tests/general/upload')
|
|||
|
||||
if ($end !== $size) {
|
||||
$response->json([
|
||||
'$id' => 'newfileid',
|
||||
'$id' => ID::custom('newfileid'),
|
||||
'chunksTotal' => $file['size'] / $chunkSize,
|
||||
'chunksUploaded' => $start / $chunkSize
|
||||
]);
|
||||
|
|
|
@ -5,6 +5,7 @@ use Appwrite\Utopia\Response;
|
|||
use Appwrite\Utopia\View;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Storage\Storage;
|
||||
|
@ -289,9 +290,22 @@ App::get('/console/databases/collection')
|
|||
])
|
||||
;
|
||||
|
||||
$permissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml');
|
||||
$permissions
|
||||
->setParam('method', 'databases.getCollection')
|
||||
->setParam('events', 'databases.updateCollection')
|
||||
->setParam('data', 'project-collection')
|
||||
->setParam('params', [
|
||||
'collection-id' => '{{router.params.id}}',
|
||||
'database-id' => '{{router.params.databaseId}}'
|
||||
]);
|
||||
|
||||
$page = new View(__DIR__ . '/../../views/console/databases/collection.phtml');
|
||||
|
||||
$page->setParam('logs', $logs);
|
||||
$page
|
||||
->setParam('permissions', $permissions)
|
||||
->setParam('logs', $logs)
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME . ' - Database Collection')
|
||||
|
@ -326,12 +340,28 @@ App::get('/console/databases/document')
|
|||
])
|
||||
;
|
||||
|
||||
$permissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml');
|
||||
|
||||
$permissions
|
||||
->setParam('method', 'databases.getDocument')
|
||||
->setParam('data', 'project-document')
|
||||
->setParam('permissions', \array_filter(
|
||||
Database::PERMISSIONS,
|
||||
fn ($perm) => $perm != Database::PERMISSION_CREATE
|
||||
))
|
||||
->setParam('params', [
|
||||
'collection-id' => '{{router.params.collection}}',
|
||||
'database-id' => '{{router.params.databaseId}}',
|
||||
'document-id' => '{{router.params.id}}',
|
||||
]);
|
||||
|
||||
$page = new View(__DIR__ . '/../../views/console/databases/document.phtml');
|
||||
|
||||
$page
|
||||
->setParam('new', false)
|
||||
->setParam('database', $databaseId)
|
||||
->setParam('collection', $collection)
|
||||
->setParam('permissions', $permissions)
|
||||
->setParam('logs', $logs)
|
||||
;
|
||||
|
||||
|
@ -349,12 +379,27 @@ App::get('/console/databases/document/new')
|
|||
->inject('layout')
|
||||
->action(function (string $databaseId, string $collection, View $layout) {
|
||||
|
||||
$permissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml');
|
||||
|
||||
$permissions
|
||||
->setParam('data', 'project-document')
|
||||
->setParam('permissions', \array_filter(
|
||||
Database::PERMISSIONS,
|
||||
fn ($perm) => $perm != Database::PERMISSION_CREATE
|
||||
))
|
||||
->setParam('params', [
|
||||
'collection-id' => '{{router.params.collection}}',
|
||||
'database-id' => '{{router.params.databaseId}}',
|
||||
'document-id' => '{{router.params.id}}',
|
||||
]);
|
||||
|
||||
$page = new View(__DIR__ . '/../../views/console/databases/document.phtml');
|
||||
|
||||
$page
|
||||
->setParam('new', true)
|
||||
->setParam('database', $databaseId)
|
||||
->setParam('collection', $collection)
|
||||
->setParam('permissions', $permissions)
|
||||
->setParam('logs', new View())
|
||||
;
|
||||
|
||||
|
@ -392,11 +437,49 @@ App::get('/console/storage/bucket')
|
|||
->inject('layout')
|
||||
->action(function (string $id, Response $response, View $layout) {
|
||||
|
||||
$bucketPermissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml');
|
||||
$bucketPermissions
|
||||
->setParam('method', 'databases.getBucket')
|
||||
->setParam('events', 'load,databases.updateBucket')
|
||||
->setParam('data', 'project-bucket')
|
||||
->setParam('form', 'bucketPermissions')
|
||||
->setParam('params', [
|
||||
'bucket-id' => '{{router.params.id}}',
|
||||
]);
|
||||
|
||||
$fileCreatePermissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml');
|
||||
$fileCreatePermissions
|
||||
->setParam('data', 'project-document')
|
||||
->setParam('form', 'fileCreatePermissions')
|
||||
->setParam('permissions', \array_filter(
|
||||
Database::PERMISSIONS,
|
||||
fn ($perm) => $perm != Database::PERMISSION_CREATE
|
||||
))
|
||||
->setParam('params', [
|
||||
'bucket-id' => '{{router.params.id}}',
|
||||
]);
|
||||
|
||||
$fileUpdatePermissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml');
|
||||
$fileUpdatePermissions
|
||||
->setParam('method', 'storage.getFile')
|
||||
->setParam('data', 'project-document')
|
||||
->setParam('form', 'fileUpdatePermissions')
|
||||
->setParam('permissions', \array_filter(
|
||||
Database::PERMISSIONS,
|
||||
fn ($perm) => $perm != Database::PERMISSION_CREATE
|
||||
))
|
||||
->setParam('params', [
|
||||
'bucket-id' => '{{router.params.id}}',
|
||||
]);
|
||||
|
||||
$page = new View(__DIR__ . '/../../views/console/storage/bucket.phtml');
|
||||
$page
|
||||
->setParam('home', App::getEnv('_APP_HOME', 0))
|
||||
->setParam('fileLimit', App::getEnv('_APP_STORAGE_LIMIT', 0))
|
||||
->setParam('fileLimitHuman', Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0)))
|
||||
->setParam('bucketPermissions', $bucketPermissions)
|
||||
->setParam('fileCreatePermissions', $fileCreatePermissions)
|
||||
->setParam('fileUpdatePermissions', $fileUpdatePermissions)
|
||||
;
|
||||
|
||||
$layout
|
||||
|
|
26
app/http.php
26
app/http.php
|
@ -10,6 +10,9 @@ use Swoole\Http\Response as SwooleResponse;
|
|||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
|
@ -132,7 +135,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
|
||||
foreach ($collection['attributes'] as $attribute) {
|
||||
$attributes[] = new Document([
|
||||
'$id' => $attribute['$id'],
|
||||
'$id' => ID::custom($attribute['$id']),
|
||||
'type' => $attribute['type'],
|
||||
'size' => $attribute['size'],
|
||||
'required' => $attribute['required'],
|
||||
|
@ -146,7 +149,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
|
||||
foreach ($collection['indexes'] as $index) {
|
||||
$indexes[] = new Document([
|
||||
'$id' => $index['$id'],
|
||||
'$id' => ID::custom($index['$id']),
|
||||
'type' => $index['type'],
|
||||
'attributes' => $index['attributes'],
|
||||
'lengths' => $index['lengths'],
|
||||
|
@ -160,17 +163,20 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
if ($dbForConsole->getDocument('buckets', 'default')->isEmpty()) {
|
||||
Console::success('[Setup] - Creating default bucket...');
|
||||
$dbForConsole->createDocument('buckets', new Document([
|
||||
'$id' => 'default',
|
||||
'$collection' => 'buckets',
|
||||
'$id' => ID::custom('default'),
|
||||
'$collection' => ID::custom('buckets'),
|
||||
'name' => 'Default',
|
||||
'permission' => 'file',
|
||||
'maximumFileSize' => (int) App::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB
|
||||
'allowedFileExtensions' => [],
|
||||
'enabled' => true,
|
||||
'encryption' => true,
|
||||
'antivirus' => true,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'fileSecurity' => true,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'search' => 'buckets Default',
|
||||
]));
|
||||
|
||||
|
@ -187,7 +193,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
|
||||
foreach ($files['attributes'] as $attribute) {
|
||||
$attributes[] = new Document([
|
||||
'$id' => $attribute['$id'],
|
||||
'$id' => ID::custom($attribute['$id']),
|
||||
'type' => $attribute['type'],
|
||||
'size' => $attribute['size'],
|
||||
'required' => $attribute['required'],
|
||||
|
@ -201,7 +207,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
|
||||
foreach ($files['indexes'] as $index) {
|
||||
$indexes[] = new Document([
|
||||
'$id' => $index['$id'],
|
||||
'$id' => ID::custom($index['$id']),
|
||||
'type' => $index['type'],
|
||||
'attributes' => $index['attributes'],
|
||||
'lengths' => $index['lengths'],
|
||||
|
@ -252,7 +258,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
|
||||
try {
|
||||
Authorization::cleanRoles();
|
||||
Authorization::setRole('role:all');
|
||||
Authorization::setRole(Role::any()->toString());
|
||||
|
||||
$app->run($request, $response);
|
||||
} catch (\Throwable $th) {
|
||||
|
|
19
app/init.php
19
app/init.php
|
@ -43,6 +43,7 @@ use Appwrite\OpenSSL\OpenSSL;
|
|||
use Appwrite\Stats\Stats;
|
||||
use Appwrite\Utopia\View;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Logger\Logger;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Locale\Locale;
|
||||
|
@ -729,7 +730,7 @@ App::setResource('usage', function ($register) {
|
|||
|
||||
App::setResource('clients', function ($request, $console, $project) {
|
||||
$console->setAttribute('platforms', [ // Always allow current host
|
||||
'$collection' => 'platforms',
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Current Host',
|
||||
'type' => 'web',
|
||||
'hostname' => $request->getHostname(),
|
||||
|
@ -805,7 +806,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
|||
|
||||
if (APP_MODE_ADMIN !== $mode) {
|
||||
if ($project->isEmpty()) {
|
||||
$user = new Document(['$id' => '', '$collection' => 'users']);
|
||||
$user = new Document(['$id' => ID::custom(''), '$collection' => 'users']);
|
||||
} else {
|
||||
$user = $dbForProject->getDocument('users', Auth::$unique);
|
||||
}
|
||||
|
@ -817,14 +818,14 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
|||
$user->isEmpty() // Check a document has been found in the DB
|
||||
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)
|
||||
) { // Validate user has valid login token
|
||||
$user = new Document(['$id' => '', '$collection' => 'users']);
|
||||
$user = new Document(['$id' => ID::custom(''), '$collection' => 'users']);
|
||||
}
|
||||
|
||||
if (APP_MODE_ADMIN === $mode) {
|
||||
if ($user->find('teamId', $project->getAttribute('teamId'), 'memberships')) {
|
||||
Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
|
||||
} else {
|
||||
$user = new Document(['$id' => '', '$collection' => 'users']);
|
||||
$user = new Document(['$id' => ID::custom(''), '$collection' => 'users']);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -847,7 +848,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
|||
}
|
||||
|
||||
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
|
||||
$user = new Document(['$id' => '', '$collection' => 'users']);
|
||||
$user = new Document(['$id' => ID::custom(''), '$collection' => 'users']);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -872,10 +873,10 @@ App::setResource('project', function ($dbForConsole, $request, $console) {
|
|||
|
||||
App::setResource('console', function () {
|
||||
return new Document([
|
||||
'$id' => 'console',
|
||||
'$internalId' => 'console',
|
||||
'$id' => ID::custom('console'),
|
||||
'$internalId' => ID::custom('console'),
|
||||
'name' => 'Appwrite',
|
||||
'$collection' => 'projects',
|
||||
'$collection' => ID::custom('projects'),
|
||||
'description' => 'Appwrite core engine',
|
||||
'logo' => '',
|
||||
'teamId' => -1,
|
||||
|
@ -883,7 +884,7 @@ App::setResource('console', function () {
|
|||
'keys' => [],
|
||||
'platforms' => [
|
||||
[
|
||||
'$collection' => 'platforms',
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Localhost',
|
||||
'type' => 'web',
|
||||
'hostname' => 'localhost',
|
||||
|
|
|
@ -13,6 +13,8 @@ use Utopia\Abuse\Abuse;
|
|||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
|
@ -134,7 +136,7 @@ function getDatabase(Registry &$register, string $namespace)
|
|||
|
||||
$server->onStart(function () use ($stats, $register, $containerId, &$statsDocument, $logError) {
|
||||
sleep(5); // wait for the initial database schema to be ready
|
||||
Console::success('Server started succefully');
|
||||
Console::success('Server started successfully');
|
||||
|
||||
/**
|
||||
* Create document for this worker to share stats across Containers.
|
||||
|
@ -146,10 +148,9 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
|||
try {
|
||||
$attempts++;
|
||||
$document = new Document([
|
||||
'$id' => $database->getId(),
|
||||
'$collection' => 'realtime',
|
||||
'$read' => [],
|
||||
'$write' => [],
|
||||
'$id' => ID::unique(),
|
||||
'$collection' => ID::custom('realtime'),
|
||||
'$permissions' => [],
|
||||
'container' => $containerId,
|
||||
'timestamp' => DateTime::now(),
|
||||
'value' => '{}'
|
||||
|
@ -203,7 +204,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
|||
/**
|
||||
* Sending current connections to project channels on the console project every 5 seconds.
|
||||
*/
|
||||
if ($realtime->hasSubscriber('console', 'role:member', 'project')) {
|
||||
if ($realtime->hasSubscriber('console', Role::users()->toString(), 'project')) {
|
||||
[$database, $returnDatabase] = getDatabase($register, '_console');
|
||||
|
||||
$payload = [];
|
||||
|
@ -254,12 +255,12 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
|||
/**
|
||||
* Sending test message for SDK E2E tests every 5 seconds.
|
||||
*/
|
||||
if ($realtime->hasSubscriber('console', 'role:guest', 'tests')) {
|
||||
if ($realtime->hasSubscriber('console', Role::guests()->toString(), 'tests')) {
|
||||
$payload = ['response' => 'WS:/v1/realtime:passed'];
|
||||
|
||||
$event = [
|
||||
'project' => 'console',
|
||||
'roles' => ['role:guest'],
|
||||
'roles' => [Role::guests()->toString()],
|
||||
'data' => [
|
||||
'events' => ['test.event'],
|
||||
'channels' => ['tests'],
|
||||
|
|
113
app/views/console/comps/permissions-matrix.phtml
Normal file
113
app/views/console/comps/permissions-matrix.phtml
Normal file
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
use Utopia\Database\Database;
|
||||
|
||||
// Data
|
||||
$method = $this->getParam('method', '');
|
||||
$params = $this->getParam('params', []);
|
||||
$events = $this->getParam('events', '');
|
||||
$permissions = $this->getParam('permissions', Database::PERMISSIONS);
|
||||
// Names
|
||||
$data = $this->getParam('data', '');
|
||||
$form = $this->getParam('form', 'form');
|
||||
|
||||
$escapedPermissions = \array_map(function ($perm) {
|
||||
// Alpine won't bind to a parameter named delete :/
|
||||
if ($perm == 'delete') {
|
||||
return 'xdelete';
|
||||
}
|
||||
return $perm;
|
||||
}, $permissions);
|
||||
|
||||
?>
|
||||
<div
|
||||
<?php if ($method): ?>
|
||||
data-method="<?php echo $method; ?>"
|
||||
<?php endif; ?>
|
||||
<?php foreach ($params as $key => $value): ?>
|
||||
data-param-<?php echo $key; ?>="<?php echo $value; ?>"
|
||||
<?php endforeach; ?>
|
||||
data-scope="sdk"
|
||||
data-event="load<?php if (!empty($events)) echo ',' . $events; ?>"
|
||||
data-name="<?php echo $data; ?>"
|
||||
class="permissions-matrix margin-bottom-large"
|
||||
x-data="permissionsMatrix"
|
||||
@reset.window="permissions = rawPermissions = []">
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="permissions"
|
||||
data-cast-from="csv"
|
||||
data-cast-to="array"
|
||||
data-ls-bind="{{<?php echo $data ?>.$permissions}}"
|
||||
:value="rawPermissions"/>
|
||||
|
||||
<table data-ls-attrs="x-init=load({{<?php echo $data ?>.$permissions}})">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Role</th>
|
||||
<?php foreach ($permissions as $permission): ?>
|
||||
<th><?php echo \ucfirst($permission); ?></th>
|
||||
<?php endforeach; ?>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="(permission, index) in permissions">
|
||||
<tr>
|
||||
<td>
|
||||
<p x-text="permission.role"></p>
|
||||
</td>
|
||||
<?php foreach ($escapedPermissions as $permission): ?>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="<?php echo $permission ?>"
|
||||
x-model="permission.<?php echo $permission; ?>"
|
||||
@click="updatePermission(index)"/>
|
||||
</td>
|
||||
<?php endforeach; ?>
|
||||
<td>
|
||||
<span class="action" @click="removePermission(index)">
|
||||
<i class="icon-trash"></i>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<tr x-data="permissionsRow"
|
||||
@addrow.window="addPermission('<?php echo $form; ?>',role,{<?php echo \implode(',', $escapedPermissions) ?>})">
|
||||
<td>
|
||||
<datalist id="types">
|
||||
<option value="user:">
|
||||
<option value="team:">
|
||||
<option value="users">
|
||||
<option value="guests">
|
||||
<option value="any">
|
||||
</datalist>
|
||||
|
||||
<input
|
||||
required
|
||||
id="<?php echo $form; ?>"
|
||||
name="<?php echo $form; ?>"
|
||||
form="<?php echo $form ?>"
|
||||
list="types"
|
||||
type="text"
|
||||
x-model="role" />
|
||||
</td>
|
||||
<?php foreach ($escapedPermissions as $permission): ?>
|
||||
<td>
|
||||
<input type="checkbox" name="<?php echo $permission ?>" x-model="<?php echo $permission; ?>"/>
|
||||
</td>
|
||||
<?php endforeach; ?>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="<?php \count($permissions) + 2 ?>">
|
||||
<button type="button" class="btn btn-primary margin-top-small" @click="$dispatch('addrow')">Add</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
$logs = $this->getParam('logs', null);
|
||||
$permissions = $this->getParam('permissions', null);
|
||||
|
||||
?>
|
||||
<div
|
||||
|
@ -489,8 +490,8 @@ $logs = $this->getParam('logs', null);
|
|||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no margin-bottom-no">
|
||||
<input
|
||||
type="hidden"
|
||||
<input
|
||||
type="hidden"
|
||||
data-ls-bind="{{usage}}"
|
||||
data-forms-chart="Created=documentsCreate,Read=documentsRead,Updated=documentsUpdate,Deleted=documentsDelete"
|
||||
data-show-y-axis="true"
|
||||
|
@ -513,6 +514,8 @@ $logs = $this->getParam('logs', null);
|
|||
|
||||
<div class="row responsive margin-top-negative">
|
||||
<div class="col span-8 margin-bottom">
|
||||
<form id="<?php echo $permissions->getParam('form', 'permissions') ?>"></form>
|
||||
|
||||
<form
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
|
@ -531,8 +534,6 @@ $logs = $this->getParam('logs', null);
|
|||
data-failure-param-alert-text="Failed to update collection"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<label> </label>
|
||||
|
||||
<div class="box">
|
||||
<label for="collection-name">Name</label>
|
||||
<input name="name" id="collection-name" type="text" autocomplete="off" data-ls-bind="{{project-collection.name}}" data-forms-text-direction required placeholder="Collection Name" maxlength="128" />
|
||||
|
@ -541,36 +542,25 @@ $logs = $this->getParam('logs', null);
|
|||
<input name="enabled" type="hidden" data-forms-switch data-cast-to="boolean" data-ls-bind="{{project-collection.enabled}}" /> Enabled <span class="tooltip" data-tooltip="Mark whether collection is enabled"><i class="icon-info-circled"></i></span>
|
||||
</div>
|
||||
|
||||
<hr class="margin-top-small" />
|
||||
|
||||
<label class="margin-bottom-small">Permissions</label>
|
||||
|
||||
<p class="text-fade text-size-small">Choose the permissions model for this collection.</p>
|
||||
<p class="text-fade text-size-small">Configure the permissions for this collection.</p>
|
||||
|
||||
<hr class="margin-top-small" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="collection" type="radio" class="margin-top-tiny" data-ls-bind="{{project-collection.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>Collection Level</b>
|
||||
<p class="text-fade margin-top-tiny">With Collection Level permissions, you assign permissions only once in the collection.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level, permissions assigned to collection takes the precedence and documents permissions are ignored.</p>
|
||||
<div data-ls-if="{{project-collection.permission}} === 'collection'">
|
||||
<label for="collection-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</span></label>
|
||||
<input type="hidden" id="collection-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-collection.$read}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
<?php echo $permissions->render(); ?>
|
||||
|
||||
<label for="collection-write">Write Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="collection-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-collection.$write}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="margin-top-no" />
|
||||
|
||||
<label class="margin-bottom-small">Document Security</label>
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="document" type="radio" class="margin-top-no" data-ls-bind="{{project-collection.permission}}" /></div>
|
||||
<div class="col span-1"><input name="documentSecurity" value="false" type="checkbox" class="margin-top-no" data-ls-bind="{{project-collection.documentSecurity}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>Document Level</b>
|
||||
<p class="text-fade margin-top-tiny">With Document Level permissions, you have granular access control over every document. Users will only be able to access documents for which they have explicit permissions.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level, document permissions take precedence and collection permissions are ignored.</p>
|
||||
<b>Enabled</b>
|
||||
<p class="text-fade margin-top-tiny">With Document Security enabled, users will be able to access documents for which they have been granted <b>either</b> Document or Collection permissions.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -592,9 +582,9 @@ $logs = $this->getParam('logs', null);
|
|||
</div>
|
||||
|
||||
<ul class="margin-bottom-large text-fade text-size-small">
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i>
|
||||
<button data-ls-ui-trigger="open-json"
|
||||
class="link text-size-small"
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i>
|
||||
<button data-ls-ui-trigger="open-json"
|
||||
class="link text-size-small"
|
||||
data-analytics
|
||||
data-analytics-event="click"
|
||||
data-analytics-category="console"
|
||||
|
|
|
@ -131,9 +131,8 @@
|
|||
<label for="collection-name">Name</label>
|
||||
<input type="text" class="full-width" id="collection-name" name="name" required autocomplete="off" maxlength="128" />
|
||||
|
||||
<input type="hidden" id="collection-permission" name="permission" required value="collection" />
|
||||
<input type="hidden" id="collection-read" name="read" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
<input type="hidden" id="collection-write" name="write" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
<input type="hidden" id="collection-permissions" name="permissions" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
<input type="hidden" id="collection-documentSecurity" name="documentSecurity" required data-cast-to="boolean" value="false" />
|
||||
|
||||
<hr />
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
$new = $this->getParam('new', false);
|
||||
$logs = $this->getParam('logs', null);
|
||||
$permissions = $this->getParam('permissions', null);
|
||||
|
||||
?>
|
||||
<div
|
||||
|
@ -52,6 +53,8 @@ $logs = $this->getParam('logs', null);
|
|||
|
||||
<div class="row responsive">
|
||||
<div class="col span-8 margin-bottom">
|
||||
<form id="<?php echo $permissions->getParam('form', 'permissions') ?>"></form>
|
||||
|
||||
<form
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
|
@ -333,19 +336,13 @@ $logs = $this->getParam('logs', null);
|
|||
</ul>
|
||||
</fieldset>
|
||||
|
||||
<div class="toggle margin-bottom" data-ls-ui-open data-button-aria="Open Permissions">
|
||||
<div class="toggle margin-bottom" data-ls-if="{{project-collection.documentSecurity}}" data-ls-ui-open data-button-aria="Open Permissions">
|
||||
<i class="icon-plus pull-end margin-top-tiny"></i>
|
||||
<i class="icon-minus pull-end margin-top-tiny"></i>
|
||||
|
||||
<h3 class="margin-bottom-large">Permissions</h3>
|
||||
|
||||
<label for="collection-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</span></label>
|
||||
<input type="hidden" id="collection-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-document.$read}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<label for="collection-write">Write Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="collection-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-document.$write}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
<?php echo $permissions->render() ?>
|
||||
</div>
|
||||
|
||||
<button data-ls-if="({{project-document.$id}})">Update</button>
|
||||
|
|
|
@ -552,7 +552,7 @@ sort($patterns);
|
|||
|
||||
<label for="execute">Execute Access <span class="tooltip small" data-tooltip="Choose who can execute this function using the client API."><i class="icon-info-circled"></i></span> <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</span></label>
|
||||
<input type="hidden" id="execute" name="execute" data-forms-tags data-cast-to="json" data-ls-bind="{{project-function.execute}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'any' for wildcard access</div>
|
||||
|
||||
<label for="timeout">Timeout (seconds) <span class="tooltip small" data-tooltip="Limit the execution time of your function."><i class="icon-info-circled"></i></span></label>
|
||||
<input name="timeout" id="function-timeout" type="number" autocomplete="off" data-ls-bind="{{project-function.timeout}}" min="1" max="<?php echo $this->escape($timeout); ?>" data-cast-to="integer" />
|
||||
|
|
|
@ -57,7 +57,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<label for="logo">Project Logo</label>
|
||||
|
||||
<div class="text-align-center clear">
|
||||
<input type="hidden" name="logo" data-ls-bind="{{console-project.logo}}" data-read="<?php echo $this->escape(json_encode(['role:all'])); ?>" data-write="<?php echo $this->escape(json_encode(['team:{{console-project.teamId}}'])); ?>" data-accept="image/*" data-forms-upload="" data-label-button="Upload" data-preview-alt="Project Logo" data-scope="console" data-default="">
|
||||
<input type="hidden" name="logo" data-ls-bind="{{console-project.logo}}" data-read="<?php echo $this->escape(json_encode(['any'])); ?>" data-write="<?php echo $this->escape(json_encode(['team:{{console-project.teamId}}'])); ?>" data-accept="image/*" data-forms-upload="" data-label-button="Upload" data-preview-alt="Project Logo" data-scope="console" data-default="">
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
$home = $this->getParam('home', '');
|
||||
$fileLimit = $this->getParam('fileLimit', 0);
|
||||
$fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
||||
$bucketPermissions = $this->getParam('bucketPermissions', null);
|
||||
$fileCreatePermissions = $this->getParam('fileCreatePermissions', null);
|
||||
$fileUpdatePermissions = $this->getParam('fileUpdatePermissions', null);
|
||||
?>
|
||||
|
||||
<div
|
||||
|
@ -34,6 +37,11 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
</div>
|
||||
|
||||
<div class="zone xl">
|
||||
<!-- Required for permission input validation -->
|
||||
<form id="<?php echo $bucketPermissions->getParam('form') ?>"></form>
|
||||
<form id="<?php echo $fileCreatePermissions->getParam('form') ?>"></form>
|
||||
<form id="<?php echo $fileUpdatePermissions->getParam('form') ?>"></form>
|
||||
|
||||
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
|
||||
<li data-state="/console/storage/bucket?id={{router.params.id}}&project={{router.params.project}}">
|
||||
<h2 class="margin-bottom">Files</h2>
|
||||
|
@ -131,13 +139,14 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
</div>
|
||||
<input type="hidden" data-ls-attrs="id=file-bucketId-{{file.$id}}" name="bucketId" data-ls-bind="{{file.bucketId}}">
|
||||
|
||||
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" data-ls-attrs="id=file-read-{{file.$id}}" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$read}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
<div class="toggle margin-bottom" data-ls-if="{{project-bucket.fileSecurity}}" data-ls-ui-open data-button-aria="Open Permissions">
|
||||
<i class="icon-plus pull-end margin-top-tiny"></i>
|
||||
<i class="icon-minus pull-end margin-top-tiny"></i>
|
||||
|
||||
<label for="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" data-ls-attrs="id=file-write-{{file.$id}}" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$write}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
<h3 class="margin-bottom-large">Permissions</h3>
|
||||
|
||||
<?php echo $fileUpdatePermissions->render(); ?>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form class="strip"
|
||||
|
@ -270,8 +279,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
data-analytics-category="console"
|
||||
data-analytics-label="Create Storage File"
|
||||
x-data
|
||||
@submit.prevent="$store.uploader.uploadFile($event.target)"
|
||||
>
|
||||
@submit.prevent="$store.uploader.uploadFile($event.target)">
|
||||
<input type="hidden" name="bucketId" id="files-bucketId" data-ls-bind="{{router.params.id}}">
|
||||
|
||||
<label for="fileId">File ID</label>
|
||||
|
@ -285,18 +293,19 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
name="fileId"
|
||||
id="fileId" />
|
||||
|
||||
<label for="file-read">File</label>
|
||||
<label for="file">File</label>
|
||||
<input type="file" name="file" id="file-file" size="1" required>
|
||||
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">(Max file size allowed: <?php echo $fileLimitHuman; ?>)</div>
|
||||
|
||||
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="file-read" name="read" data-forms-tags data-cast-to="json" value="<?php echo htmlentities(json_encode(['role:all'])); ?>" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
<div class="toggle margin-bottom" data-ls-if="{{project-bucket.fileSecurity}}" data-ls-ui-open data-button-aria="Open Permissions">
|
||||
<i class="icon-plus pull-end margin-top-tiny"></i>
|
||||
<i class="icon-minus pull-end margin-top-tiny"></i>
|
||||
|
||||
<label for="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="file-write" name="write" data-forms-tags data-cast-to="json" value="" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
<h3 class="margin-bottom-large">Permissions</h3>
|
||||
|
||||
<?php echo $fileCreatePermissions->render() ?>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
|
@ -381,6 +390,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
|
||||
<div class="row responsive margin-top-negative">
|
||||
<div class="col span-8 margin-bottom">
|
||||
|
||||
<form
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
|
@ -426,35 +436,21 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
|
||||
<label class="margin-bottom-small">Permissions</label>
|
||||
|
||||
<p class="text-fade text-size-small">Choose the permissions model for this bucket.</p>
|
||||
<p class="text-fade text-size-small">Configure the permissions for this bucket.</p>
|
||||
|
||||
<hr class="margin-top-small" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="bucket" type="radio" class="margin-top-tiny" data-ls-bind="{{project-bucket.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>Bucket Level</b>
|
||||
<p class="text-fade margin-top-tiny">With Bucket Level permissions, you assign permissions only once in the bucket.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level permissions assigned to bucket takes the precedence and file permissions are ignored</p>
|
||||
<div data-ls-if="{{project-bucket.permission}} == 'bucket'">
|
||||
<?php echo $bucketPermissions->render(); ?>
|
||||
|
||||
<label for="bucket-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</span></label>
|
||||
<input type="hidden" id="bucket-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-bucket.$read}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
<hr class="margin-top-no" />
|
||||
|
||||
<label for="bucket-write">Write Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="bucket-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-bucket.$write}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="margin-bottom-small">File Security</label>
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="file" type="radio" class="margin-top-no" data-ls-bind="{{project-bucket.permission}}" /></div>
|
||||
<div class="col span-1"><input name="fileSecurity" value="false" type="checkbox" class="margin-top-no" data-ls-bind="{{project-bucket.fileSecurity}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>File Level</b>
|
||||
<p class="text-fade margin-top-tiny">With File Level permissions, you have granular access control over every file. Users will only be able to access files for which they have explicit permissions.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level file permissions take precedence and bucket permissions are ignored.</p>
|
||||
<b>Enabled</b>
|
||||
<p class="text-fade margin-top-tiny">With File Security enabled, users will be able to access files for which they have been granted <b>either</b> File or Bucket permissions.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -108,9 +108,8 @@
|
|||
<label for="bucket-name">Name</label>
|
||||
<input type="text" class="full-width" id="bucket-name" name="name" required autocomplete="off" maxlength="128" />
|
||||
|
||||
<input type="hidden" id="bucket-permission" name="permission" required value="bucket" />
|
||||
<input type="hidden" id="bucket-read" name="read" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
<input type="hidden" id="bucket-write" name="write" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
<input type="hidden" id="bucket-permissions" name="permissions" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
<input type="hidden" id="bucket-fileSecurity" name="fileSecurity" required value="false" data-cast-to="boolean" />
|
||||
|
||||
<hr />
|
||||
|
||||
|
|
|
@ -329,8 +329,8 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</li>
|
||||
|
||||
<li data-state="/console/users/providers?project={{router.params.project}}">
|
||||
<p data-ls-if="{{console-project.authLimit}} == 0" class="text-fade text-size-small margin-bottom pull-end">Unlimited Users <span class="link" data-ls-ui-trigger="project-update-auth-users-limit">Set Limit</a></p>
|
||||
<p data-ls-if="{{console-project.authLimit}} != 0" class="text-fade text-size-small margin-bottom pull-end"><span data-ls-bind="{{console-project.authLimit|statsTotal}}"></span> Users allowed <span class="link" data-ls-ui-trigger="project-update-auth-users-limit">Change Limit</a></p>
|
||||
<p data-ls-if="{{console-project.authLimit}} == 0" class="text-fade text-size-small margin-bottom pull-end">Unlimited Users <span class="link" data-ls-ui-trigger="project-update-auth-users-limit">Set Limit</span></p>
|
||||
<p data-ls-if="{{console-project.authLimit}} != 0" class="text-fade text-size-small margin-bottom pull-end"><span data-ls-bind="{{console-project.authLimit|statsTotal}}"></span> Users allowed <span class="link" data-ls-ui-trigger="project-update-auth-users-limit">Change Limit</span></p>
|
||||
|
||||
<h2>Settings</h2>
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ use Utopia\Database\Database;
|
|||
use Utopia\Database\DateTime;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Config\Config;
|
||||
|
@ -78,11 +79,10 @@ class BuildsV1 extends Worker
|
|||
$buildId = $deployment->getAttribute('buildId', '');
|
||||
$startTime = DateTime::now();
|
||||
if (empty($buildId)) {
|
||||
$buildId = $dbForProject->getId();
|
||||
$buildId = ID::unique();
|
||||
$build = $dbForProject->createDocument('builds', new Document([
|
||||
'$id' => $buildId,
|
||||
'$read' => [],
|
||||
'$write' => [],
|
||||
'$permissions' => [],
|
||||
'startTime' => $startTime,
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'status' => 'processing',
|
||||
|
|
|
@ -14,7 +14,10 @@ use Utopia\Config\Config;
|
|||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
require_once __DIR__ . '/../init.php';
|
||||
|
||||
|
@ -234,11 +237,10 @@ class FunctionsV1 extends Worker
|
|||
/** Create execution or update execution status */
|
||||
$execution = $dbForProject->getDocument('executions', $executionId ?? '');
|
||||
if ($execution->isEmpty()) {
|
||||
$executionId = $dbForProject->getId();
|
||||
$executionId = ID::unique();
|
||||
$execution = $dbForProject->createDocument('executions', new Document([
|
||||
'$id' => $executionId,
|
||||
'$read' => $user->isEmpty() ? [] : ['user:' . $user->getId()],
|
||||
'$write' => [],
|
||||
'$permissions' => $user->isEmpty() ? [] : [Permission::read(Role::user($user->getId()))],
|
||||
'functionId' => $functionId,
|
||||
'deploymentId' => $deploymentId,
|
||||
'trigger' => $trigger,
|
||||
|
|
|
@ -45,13 +45,13 @@
|
|||
"appwrite/php-runtimes": "0.11.*",
|
||||
"utopia-php/framework": "0.21.*",
|
||||
"utopia-php/logger": "0.3.*",
|
||||
"utopia-php/abuse": "0.9.*",
|
||||
"utopia-php/abuse": "0.10.*",
|
||||
"utopia-php/analytics": "0.2.*",
|
||||
"utopia-php/audit": "0.10.*",
|
||||
"utopia-php/audit": "0.11.*",
|
||||
"utopia-php/cache": "0.6.*",
|
||||
"utopia-php/cli": "0.13.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.20.*",
|
||||
"utopia-php/database": "0.22.*",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
|
@ -74,22 +74,11 @@
|
|||
{
|
||||
"url": "https://github.com/appwrite/runtimes.git",
|
||||
"type": "git"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"type": "git"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/utopia-php/abuse.git",
|
||||
"type": "git"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/utopia-php/audit.git",
|
||||
"type": "git"
|
||||
}
|
||||
],
|
||||
"require-dev": {
|
||||
"appwrite/sdk-generator": "dev-feat-new-headers",
|
||||
"ext-fileinfo": "*",
|
||||
"phpunit/phpunit": "9.5.20",
|
||||
"squizlabs/php_codesniffer": "^3.6",
|
||||
"swoole/ide-helper": "4.8.9",
|
||||
|
@ -103,4 +92,4 @@
|
|||
"php": "8.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
78
composer.lock
generated
78
composer.lock
generated
|
@ -1733,17 +1733,23 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/abuse",
|
||||
"version": "0.9.0",
|
||||
"version": "0.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/abuse.git",
|
||||
"reference": "34156bb5292d704bb8bc8141bb5151126ed4830a"
|
||||
"reference": "b5beadce6581291e4385b0cc86f1be2a79bb2ef0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/b5beadce6581291e4385b0cc86f1be2a79bb2ef0",
|
||||
"reference": "b5beadce6581291e4385b0cc86f1be2a79bb2ef0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-pdo": "*",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/database": "0.20.0"
|
||||
"utopia-php/database": "0.22.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.4",
|
||||
|
@ -1755,6 +1761,7 @@
|
|||
"Utopia\\Abuse\\": "src/Abuse"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
|
@ -1766,13 +1773,17 @@
|
|||
],
|
||||
"description": "A simple abuse library to manage application usage limits",
|
||||
"keywords": [
|
||||
"abuse",
|
||||
"Abuse",
|
||||
"framework",
|
||||
"php",
|
||||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"time": "2022-08-15T07:35:56+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/abuse/issues",
|
||||
"source": "https://github.com/utopia-php/abuse/tree/0.10.0"
|
||||
},
|
||||
"time": "2022-08-17T14:31:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/analytics",
|
||||
|
@ -1831,16 +1842,22 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/audit",
|
||||
"version": "0.10.0",
|
||||
"version": "0.11.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/audit.git",
|
||||
"reference": "458da3e60ea222bf9791f4891591d7f2ee16e4bb"
|
||||
"reference": "a06f784f8e8b69bcae4f1a5bca58d41bda76c250"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/audit/zipball/a06f784f8e8b69bcae4f1a5bca58d41bda76c250",
|
||||
"reference": "a06f784f8e8b69bcae4f1a5bca58d41bda76c250",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pdo": "*",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/database": "0.20.0"
|
||||
"utopia-php/database": "0.22.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.3",
|
||||
|
@ -1852,6 +1869,7 @@
|
|||
"Utopia\\Audit\\": "src/Audit"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
|
@ -1863,13 +1881,17 @@
|
|||
],
|
||||
"description": "A simple audit library to manage application users logs",
|
||||
"keywords": [
|
||||
"audit",
|
||||
"Audit",
|
||||
"framework",
|
||||
"php",
|
||||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"time": "2022-08-14T19:59:21+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/audit/issues",
|
||||
"source": "https://github.com/utopia-php/audit/tree/0.11.0"
|
||||
},
|
||||
"time": "2022-08-17T15:08:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/cache",
|
||||
|
@ -2030,11 +2052,17 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.20.0",
|
||||
"version": "0.22.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "cd89b41564223cddf7d87a41bbaf736a0c89f327"
|
||||
"reference": "22c45ae83612e907203b7571cd8e3115ae3ae4c5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/22c45ae83612e907203b7571cd8e3115ae3ae4c5",
|
||||
"reference": "22c45ae83612e907203b7571cd8e3115ae3ae4c5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mongodb": "*",
|
||||
|
@ -2058,11 +2086,7 @@
|
|||
"Utopia\\Database\\": "src/Database"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Utopia\\Tests\\": "tests/Database"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
|
@ -2084,7 +2108,11 @@
|
|||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"time": "2022-08-14T15:22:34+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/0.22.0"
|
||||
},
|
||||
"time": "2022-08-17T12:55:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
|
@ -2825,6 +2853,7 @@
|
|||
"brianium/paratest": "^6.4",
|
||||
"phpunit/phpunit": "^9.5.21"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -5319,7 +5348,14 @@
|
|||
"time": "2022-08-12T06:47:24+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
"aliases": [
|
||||
{
|
||||
"package": "appwrite/sdk-generator",
|
||||
"version": "9999999-dev",
|
||||
"alias": "0.19.5",
|
||||
"alias_normalized": "0.19.5.0"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"appwrite/sdk-generator": 20
|
||||
|
@ -5341,7 +5377,9 @@
|
|||
"ext-zlib": "*",
|
||||
"ext-sockets": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"platform-dev": {
|
||||
"ext-fileinfo": "*"
|
||||
},
|
||||
"platform-overrides": {
|
||||
"php": "8.0"
|
||||
},
|
||||
|
|
|
@ -342,7 +342,7 @@
|
|||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Guset
|
||||
<br/>
|
||||
role:guest
|
||||
guests
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -363,7 +363,7 @@
|
|||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Member
|
||||
<br/>
|
||||
role:member
|
||||
users
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
@ -35,6 +35,7 @@ const configApp = {
|
|||
'public/scripts/app.js',
|
||||
'public/scripts/upload-modal.js',
|
||||
'public/scripts/events.js',
|
||||
'public/scripts/permissions-matrix.js',
|
||||
|
||||
'public/scripts/views/service.js',
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</extensions>
|
||||
<testsuites>
|
||||
<testsuite name="unit">
|
||||
<directory>./tests/unit</directory>
|
||||
<directory>./tests/unit/</directory>
|
||||
</testsuite>
|
||||
<testsuite name="e2e">
|
||||
<file>./tests/e2e/Client.php</file>
|
||||
|
|
209
public/dist/scripts/app-all.js
vendored
209
public/dist/scripts/app-all.js
vendored
|
@ -220,29 +220,26 @@ if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
|
|||
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
|
||||
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
createCollection(databaseId,collectionId,name,permission,read,write){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
createCollection(databaseId,collectionId,name,permissions,documentSecurity){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
if(typeof permission==='undefined'){throw new AppwriteException('Missing required parameter: "permission"');}
|
||||
if(typeof read==='undefined'){throw new AppwriteException('Missing required parameter: "read"');}
|
||||
if(typeof write==='undefined'){throw new AppwriteException('Missing required parameter: "write"');}
|
||||
if(typeof permissions==='undefined'){throw new AppwriteException('Missing required parameter: "permissions"');}
|
||||
if(typeof documentSecurity==='undefined'){throw new AppwriteException('Missing required parameter: "documentSecurity"');}
|
||||
let path='/databases/{databaseId}/collections'.replace('{databaseId}',databaseId);let payload={};if(typeof collectionId!=='undefined'){payload['collectionId']=collectionId;}
|
||||
if(typeof name!=='undefined'){payload['name']=name;}
|
||||
if(typeof permission!=='undefined'){payload['permission']=permission;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
if(typeof documentSecurity!=='undefined'){payload['documentSecurity']=documentSecurity;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('post',uri,{'content-type':'application/json',},payload);});}
|
||||
getCollection(databaseId,collectionId){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
let path='/databases/{databaseId}/collections/{collectionId}'.replace('{databaseId}',databaseId).replace('{collectionId}',collectionId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
updateCollection(databaseId,collectionId,name,permission,read,write,enabled){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
updateCollection(databaseId,collectionId,name,documentSecurity,permissions,enabled){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
if(typeof permission==='undefined'){throw new AppwriteException('Missing required parameter: "permission"');}
|
||||
if(typeof documentSecurity==='undefined'){throw new AppwriteException('Missing required parameter: "documentSecurity"');}
|
||||
let path='/databases/{databaseId}/collections/{collectionId}'.replace('{databaseId}',databaseId).replace('{collectionId}',collectionId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
|
||||
if(typeof permission!=='undefined'){payload['permission']=permission;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
if(typeof documentSecurity!=='undefined'){payload['documentSecurity']=documentSecurity;}
|
||||
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('put',uri,{'content-type':'application/json',},payload);});}
|
||||
deleteCollection(databaseId,collectionId){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
|
@ -260,6 +257,15 @@ if(typeof required!=='undefined'){payload['required']=required;}
|
|||
if(typeof xdefault!=='undefined'){payload['default']=xdefault;}
|
||||
if(typeof array!=='undefined'){payload['array']=array;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('post',uri,{'content-type':'application/json',},payload);});}
|
||||
createDatetimeAttribute(databaseId,collectionId,key,required,xdefault,array){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof key==='undefined'){throw new AppwriteException('Missing required parameter: "key"');}
|
||||
if(typeof required==='undefined'){throw new AppwriteException('Missing required parameter: "required"');}
|
||||
let path='/databases/{databaseId}/collections/{collectionId}/attributes/datetime'.replace('{databaseId}',databaseId).replace('{collectionId}',collectionId);let payload={};if(typeof key!=='undefined'){payload['key']=key;}
|
||||
if(typeof required!=='undefined'){payload['required']=required;}
|
||||
if(typeof xdefault!=='undefined'){payload['default']=xdefault;}
|
||||
if(typeof array!=='undefined'){payload['array']=array;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('post',uri,{'content-type':'application/json',},payload);});}
|
||||
createEmailAttribute(databaseId,collectionId,key,required,xdefault,array){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof key==='undefined'){throw new AppwriteException('Missing required parameter: "key"');}
|
||||
|
@ -349,25 +355,23 @@ if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirect
|
|||
if(typeof orderAttributes!=='undefined'){payload['orderAttributes']=orderAttributes;}
|
||||
if(typeof orderTypes!=='undefined'){payload['orderTypes']=orderTypes;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
createDocument(databaseId,collectionId,documentId,data,read,write){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
createDocument(databaseId,collectionId,documentId,data,permissions){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof documentId==='undefined'){throw new AppwriteException('Missing required parameter: "documentId"');}
|
||||
if(typeof data==='undefined'){throw new AppwriteException('Missing required parameter: "data"');}
|
||||
let path='/databases/{databaseId}/collections/{collectionId}/documents'.replace('{databaseId}',databaseId).replace('{collectionId}',collectionId);let payload={};if(typeof documentId!=='undefined'){payload['documentId']=documentId;}
|
||||
if(typeof data!=='undefined'){payload['data']=data;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('post',uri,{'content-type':'application/json',},payload);});}
|
||||
getDocument(databaseId,collectionId,documentId){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof documentId==='undefined'){throw new AppwriteException('Missing required parameter: "documentId"');}
|
||||
let path='/databases/{databaseId}/collections/{collectionId}/documents/{documentId}'.replace('{databaseId}',databaseId).replace('{collectionId}',collectionId).replace('{documentId}',documentId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
updateDocument(databaseId,collectionId,documentId,data,read,write){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
updateDocument(databaseId,collectionId,documentId,data,permissions){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof documentId==='undefined'){throw new AppwriteException('Missing required parameter: "documentId"');}
|
||||
let path='/databases/{databaseId}/collections/{collectionId}/documents/{documentId}'.replace('{databaseId}',databaseId).replace('{collectionId}',collectionId).replace('{documentId}',documentId);let payload={};if(typeof data!=='undefined'){payload['data']=data;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('patch',uri,{'content-type':'application/json',},payload);});}
|
||||
deleteDocument(databaseId,collectionId,documentId){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
|
@ -696,14 +700,13 @@ if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
|
|||
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
|
||||
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
createBucket(bucketId,name,permission,read,write,enabled,maximumFileSize,allowedFileExtensions,encryption,antivirus){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
createBucket(bucketId,name,fileSecurity,permissions,enabled,maximumFileSize,allowedFileExtensions,encryption,antivirus){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
if(typeof permission==='undefined'){throw new AppwriteException('Missing required parameter: "permission"');}
|
||||
if(typeof fileSecurity==='undefined'){throw new AppwriteException('Missing required parameter: "fileSecurity"');}
|
||||
let path='/storage/buckets';let payload={};if(typeof bucketId!=='undefined'){payload['bucketId']=bucketId;}
|
||||
if(typeof name!=='undefined'){payload['name']=name;}
|
||||
if(typeof permission!=='undefined'){payload['permission']=permission;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
if(typeof fileSecurity!=='undefined'){payload['fileSecurity']=fileSecurity;}
|
||||
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
|
||||
if(typeof maximumFileSize!=='undefined'){payload['maximumFileSize']=maximumFileSize;}
|
||||
if(typeof allowedFileExtensions!=='undefined'){payload['allowedFileExtensions']=allowedFileExtensions;}
|
||||
|
@ -712,13 +715,12 @@ if(typeof antivirus!=='undefined'){payload['antivirus']=antivirus;}
|
|||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('post',uri,{'content-type':'application/json',},payload);});}
|
||||
getBucket(bucketId){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
let path='/storage/buckets/{bucketId}'.replace('{bucketId}',bucketId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
updateBucket(bucketId,name,permission,read,write,enabled,maximumFileSize,allowedFileExtensions,encryption,antivirus){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
updateBucket(bucketId,name,fileSecurity,permissions,enabled,maximumFileSize,allowedFileExtensions,encryption,antivirus){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
if(typeof permission==='undefined'){throw new AppwriteException('Missing required parameter: "permission"');}
|
||||
if(typeof fileSecurity==='undefined'){throw new AppwriteException('Missing required parameter: "fileSecurity"');}
|
||||
let path='/storage/buckets/{bucketId}'.replace('{bucketId}',bucketId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
|
||||
if(typeof permission!=='undefined'){payload['permission']=permission;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
if(typeof fileSecurity!=='undefined'){payload['fileSecurity']=fileSecurity;}
|
||||
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
|
||||
if(typeof maximumFileSize!=='undefined'){payload['maximumFileSize']=maximumFileSize;}
|
||||
if(typeof allowedFileExtensions!=='undefined'){payload['allowedFileExtensions']=allowedFileExtensions;}
|
||||
|
@ -735,13 +737,12 @@ if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
|
|||
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
|
||||
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
createFile(bucketId,fileId,file,read,write,onProgress=(progress)=>{}){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
createFile(bucketId,fileId,file,permissions,onProgress=(progress)=>{}){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
|
||||
if(typeof file==='undefined'){throw new AppwriteException('Missing required parameter: "file"');}
|
||||
let path='/storage/buckets/{bucketId}/files'.replace('{bucketId}',bucketId);let payload={};if(typeof fileId!=='undefined'){payload['fileId']=fileId;}
|
||||
if(typeof file!=='undefined'){payload['file']=file;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
const uri=new URL(this.client.config.endpoint+path);if(!(file instanceof File)){throw new AppwriteException('Parameter "file" has to be a File.');}
|
||||
const size=file.size;if(size<=Service.CHUNK_SIZE){return yield this.client.call('post',uri,{'content-type':'multipart/form-data',},payload);}
|
||||
let id=undefined;let response=undefined;const headers={'content-type':'multipart/form-data',};let counter=0;const totalCounters=Math.ceil(size/Service.CHUNK_SIZE);if(fileId!='unique()'){try{response=yield this.client.call('GET',new URL(this.client.config.endpoint+path+'/'+fileId),headers);counter=response.chunksUploaded;}
|
||||
|
@ -753,10 +754,9 @@ return response;});}
|
|||
getFile(bucketId,fileId){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
|
||||
let path='/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}',bucketId).replace('{fileId}',fileId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
updateFile(bucketId,fileId,read,write){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
updateFile(bucketId,fileId,permissions){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
|
||||
let path='/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}',bucketId).replace('{fileId}',fileId);let payload={};if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
let path='/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}',bucketId).replace('{fileId}',fileId);let payload={};if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('put',uri,{'content-type':'application/json',},payload);});}
|
||||
deleteFile(bucketId,fileId){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
|
||||
|
@ -3938,115 +3938,15 @@ params=formData;break;}
|
|||
return new Promise(function(resolve,reject){let request=new XMLHttpRequest(),key;request.withCredentials=true;request.open(method,path,true);for(key in headers){if(headers.hasOwnProperty(key)){request.setRequestHeader(key,headers[key]);}}
|
||||
request.onload=function(){if(4===request.readyState&&399>=request.status){let data=request.response;let contentType=this.getResponseHeader('content-type');contentType=contentType.substring(0,contentType.indexOf(';'));switch(contentType){case'application/json':data=JSON.parse(data);break;}
|
||||
resolve(data);}else{reject(new Error(request.statusText));}};if(progress){request.addEventListener('progress',progress);request.upload.addEventListener('progress',progress,false);}
|
||||
request.onerror=function(){reject(new Error("Network Error"));};request.send(params);})};return{'get':function(path,headers={},params={}){return call('GET',path+((params.length>0)?'?'+buildQuery(params):''),headers,{});},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress);},'put':function(path,headers={},params={},progress=null){return call('PUT',headers,params,progress);},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress);},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress);},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let analytics={create:function(id,source,activity,url){return http.post('/analytics',{'content-type':'application/json'},{id:id,source:source,activity:activity,url:url,version:env.VERSION,setup:env.SETUP});},};return{analytics:analytics,};},true);})(window);(function(window){"use strict";window.ls.container.set('console',function(window){var client=new Appwrite.Client();var endpoint=window.location.origin+'/v1';client.setEndpoint(endpoint).setProject('console').setLocale(APP_ENV.LOCALE);return{client:client,account:new Appwrite.Account(client),avatars:new Appwrite.Avatars(client),databases:new Appwrite.Databases(client),functions:new Appwrite.Functions(client),health:new Appwrite.Health(client),locale:new Appwrite.Locale(client),projects:new Appwrite.Projects(client),storage:new Appwrite.Storage(client),teams:new Appwrite.Teams(client),users:new Appwrite.Users(client)}},true);})(window);(function(window){"use strict";window.ls.container.set('date',function(){function format(format,timestamp){var jsdate,f
|
||||
var txtWords=['Sun','Mon','Tues','Wednes','Thurs','Fri','Satur','January','February','March','April','May','June','July','August','September','October','November','December']
|
||||
var formatChr=/\\?(.?)/gi
|
||||
var formatChrCb=function(t,s){return f[t]?f[t]():s}
|
||||
var _pad=function(n,c){n=String(n)
|
||||
while(n.length<c){n='0'+n}
|
||||
return n}
|
||||
f={d:function(){return _pad(f.j(),2)},D:function(){return f.l().slice(0,3)},j:function(){return jsdate.getDate()},l:function(){return txtWords[f.w()]+'day'},N:function(){return f.w()||7},S:function(){var j=f.j()
|
||||
var i=j%10
|
||||
if(i<=3&&parseInt((j%100)/10,10)===1){i=0}
|
||||
return['st','nd','rd'][i-1]||'th'},w:function(){return jsdate.getDay()},z:function(){var a=new Date(f.Y(),f.n()-1,f.j())
|
||||
var b=new Date(f.Y(),0,1)
|
||||
return Math.round((a-b)/864e5)},W:function(){var a=new Date(f.Y(),f.n()-1,f.j()-f.N()+3)
|
||||
var b=new Date(a.getFullYear(),0,4)
|
||||
return _pad(1+Math.round((a-b)/864e5/7),2)},F:function(){return txtWords[6+f.n()]},m:function(){return _pad(f.n(),2)},M:function(){return f.F().slice(0,3)},n:function(){return jsdate.getMonth()+1},t:function(){return(new Date(f.Y(),f.n(),0)).getDate()},L:function(){var j=f.Y()
|
||||
return j%4===0&j%100!==0|j%400===0},o:function(){var n=f.n()
|
||||
var W=f.W()
|
||||
var Y=f.Y()
|
||||
return Y+(n===12&&W<9?1:n===1&&W>9?-1:0)},Y:function(){return jsdate.getFullYear()},y:function(){return f.Y().toString().slice(-2)},a:function(){return jsdate.getHours()>11?'pm':'am'},A:function(){return f.a().toUpperCase()},B:function(){var H=jsdate.getUTCHours()*36e2
|
||||
var i=jsdate.getUTCMinutes()*60
|
||||
var s=jsdate.getUTCSeconds()
|
||||
return _pad(Math.floor((H+i+s+36e2)/86.4)%1e3,3)},g:function(){return f.G()%12||12},G:function(){return jsdate.getHours()},h:function(){return _pad(f.g(),2)},H:function(){return _pad(f.G(),2)},i:function(){return _pad(jsdate.getMinutes(),2)},s:function(){return _pad(jsdate.getSeconds(),2)},u:function(){return _pad(jsdate.getMilliseconds()*1000,6)},e:function(){var msg='Not supported (see source code of date() for timezone on how to add support)'
|
||||
throw new Error(msg)},I:function(){var a=new Date(f.Y(),0)
|
||||
var c=Date.UTC(f.Y(),0)
|
||||
var b=new Date(f.Y(),6)
|
||||
var d=Date.UTC(f.Y(),6)
|
||||
return((a-c)!==(b-d))?1:0},O:function(){var tzo=jsdate.getTimezoneOffset()
|
||||
var a=Math.abs(tzo)
|
||||
return(tzo>0?'-':'+')+_pad(Math.floor(a/60)*100+a%60,4)},P:function(){var O=f.O()
|
||||
return(O.substr(0,3)+':'+O.substr(3,2))},T:function(){return'UTC'},Z:function(){return-jsdate.getTimezoneOffset()*60},c:function(){return'Y-m-d\\TH:i:sP'.replace(formatChr,formatChrCb)},r:function(){return'D, d M Y H:i:s O'.replace(formatChr,formatChrCb)},U:function(){return jsdate/1000|0}}
|
||||
var _date=function(format,timestamp){jsdate=(timestamp===undefined?new Date():(timestamp instanceof Date)?new Date(timestamp):new Date(timestamp*1000))
|
||||
return format.replace(formatChr,formatChrCb)}
|
||||
return _date(format,timestamp)}
|
||||
function strtotime(text,now){var parsed
|
||||
var match
|
||||
var today
|
||||
var year
|
||||
var date
|
||||
var days
|
||||
var ranges
|
||||
var len
|
||||
var times
|
||||
var regex
|
||||
var i
|
||||
var fail=false
|
||||
if(!text){return fail}
|
||||
text=text.replace(/^\s+|\s+$/g,'').replace(/\s{2,}/g,' ').replace(/[\t\r\n]/g,'').toLowerCase()
|
||||
var pattern=new RegExp(['^(\\d{1,4})','([\\-\\.\\/:])','(\\d{1,2})','([\\-\\.\\/:])','(\\d{1,4})','(?:\\s(\\d{1,2}):(\\d{2})?:?(\\d{2})?)?','(?:\\s([A-Z]+)?)?$'].join(''))
|
||||
match=text.match(pattern)
|
||||
if(match&&match[2]===match[4]){if(match[1]>1901){switch(match[2]){case'-':if(match[3]>12||match[5]>31){return fail}
|
||||
return new Date(match[1],parseInt(match[3],10)-1,match[5],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000
|
||||
case'.':return fail
|
||||
case'/':if(match[3]>12||match[5]>31){return fail}
|
||||
return new Date(match[1],parseInt(match[3],10)-1,match[5],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000}}else if(match[5]>1901){switch(match[2]){case'-':if(match[3]>12||match[1]>31){return fail}
|
||||
return new Date(match[5],parseInt(match[3],10)-1,match[1],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000
|
||||
case'.':if(match[3]>12||match[1]>31){return fail}
|
||||
return new Date(match[5],parseInt(match[3],10)-1,match[1],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000
|
||||
case'/':if(match[1]>12||match[3]>31){return fail}
|
||||
return new Date(match[5],parseInt(match[1],10)-1,match[3],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000}}else{switch(match[2]){case'-':if(match[3]>12||match[5]>31||(match[1]<70&&match[1]>38)){return fail}
|
||||
year=match[1]>=0&&match[1]<=38?+match[1]+2000:match[1]
|
||||
return new Date(year,parseInt(match[3],10)-1,match[5],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000
|
||||
case'.':if(match[5]>=70){if(match[3]>12||match[1]>31){return fail}
|
||||
return new Date(match[5],parseInt(match[3],10)-1,match[1],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000}
|
||||
if(match[5]<60&&!match[6]){if(match[1]>23||match[3]>59){return fail}
|
||||
today=new Date()
|
||||
return new Date(today.getFullYear(),today.getMonth(),today.getDate(),match[1]||0,match[3]||0,match[5]||0,match[9]||0)/1000}
|
||||
return fail
|
||||
case'/':if(match[1]>12||match[3]>31||(match[5]<70&&match[5]>38)){return fail}
|
||||
year=match[5]>=0&&match[5]<=38?+match[5]+2000:match[5]
|
||||
return new Date(year,parseInt(match[1],10)-1,match[3],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000
|
||||
case':':if(match[1]>23||match[3]>59||match[5]>59){return fail}
|
||||
today=new Date()
|
||||
return new Date(today.getFullYear(),today.getMonth(),today.getDate(),match[1]||0,match[3]||0,match[5]||0)/1000}}}
|
||||
if(text==='now'){return now===null||isNaN(now)?new Date().getTime()/1000|0:now|0}
|
||||
if(!isNaN(parsed=Date.parse(text))){return parsed/1000|0}
|
||||
pattern=new RegExp(['^([0-9]{4}-[0-9]{2}-[0-9]{2})','[ t]','([0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?)','([\\+-][0-9]{2}(:[0-9]{2})?|z)'].join(''))
|
||||
match=text.match(pattern)
|
||||
if(match){if(match[4]==='z'){match[4]='Z'}else if(match[4].match(/^([+-][0-9]{2})$/)){match[4]=match[4]+':00'}
|
||||
if(!isNaN(parsed=Date.parse(match[1]+'T'+match[2]+match[4]))){return parsed/1000|0}}
|
||||
date=now?new Date(now*1000):new Date()
|
||||
days={'sun':0,'mon':1,'tue':2,'wed':3,'thu':4,'fri':5,'sat':6}
|
||||
ranges={'yea':'FullYear','mon':'Month','day':'Date','hou':'Hours','min':'Minutes','sec':'Seconds'}
|
||||
function lastNext(type,range,modifier){var diff
|
||||
var day=days[range]
|
||||
if(typeof day!=='undefined'){diff=day-date.getDay()
|
||||
if(diff===0){diff=7*modifier}else if(diff>0&&type==='last'){diff-=7}else if(diff<0&&type==='next'){diff+=7}
|
||||
date.setDate(date.getDate()+diff)}}
|
||||
function process(val){var splt=val.split(' ')
|
||||
var type=splt[0]
|
||||
var range=splt[1].substring(0,3)
|
||||
var typeIsNumber=/\d+/.test(type)
|
||||
var ago=splt[2]==='ago'
|
||||
var num=(type==='last'?-1:1)*(ago?-1:1)
|
||||
if(typeIsNumber){num*=parseInt(type,10)}
|
||||
if(ranges.hasOwnProperty(range)&&!splt[1].match(/^mon(day|\.)?$/i)){return date['set'+ranges[range]](date['get'+ranges[range]]()+num)}
|
||||
if(range==='wee'){return date.setDate(date.getDate()+(num*7))}
|
||||
if(type==='next'||type==='last'){lastNext(type,range,num)}else if(!typeIsNumber){return false}
|
||||
return true}
|
||||
times='(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec'+'|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?'+'|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)'
|
||||
regex='([+-]?\\d+\\s'+times+'|'+'(last|next)\\s'+times+')(\\sago)?'
|
||||
match=text.match(new RegExp(regex,'gi'))
|
||||
if(!match){return fail}
|
||||
for(i=0,len=match.length;i<len;i++){if(!process(match[i])){return fail}}
|
||||
return(date.getTime()/1000)}
|
||||
return{format:format,strtotime:strtotime}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,to){if(value&&Array.isArray(value)&&to!=='array'){value=value.map(element=>cast(element,to));return value;}
|
||||
request.onerror=function(){reject(new Error("Network Error"));};request.send(params);})};return{'get':function(path,headers={},params={}){return call('GET',path+((params.length>0)?'?'+buildQuery(params):''),headers,{});},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress);},'put':function(path,headers={},params={},progress=null){return call('PUT',headers,params,progress);},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress);},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress);},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let analytics={create:function(id,source,activity,url){return http.post('/analytics',{'content-type':'application/json'},{id:id,source:source,activity:activity,url:url,version:env.VERSION,setup:env.SETUP});},};return{analytics:analytics,};},true);})(window);(function(window){"use strict";window.ls.container.set('console',function(window){var client=new Appwrite.Client();var endpoint=window.location.origin+'/v1';client.setEndpoint(endpoint).setProject('console').setLocale(APP_ENV.LOCALE);return{client:client,account:new Appwrite.Account(client),avatars:new Appwrite.Avatars(client),databases:new Appwrite.Databases(client),functions:new Appwrite.Functions(client),health:new Appwrite.Health(client),locale:new Appwrite.Locale(client),projects:new Appwrite.Projects(client),storage:new Appwrite.Storage(client),teams:new Appwrite.Teams(client),users:new Appwrite.Users(client)}},true);})(window);(function(window){"use strict";window.ls.container.set('date',function(){function format(format,datetime){if(!datetime){return null;}
|
||||
return new Intl.DateTimeFormat('en-US',{timeZone:'UTC',hourCycle:'h24',...format}).format(new Date(datetime));}
|
||||
return{format:format,}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,from,to,){if(value&&Array.isArray(value)&&to!=='array'){value=value.map(element=>cast(element,from,to));return value;}
|
||||
switch(to){case'int':case'integer':value=parseInt(value);break;case'numeric':value=Number(value);break;case'float':value=parseFloat(value);break;case'string':value=value.toString();if(value.length===0){value=null;}
|
||||
break;case'json':value=(value)?JSON.parse(value):[];break;case'array':value=(value&&value.constructor&&value.constructor===Array)?value:[value];break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
|
||||
break;case'json':value=(value)?JSON.parse(value):[];break;case'array':if(value&&value.constructor&&value.constructor===Array){break;}
|
||||
if(from==='csv'){if(value.length===0){value=[];}else{value=value.split(',');}}else{value=[value];}
|
||||
break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
|
||||
return value;}
|
||||
function toJson(element,json){json=json||{};let name=element.getAttribute('name');let type=element.getAttribute('type');let castTo=element.getAttribute('data-cast-to');let ref=json;if(name&&'FORM'!==element.tagName){if(name.startsWith('[')){let splitName=name.split('.');if(splitName.length>1&&splitName[0].endsWith(']')){name=splitName[splitName.length-1];}}
|
||||
function toJson(element,json){json=json||{};let name=element.getAttribute('name');let type=element.getAttribute('type');let castTo=element.getAttribute('data-cast-to');let castFrom=element.getAttribute('data-cast-from');let ref=json;if(name&&'FORM'!==element.tagName){if(name.startsWith('[')){let splitName=name.split('.');if(splitName.length>1&&splitName[0].endsWith(']')){name=splitName[splitName.length-1];}}
|
||||
if('FIELDSET'===element.tagName){if(castTo==='object'){if(json[name]===undefined){json[name]={};}
|
||||
ref=json[name];}
|
||||
else{if(!Array.isArray(json[name])){json[name]=[];}
|
||||
|
@ -4059,7 +3959,7 @@ else if('file'===type){json[name]=element.files[0];}
|
|||
else if(undefined!==element.value){if((json[name]!==undefined)&&(!Array.isArray(json[name]))){json[name]=[json[name]];}
|
||||
if(Array.isArray(json[name])){json[name].push(element.value);}
|
||||
else{json[name]=element.value;}}
|
||||
json[name]=cast(json[name],castTo);}}
|
||||
json[name]=cast(json[name],castFrom,castTo);}}
|
||||
for(let i=0;i<element.children.length;i++){if(Array.isArray(ref)){ref.push({});toJson(element.children[i],ref[ref.length]);}
|
||||
else{toJson(element.children[i],ref);}}
|
||||
return json;}
|
||||
|
@ -4072,7 +3972,7 @@ return false;};return{isRTL:isRTL,};},true);})(window);(function(window){"use st
|
|||
let size=element.dataset["size"]||80;let name=$value.name||$value||"";name=(typeof name!=='string')?'--':name;return def="/v1/avatars/initials?project=console"+"&name="+
|
||||
encodeURIComponent(name)+"&width="+
|
||||
size+"&height="+
|
||||
size;}).add("selectedCollection",function($value,router){return $value===router.params.collectionId?"selected":"";}).add("selectedDocument",function($value,router){return $value===router.params.documentId?"selected":"";}).add("localeString",function($value){$value=parseInt($value);return!Number.isNaN($value)?$value.toLocaleString():"";}).add("date",function($value,date){return $value?date.format("Y-m-d",$value):"";}).add("dateTime",function($value,date){return $value?date.format("Y-m-d H:i",$value):"";}).add("dateText",function($value,date){return $value?date.format("d M Y",$value):"";}).add("timeSince",function($value){$value=$value*1000;let seconds=Math.floor((Date.now()-$value)/1000);let unit="second";let direction="ago";if(seconds<0){seconds=-seconds;direction="from now";}
|
||||
size;}).add("selectedCollection",function($value,router){return $value===router.params.collectionId?"selected":"";}).add("selectedDocument",function($value,router){return $value===router.params.documentId?"selected":"";}).add("localeString",function($value){$value=parseInt($value);return!Number.isNaN($value)?$value.toLocaleString():"";}).add("dateTime",function($value,date){return $value?date.format({year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit'},$value):"";}).add("date",function($value,date){return $value?date.format({year:'numeric',month:'short',day:'2-digit',},$value):"";}).add("timeSince",function($value){$value=new Date($value).getTime();let now=new Date();now.setMinutes(now.getMinutes()+now.getTimezoneOffset());let timestamp=new Date(now.toISOString()).getTime();let seconds=Math.floor((timestamp-$value)/1000);let unit="second";let direction="ago";if(seconds<0){seconds=-seconds;direction="from now";}
|
||||
let value=seconds;if(seconds>=31536000){value=Math.floor(seconds/31536000);unit="year";}
|
||||
else if(seconds>=86400){value=Math.floor(seconds/86400);unit="day";}
|
||||
else if(seconds>=3600){value=Math.floor(seconds/3600);unit="hour";}
|
||||
|
@ -4110,11 +4010,10 @@ if(forcePlaces!==false){rounded=Number(rounded).toFixed(forcePlaces);}
|
|||
return rounded+abbr;}
|
||||
window.ls.container.get("view").add({selector:"data-acl",controller:function(element,document,router,alerts){document.body.classList.remove("console");document.body.classList.remove("home");document.body.classList.add(router.getCurrent().view.scope);if(!router.getCurrent().view.project){document.body.classList.add("hide-nav");document.body.classList.remove("show-nav");}else{document.body.classList.add("show-nav");document.body.classList.remove("hide-nav");}
|
||||
if("/console"===router.getCurrent().path){document.body.classList.add("index");}else{document.body.classList.remove("index");}}}).add({selector:"data-prism",controller:function(window,document,element,alerts){Prism.highlightElement(element);let copy=document.createElement("i");copy.className="icon-docs copy";copy.title="Copy to Clipboard";copy.textContent="Click Here to Copy";copy.addEventListener("click",function(){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
|
||||
window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendChild(copy);}});(function(window){document.addEventListener('alpine:init',()=>{Alpine.store('uploader',{_files:[],files(){return(this._files??[]).filter((file)=>!file.cancelled);},isOpen:true,init(){window.addEventListener('beforeunload',(event)=>{if(this.hasOngoingUploads()){let confirmationMessage="There are incomplete uploads, are you sure you want to leave?";event.returnValue=confirmationMessage;return confirmationMessage;}});},cancelAll(){if(this.hasOngoingUploads()?confirm("Are you sure? This will cancel and remove any ongoing uploads?"):true){this._files.forEach(file=>{if(file.completed||file.failed){this.removeFile(file.id);}else{this.updateFile(file.id,{cancelled:true});}});}},hasOngoingUploads(){let ongoing=false;this._files.some((file)=>{if(!file.completed&&!file.failed){ongoing=true;return;}});return ongoing;},toggle(){this.isOpen=!this.isOpen;},addFile(file){this._files.push(file);},updateFile(id,file){this._files=this._files.map((oldFile)=>id==oldFile.id?{...oldFile,...file}:oldFile);},removeFile(id){const file=this.getFile(id)??{};if(file.completed||file.failed){this._files=this._files.filter((file)=>file.id!==id);}else{if(confirm("Are you sure you want to cancel the upload?")){this.updateFile(id,{cancelled:true});}}},getFile(id){return this._files.find((file)=>file.id===id);},async uploadFile(target){const formData=new FormData(target);const sdk=window.ls.container.get('sdk');const bucketId=formData.get('bucketId');const file=formData.get('file');const fileId=formData.get('fileId');let id=fileId==='unique()'?performance.now():fileId;let read=formData.get('read');if(!file||!fileId){return;}
|
||||
if(read){read=JSON.parse(read);}
|
||||
let write=formData.get('write');if(write){write=JSON.parse(write);}
|
||||
window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendChild(copy);}});(function(window){document.addEventListener('alpine:init',()=>{Alpine.store('uploader',{_files:[],files(){return(this._files??[]).filter((file)=>!file.cancelled);},isOpen:true,init(){window.addEventListener('beforeunload',(event)=>{if(this.hasOngoingUploads()){let confirmationMessage="There are incomplete uploads, are you sure you want to leave?";event.returnValue=confirmationMessage;return confirmationMessage;}});},cancelAll(){if(this.hasOngoingUploads()?confirm("Are you sure? This will cancel and remove any ongoing uploads?"):true){this._files.forEach(file=>{if(file.completed||file.failed){this.removeFile(file.id);}else{this.updateFile(file.id,{cancelled:true});}});}},hasOngoingUploads(){let ongoing=false;this._files.some((file)=>{if(!file.completed&&!file.failed){ongoing=true;return;}});return ongoing;},toggle(){this.isOpen=!this.isOpen;},addFile(file){this._files.push(file);},updateFile(id,file){this._files=this._files.map((oldFile)=>id==oldFile.id?{...oldFile,...file}:oldFile);},removeFile(id){const file=this.getFile(id)??{};if(file.completed||file.failed){this._files=this._files.filter((file)=>file.id!==id);}else{if(confirm("Are you sure you want to cancel the upload?")){this.updateFile(id,{cancelled:true});}}},getFile(id){return this._files.find((file)=>file.id===id);},async uploadFile(target){const formData=new FormData(target);const sdk=window.ls.container.get('sdk');const bucketId=formData.get('bucketId');const file=formData.get('file');const fileId=formData.get('fileId');let id=fileId==='unique()'?performance.now():fileId;if(!file||!fileId){return;}
|
||||
let permissions=formData.get('permissions');if(permissions){permissions=permissions.split(',');}
|
||||
if(this.getFile(id)){this.updateFile(id,{name:file.name,completed:false,failed:false,cancelled:false,error:"",});}else{this.addFile({id:id,name:file.name,progress:0,completed:false,failed:false,cancelled:false,error:"",});}
|
||||
target.reset();try{const response=await sdk.storage.createFile(bucketId,fileId,file,read,write,(progress)=>{this.updateFile(id,{id:progress.$id,progress:Math.round(progress.progress),error:"",});id=progress.$id;const file=this.getFile(id)??{};if(file.cancelled===true){throw'USER_CANCELLED';}});const existingFile=this.getFile(id)??{};if(existingFile.cancelled){this.updateFile(id,{id:response.$id,name:response.name,failed:false,});id=response.$id;throw'USER_CANCELLED'}else{this.updateFile(id,{id:response.$id,name:response.name,progress:100,completed:true,failed:false,});id=response.$id;}
|
||||
target.reset();try{const response=await sdk.storage.createFile(bucketId,fileId,file,permissions,(progress)=>{this.updateFile(id,{id:progress.$id,progress:Math.round(progress.progress),error:"",});id=progress.$id;const file=this.getFile(id)??{};if(file.cancelled===true){throw'USER_CANCELLED';}});const existingFile=this.getFile(id)??{};if(existingFile.cancelled){this.updateFile(id,{id:response.$id,name:response.name,failed:false,});id=response.$id;throw'USER_CANCELLED'}else{this.updateFile(id,{id:response.$id,name:response.name,progress:100,completed:true,failed:false,});id=response.$id;}
|
||||
document.dispatchEvent(new CustomEvent('storage.createFile'));}catch(error){if(error==='USER_CANCELLED'){await sdk.storage.deleteFile(bucketId,id);this.updateFile(id,{cancelled:false,failed:true,});this.removeFile(id);}else{this.updateFile(id,{id:id,failed:true,error:error.message??error});}
|
||||
document.dispatchEvent(new CustomEvent('storage.createFile'));}}});});})(window);(function(window){document.addEventListener('alpine:init',()=>{Alpine.data('events',()=>({events:new Set(),selected:null,action:null,type:null,subType:null,subSubType:null,resource:null,resourceName:'',subResource:null,subResourceName:'',subSubResource:null,subSubResourceName:'',hasResource:false,hasSubResource:false,hasSubSubResource:false,attribute:null,hasAttribute:false,attributes:[],load(events){this.events=new Set(events);},reset(){this.hasResource=this.hasSubResource=this.hasSubSubResource=this.hasAttribute=false;this.type=this.subType=this.subResource=this.resource=this.attribute=this.selected=this.action=null;},setEvent(){this.hasResource=this.hasSubResource=this.hasSubSubResource=this.hasAttribute=this.action=false;if(!this.selected){this.reset();return;}
|
||||
let[type,action]=this.selected.split('.');switch(type){case'users':if(action==='update'){this.hasAttribute=true;this.attributes=['email','name','password','status','prefs']}
|
||||
|
@ -4124,7 +4023,14 @@ this.action=action;},showModal(modal){document.documentElement.classList.add("mo
|
|||
if(this.hasSubSubResource){event+=`.${this.subSubType}.${this.subSubResource ? this.subSubResource : '*'}`;}
|
||||
if(this.action){event+=`.${this.action}`;}
|
||||
if(this.attribute){event+=`.${this.attribute}`;}
|
||||
this.events.add(event);this.reset();},removeEvent(value){this.events.delete(value);}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
|
||||
this.events.add(event);this.reset();},removeEvent(value){this.events.delete(value);}}));});})(window);(function(window){document.addEventListener('alpine:init',()=>{Alpine.data('permissionsMatrix',()=>({permissions:[],rawPermissions:[],load(permissions){if(permissions===undefined){return;}
|
||||
this.rawPermissions=permissions;permissions.map(p=>{let{type,role}=this.parsePermission(p);type=this.parseInputPermission(type);let index=-1;let existing=this.permissions.find((p,idx)=>{if(p.role===role){index=idx;return true;}})
|
||||
if(existing===undefined){this.permissions.push({role,[type]:true,});}
|
||||
if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId,role,permissions){if(!document.getElementById(formId).reportValidity()){return;}
|
||||
Object.entries(permissions).forEach(entry=>{let[type,enabled]=entry;type=this.parseOutputPermission(type);if(enabled){this.rawPermissions.push(this.buildPermission(type,role));}});this.permissions.push({role,...permissions,});this.reset();},updatePermission(index){setTimeout(()=>{const permission=this.permissions[index];Object.keys(permission).forEach(key=>{if(key==='role'){return;}
|
||||
const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';}
|
||||
return key;},parseOutputPermission(key){if(key==='xdelete'){return'delete';}
|
||||
return key;}}));Alpine.data('permissionsRow',()=>({role:'',read:false,create:false,update:false,xdelete:false,reset(){this.role='';this.read=this.create=this.update=this.xdelete=false;}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
|
||||
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},6000);};},redirect:function(url){return function(router){if(url==="/console"){window.location=url;return;}
|
||||
router.change(url||"/");};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
|
||||
return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
|
||||
|
@ -4170,9 +4076,9 @@ button.addEventListener("click",function(){var clone=document.createElement(elem
|
|||
clone.innerHTML=template;clone.className=element.className;var input=clone.querySelector("input, select, textarea");view.render(clone);if(debug){console.log('Debug: clone: ',clone);console.log('Debug: target: ',target);}
|
||||
if(target){target.appendChild(clone);}else{button.parentNode.insertBefore(clone,button);}
|
||||
if(input){input.focus();}
|
||||
Array.prototype.slice.call(clone.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){clone.parentNode.removeChild(clone);obj.scrollIntoView({behavior:"smooth"});});});Array.prototype.slice.call(clone.querySelectorAll("[data-up]")).map(function(obj){obj.addEventListener("click",function(){if(clone.previousElementSibling){clone.parentNode.insertBefore(clone,clone.previousElementSibling);obj.scrollIntoView({behavior:"smooth"});}});});Array.prototype.slice.call(clone.querySelectorAll("[data-down]")).map(function(obj){obj.addEventListener("click",function(){if(clone.nextElementSibling){clone.parentNode.insertBefore(clone.nextElementSibling,clone);obj.scrollIntoView({behavior:"smooth"});}});});});element.parentNode.insertBefore(button,element.nextSibling);element.parentNode.removeChild(element);button.form.addEventListener('reset',function(event){target.innerHTML='';if(first){button.click();}});if(first){button.click();}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-add",repeat:false,controller:function(element,view,container,document){for(var i=0;i<element.children.length;i++){let button=document.createElement("button");let template=element.children[i].cloneNode(true);let as=element.getAttribute('data-ls-as');let counter=0;button.type="button";button.innerText="Add";button.classList.add("reverse");button.classList.add("margin-end-small");button.addEventListener('click',function(){container.addNamespace(as,'new-'+counter++);console.log(container.namespaces,container.get(as),as);container.set(as,null,true,true);let child=template.cloneNode(true);view.render(child);element.appendChild(child);element.style.visibility='visible';let inputs=child.querySelectorAll('input,textarea');for(let index=0;index<inputs.length;++index){if(inputs[index].type!=='hidden'){inputs[index].focus();break;}}});element.after(button);}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-chart",controller:function(element,container,date,document){let wrapper=document.createElement("div");let child=document.createElement("canvas");let sources=element.getAttribute('data-forms-chart');let width=element.getAttribute('data-width')||500;let height=element.getAttribute('data-height')||175;let showXAxis=element.getAttribute('data-show-x-axis')||false;let showYAxis=element.getAttribute('data-show-y-axis')||false;let colors=(element.getAttribute('data-colors')||'blue,green,orange,red').split(',');let themes={'blue':'#29b5d9','green':'#4eb55b','orange':'#fba233','red':'#dc3232','create':'#00b680','read':'#009cde','update':'#696fd7','delete':'#da5d95',};let range={'24h':'H:i','7d':'d F Y','30d':'d F Y','90d':'d F Y'}
|
||||
Array.prototype.slice.call(clone.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){clone.parentNode.removeChild(clone);obj.scrollIntoView({behavior:"smooth"});});});Array.prototype.slice.call(clone.querySelectorAll("[data-up]")).map(function(obj){obj.addEventListener("click",function(){if(clone.previousElementSibling){clone.parentNode.insertBefore(clone,clone.previousElementSibling);obj.scrollIntoView({behavior:"smooth"});}});});Array.prototype.slice.call(clone.querySelectorAll("[data-down]")).map(function(obj){obj.addEventListener("click",function(){if(clone.nextElementSibling){clone.parentNode.insertBefore(clone.nextElementSibling,clone);obj.scrollIntoView({behavior:"smooth"});}});});});element.parentNode.insertBefore(button,element.nextSibling);element.parentNode.removeChild(element);button.form.addEventListener('reset',function(event){target.innerHTML='';if(first){button.click();}});if(first){button.click();}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-add",repeat:false,controller:function(element,view,container,document){for(var i=0;i<element.children.length;i++){let button=document.createElement("button");let template=element.children[i].cloneNode(true);let as=element.getAttribute('data-ls-as');let counter=0;button.type="button";button.innerText="Add";button.classList.add("reverse");button.classList.add("margin-end-small");button.addEventListener('click',function(){container.addNamespace(as,'new-'+counter++);console.log(container.namespaces,container.get(as),as);container.set(as,null,true,true);let child=template.cloneNode(true);view.render(child);element.appendChild(child);element.style.visibility='visible';let inputs=child.querySelectorAll('input,textarea');for(let index=0;index<inputs.length;++index){if(inputs[index].type!=='hidden'){inputs[index].focus();break;}}});element.after(button);}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-chart",controller:function(element,container,date,document){let wrapper=document.createElement("div");let child=document.createElement("canvas");let sources=element.getAttribute('data-forms-chart');let width=element.getAttribute('data-width')||500;let height=element.getAttribute('data-height')||175;let showXAxis=element.getAttribute('data-show-x-axis')||false;let showYAxis=element.getAttribute('data-show-y-axis')||false;let colors=(element.getAttribute('data-colors')||'blue,green,orange,red').split(',');let themes={'blue':'#29b5d9','green':'#4eb55b','orange':'#fba233','red':'#dc3232','create':'#00b680','read':'#009cde','update':'#696fd7','delete':'#da5d95',};let range={'24h':{hour:'2-digit',minute:'2-digit'},'7d':{year:'numeric',month:'short',day:'2-digit',},'30d':{year:'numeric',month:'short',day:'2-digit',},'90d':{year:'numeric',month:'short',day:'2-digit',}}
|
||||
let ticksCount=5;element.parentNode.insertBefore(wrapper,element.nextSibling);wrapper.classList.add('content');child.width=width;child.height=height;sources=sources.split(',');wrapper.appendChild(child);let chart=null;let check=function(){let config={type:"line",data:{labels:[],datasets:[]},options:{animation:{duration:0},responsive:true,hover:{mode:"nearest",intersect:false},scales:{x:{display:showXAxis},y:{display:showYAxis,min:0,ticks:{count:ticksCount,fontColor:"#8f8f8f"},}},plugins:{title:{display:false,text:"Stats"},legend:{display:false},tooltip:{mode:"index",intersect:false,caretPadding:0},}}};let highest=0;for(let i=0;i<sources.length;i++){let label=sources[i].substring(0,sources[i].indexOf('='));let path=sources[i].substring(sources[i].indexOf('=')+1);let usage=container.get('usage');let data=usage[path];let value=JSON.parse(element.value);config.data.labels[i]=label;config.data.datasets[i]={};config.data.datasets[i].label=label;config.data.datasets[i].borderColor=themes[colors[i]];config.data.datasets[i].backgroundColor=themes[colors[i]]+'36';config.data.datasets[i].borderWidth=2;config.data.datasets[i].data=[0,0,0,0,0,0,0];config.data.datasets[i].fill=true;if(!data){return;}
|
||||
let dateFormat=(value.range&&range[value.range])?range[value.range]:'d F Y';for(let x=0;x<data.length;x++){if(data[x].value>highest){highest=data[x].value;}
|
||||
let dateFormat=(value.range&&range[value.range])?range[value.range]:{year:'numeric',month:'short',day:'2-digit',};for(let x=0;x<data.length;x++){if(data[x].value>highest){highest=data[x].value;}
|
||||
config.data.datasets[i].data[x]=data[x].value;config.data.labels[x]=date.format(dateFormat,data[x].date);}}
|
||||
if(highest==0){config.options.scales.y.ticks.stepSize=1;config.options.scales.y.max=ticksCount;}else{highest=Math.ceil(highest/ticksCount)*ticksCount;config.options.scales.y.ticks.stepSize=highest/ticksCount;config.options.scales.y.max=highest;}
|
||||
if(chart){chart.destroy();}
|
||||
|
@ -4245,7 +4151,8 @@ var file=document.createElement("li");var image=document.createElement("img");im
|
|||
result.bucketId+"/files/"+
|
||||
result.fileId+"/preview?width="+
|
||||
previewWidth+"&height="+
|
||||
previewHeight+"&project="+project+"&mode=admin";image.alt=previewAlt;file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);element.value='';};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=JSON.stringify(result);};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile('default','unique()',files[0],read,write).then(function(response){onComplete(message);render({bucketId:response.bucketId,fileId:response.$id});},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
|
||||
previewHeight+"&project="+project+"&mode=admin";image.alt=previewAlt;file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);element.value='';};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=JSON.stringify(result);};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var permissions=JSON.parse(expression.parse(element.dataset["permissions"]||"[]"))
|
||||
sdk.storage.createFile('default','unique()',files[0],permissions).then(function(response){onComplete(message);render({bucketId:response.bucketId,fileId:response.$id});},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
|
||||
render(element.value);wrapper.scrollIntoView();});upload.addEventListener("keypress",function(){input.click();});element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(preview);wrapper.appendChild(progress);wrapper.appendChild(upload);upload.appendChild(input);render(output);if(searchButton){let searchOpen=document.createElement("button");searchOpen.type='button';searchOpen.innerHTML='<i class="icon icon-search"></i> Search';searchOpen.classList.add('reverse');let path=container.scope(searchButton);searchOpen.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent("open-file-search",{bubbles:false,cancelable:true}));});wrapper.appendChild(searchOpen);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-cookies",controller:function(element,alerts,cookie,env){if(!cookie.get("cookie-alert")){let text=element.dataset["cookies"]||"";alerts.add({text:text,class:"cookie-alert",link:env.HOME+"/policy/cookies",label:'Learn More',callback:function(){cookie.set("cookie-alert","true",365*10);}},0);}}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-copy',repeat:false,controller:function(document,element,alerts){let button=document.createElement("i");button.type="button";button.title="Copy to Clipboard";button.className=element.getAttribute("data-class")||"icon-docs note copy";button.style.cursor="pointer";element.parentNode.insertBefore(button,element.nextSibling);let copy=function(event){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
|
||||
window.getSelection().removeAllRanges();};button.addEventListener("click",copy);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-page-title",repeat:true,controller:function(element,document,expression){document.title=expression.parse(element.getAttribute("data-page-title"))||document.title;}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-to',repeat:false,controller:function(element,window){let button=window.document.createElement('button');button.className='scroll-to icon-up-dir';button.alt='Back To Top';button.title='Back To Top';button.addEventListener('click',function(){element.scrollIntoView(true,{behavior:'smooth'});button.blur();},false);element.appendChild(button);}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-direction',repeat:false,controller:function(element,window){let position=0;let check=function(){let direction=window.document.documentElement.scrollTop;if(direction>position){element.classList.remove('scroll-to-top')
|
||||
element.classList.add('scroll-to-bottom')}
|
||||
|
|
68
public/dist/scripts/app-dep.js
vendored
68
public/dist/scripts/app-dep.js
vendored
|
@ -220,29 +220,26 @@ if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
|
|||
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
|
||||
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
createCollection(databaseId,collectionId,name,permission,read,write){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
createCollection(databaseId,collectionId,name,permissions,documentSecurity){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
if(typeof permission==='undefined'){throw new AppwriteException('Missing required parameter: "permission"');}
|
||||
if(typeof read==='undefined'){throw new AppwriteException('Missing required parameter: "read"');}
|
||||
if(typeof write==='undefined'){throw new AppwriteException('Missing required parameter: "write"');}
|
||||
if(typeof permissions==='undefined'){throw new AppwriteException('Missing required parameter: "permissions"');}
|
||||
if(typeof documentSecurity==='undefined'){throw new AppwriteException('Missing required parameter: "documentSecurity"');}
|
||||
let path='/databases/{databaseId}/collections'.replace('{databaseId}',databaseId);let payload={};if(typeof collectionId!=='undefined'){payload['collectionId']=collectionId;}
|
||||
if(typeof name!=='undefined'){payload['name']=name;}
|
||||
if(typeof permission!=='undefined'){payload['permission']=permission;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
if(typeof documentSecurity!=='undefined'){payload['documentSecurity']=documentSecurity;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('post',uri,{'content-type':'application/json',},payload);});}
|
||||
getCollection(databaseId,collectionId){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
let path='/databases/{databaseId}/collections/{collectionId}'.replace('{databaseId}',databaseId).replace('{collectionId}',collectionId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
updateCollection(databaseId,collectionId,name,permission,read,write,enabled){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
updateCollection(databaseId,collectionId,name,documentSecurity,permissions,enabled){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
if(typeof permission==='undefined'){throw new AppwriteException('Missing required parameter: "permission"');}
|
||||
if(typeof documentSecurity==='undefined'){throw new AppwriteException('Missing required parameter: "documentSecurity"');}
|
||||
let path='/databases/{databaseId}/collections/{collectionId}'.replace('{databaseId}',databaseId).replace('{collectionId}',collectionId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
|
||||
if(typeof permission!=='undefined'){payload['permission']=permission;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
if(typeof documentSecurity!=='undefined'){payload['documentSecurity']=documentSecurity;}
|
||||
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('put',uri,{'content-type':'application/json',},payload);});}
|
||||
deleteCollection(databaseId,collectionId){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
|
@ -260,6 +257,15 @@ if(typeof required!=='undefined'){payload['required']=required;}
|
|||
if(typeof xdefault!=='undefined'){payload['default']=xdefault;}
|
||||
if(typeof array!=='undefined'){payload['array']=array;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('post',uri,{'content-type':'application/json',},payload);});}
|
||||
createDatetimeAttribute(databaseId,collectionId,key,required,xdefault,array){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof key==='undefined'){throw new AppwriteException('Missing required parameter: "key"');}
|
||||
if(typeof required==='undefined'){throw new AppwriteException('Missing required parameter: "required"');}
|
||||
let path='/databases/{databaseId}/collections/{collectionId}/attributes/datetime'.replace('{databaseId}',databaseId).replace('{collectionId}',collectionId);let payload={};if(typeof key!=='undefined'){payload['key']=key;}
|
||||
if(typeof required!=='undefined'){payload['required']=required;}
|
||||
if(typeof xdefault!=='undefined'){payload['default']=xdefault;}
|
||||
if(typeof array!=='undefined'){payload['array']=array;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('post',uri,{'content-type':'application/json',},payload);});}
|
||||
createEmailAttribute(databaseId,collectionId,key,required,xdefault,array){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof key==='undefined'){throw new AppwriteException('Missing required parameter: "key"');}
|
||||
|
@ -349,25 +355,23 @@ if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirect
|
|||
if(typeof orderAttributes!=='undefined'){payload['orderAttributes']=orderAttributes;}
|
||||
if(typeof orderTypes!=='undefined'){payload['orderTypes']=orderTypes;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
createDocument(databaseId,collectionId,documentId,data,read,write){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
createDocument(databaseId,collectionId,documentId,data,permissions){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof documentId==='undefined'){throw new AppwriteException('Missing required parameter: "documentId"');}
|
||||
if(typeof data==='undefined'){throw new AppwriteException('Missing required parameter: "data"');}
|
||||
let path='/databases/{databaseId}/collections/{collectionId}/documents'.replace('{databaseId}',databaseId).replace('{collectionId}',collectionId);let payload={};if(typeof documentId!=='undefined'){payload['documentId']=documentId;}
|
||||
if(typeof data!=='undefined'){payload['data']=data;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('post',uri,{'content-type':'application/json',},payload);});}
|
||||
getDocument(databaseId,collectionId,documentId){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof documentId==='undefined'){throw new AppwriteException('Missing required parameter: "documentId"');}
|
||||
let path='/databases/{databaseId}/collections/{collectionId}/documents/{documentId}'.replace('{databaseId}',databaseId).replace('{collectionId}',collectionId).replace('{documentId}',documentId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
updateDocument(databaseId,collectionId,documentId,data,read,write){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
updateDocument(databaseId,collectionId,documentId,data,permissions){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof documentId==='undefined'){throw new AppwriteException('Missing required parameter: "documentId"');}
|
||||
let path='/databases/{databaseId}/collections/{collectionId}/documents/{documentId}'.replace('{databaseId}',databaseId).replace('{collectionId}',collectionId).replace('{documentId}',documentId);let payload={};if(typeof data!=='undefined'){payload['data']=data;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('patch',uri,{'content-type':'application/json',},payload);});}
|
||||
deleteDocument(databaseId,collectionId,documentId){return __awaiter(this,void 0,void 0,function*(){if(typeof databaseId==='undefined'){throw new AppwriteException('Missing required parameter: "databaseId"');}
|
||||
if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
|
@ -696,14 +700,13 @@ if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
|
|||
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
|
||||
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
createBucket(bucketId,name,permission,read,write,enabled,maximumFileSize,allowedFileExtensions,encryption,antivirus){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
createBucket(bucketId,name,fileSecurity,permissions,enabled,maximumFileSize,allowedFileExtensions,encryption,antivirus){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
if(typeof permission==='undefined'){throw new AppwriteException('Missing required parameter: "permission"');}
|
||||
if(typeof fileSecurity==='undefined'){throw new AppwriteException('Missing required parameter: "fileSecurity"');}
|
||||
let path='/storage/buckets';let payload={};if(typeof bucketId!=='undefined'){payload['bucketId']=bucketId;}
|
||||
if(typeof name!=='undefined'){payload['name']=name;}
|
||||
if(typeof permission!=='undefined'){payload['permission']=permission;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
if(typeof fileSecurity!=='undefined'){payload['fileSecurity']=fileSecurity;}
|
||||
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
|
||||
if(typeof maximumFileSize!=='undefined'){payload['maximumFileSize']=maximumFileSize;}
|
||||
if(typeof allowedFileExtensions!=='undefined'){payload['allowedFileExtensions']=allowedFileExtensions;}
|
||||
|
@ -712,13 +715,12 @@ if(typeof antivirus!=='undefined'){payload['antivirus']=antivirus;}
|
|||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('post',uri,{'content-type':'application/json',},payload);});}
|
||||
getBucket(bucketId){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
let path='/storage/buckets/{bucketId}'.replace('{bucketId}',bucketId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
updateBucket(bucketId,name,permission,read,write,enabled,maximumFileSize,allowedFileExtensions,encryption,antivirus){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
updateBucket(bucketId,name,fileSecurity,permissions,enabled,maximumFileSize,allowedFileExtensions,encryption,antivirus){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
if(typeof permission==='undefined'){throw new AppwriteException('Missing required parameter: "permission"');}
|
||||
if(typeof fileSecurity==='undefined'){throw new AppwriteException('Missing required parameter: "fileSecurity"');}
|
||||
let path='/storage/buckets/{bucketId}'.replace('{bucketId}',bucketId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
|
||||
if(typeof permission!=='undefined'){payload['permission']=permission;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
if(typeof fileSecurity!=='undefined'){payload['fileSecurity']=fileSecurity;}
|
||||
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
|
||||
if(typeof maximumFileSize!=='undefined'){payload['maximumFileSize']=maximumFileSize;}
|
||||
if(typeof allowedFileExtensions!=='undefined'){payload['allowedFileExtensions']=allowedFileExtensions;}
|
||||
|
@ -735,13 +737,12 @@ if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
|
|||
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
|
||||
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
createFile(bucketId,fileId,file,read,write,onProgress=(progress)=>{}){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
createFile(bucketId,fileId,file,permissions,onProgress=(progress)=>{}){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
|
||||
if(typeof file==='undefined'){throw new AppwriteException('Missing required parameter: "file"');}
|
||||
let path='/storage/buckets/{bucketId}/files'.replace('{bucketId}',bucketId);let payload={};if(typeof fileId!=='undefined'){payload['fileId']=fileId;}
|
||||
if(typeof file!=='undefined'){payload['file']=file;}
|
||||
if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
const uri=new URL(this.client.config.endpoint+path);if(!(file instanceof File)){throw new AppwriteException('Parameter "file" has to be a File.');}
|
||||
const size=file.size;if(size<=Service.CHUNK_SIZE){return yield this.client.call('post',uri,{'content-type':'multipart/form-data',},payload);}
|
||||
let id=undefined;let response=undefined;const headers={'content-type':'multipart/form-data',};let counter=0;const totalCounters=Math.ceil(size/Service.CHUNK_SIZE);if(fileId!='unique()'){try{response=yield this.client.call('GET',new URL(this.client.config.endpoint+path+'/'+fileId),headers);counter=response.chunksUploaded;}
|
||||
|
@ -753,10 +754,9 @@ return response;});}
|
|||
getFile(bucketId,fileId){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
|
||||
let path='/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}',bucketId).replace('{fileId}',fileId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
|
||||
updateFile(bucketId,fileId,read,write){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
updateFile(bucketId,fileId,permissions){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
|
||||
let path='/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}',bucketId).replace('{fileId}',fileId);let payload={};if(typeof read!=='undefined'){payload['read']=read;}
|
||||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
let path='/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}',bucketId).replace('{fileId}',fileId);let payload={};if(typeof permissions!=='undefined'){payload['permissions']=permissions;}
|
||||
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('put',uri,{'content-type':'application/json',},payload);});}
|
||||
deleteFile(bucketId,fileId){return __awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
|
||||
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
|
||||
|
|
141
public/dist/scripts/app.js
vendored
141
public/dist/scripts/app.js
vendored
|
@ -521,115 +521,15 @@ params=formData;break;}
|
|||
return new Promise(function(resolve,reject){let request=new XMLHttpRequest(),key;request.withCredentials=true;request.open(method,path,true);for(key in headers){if(headers.hasOwnProperty(key)){request.setRequestHeader(key,headers[key]);}}
|
||||
request.onload=function(){if(4===request.readyState&&399>=request.status){let data=request.response;let contentType=this.getResponseHeader('content-type');contentType=contentType.substring(0,contentType.indexOf(';'));switch(contentType){case'application/json':data=JSON.parse(data);break;}
|
||||
resolve(data);}else{reject(new Error(request.statusText));}};if(progress){request.addEventListener('progress',progress);request.upload.addEventListener('progress',progress,false);}
|
||||
request.onerror=function(){reject(new Error("Network Error"));};request.send(params);})};return{'get':function(path,headers={},params={}){return call('GET',path+((params.length>0)?'?'+buildQuery(params):''),headers,{});},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress);},'put':function(path,headers={},params={},progress=null){return call('PUT',headers,params,progress);},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress);},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress);},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let analytics={create:function(id,source,activity,url){return http.post('/analytics',{'content-type':'application/json'},{id:id,source:source,activity:activity,url:url,version:env.VERSION,setup:env.SETUP});},};return{analytics:analytics,};},true);})(window);(function(window){"use strict";window.ls.container.set('console',function(window){var client=new Appwrite.Client();var endpoint=window.location.origin+'/v1';client.setEndpoint(endpoint).setProject('console').setLocale(APP_ENV.LOCALE);return{client:client,account:new Appwrite.Account(client),avatars:new Appwrite.Avatars(client),databases:new Appwrite.Databases(client),functions:new Appwrite.Functions(client),health:new Appwrite.Health(client),locale:new Appwrite.Locale(client),projects:new Appwrite.Projects(client),storage:new Appwrite.Storage(client),teams:new Appwrite.Teams(client),users:new Appwrite.Users(client)}},true);})(window);(function(window){"use strict";window.ls.container.set('date',function(){function format(format,timestamp){var jsdate,f
|
||||
var txtWords=['Sun','Mon','Tues','Wednes','Thurs','Fri','Satur','January','February','March','April','May','June','July','August','September','October','November','December']
|
||||
var formatChr=/\\?(.?)/gi
|
||||
var formatChrCb=function(t,s){return f[t]?f[t]():s}
|
||||
var _pad=function(n,c){n=String(n)
|
||||
while(n.length<c){n='0'+n}
|
||||
return n}
|
||||
f={d:function(){return _pad(f.j(),2)},D:function(){return f.l().slice(0,3)},j:function(){return jsdate.getDate()},l:function(){return txtWords[f.w()]+'day'},N:function(){return f.w()||7},S:function(){var j=f.j()
|
||||
var i=j%10
|
||||
if(i<=3&&parseInt((j%100)/10,10)===1){i=0}
|
||||
return['st','nd','rd'][i-1]||'th'},w:function(){return jsdate.getDay()},z:function(){var a=new Date(f.Y(),f.n()-1,f.j())
|
||||
var b=new Date(f.Y(),0,1)
|
||||
return Math.round((a-b)/864e5)},W:function(){var a=new Date(f.Y(),f.n()-1,f.j()-f.N()+3)
|
||||
var b=new Date(a.getFullYear(),0,4)
|
||||
return _pad(1+Math.round((a-b)/864e5/7),2)},F:function(){return txtWords[6+f.n()]},m:function(){return _pad(f.n(),2)},M:function(){return f.F().slice(0,3)},n:function(){return jsdate.getMonth()+1},t:function(){return(new Date(f.Y(),f.n(),0)).getDate()},L:function(){var j=f.Y()
|
||||
return j%4===0&j%100!==0|j%400===0},o:function(){var n=f.n()
|
||||
var W=f.W()
|
||||
var Y=f.Y()
|
||||
return Y+(n===12&&W<9?1:n===1&&W>9?-1:0)},Y:function(){return jsdate.getFullYear()},y:function(){return f.Y().toString().slice(-2)},a:function(){return jsdate.getHours()>11?'pm':'am'},A:function(){return f.a().toUpperCase()},B:function(){var H=jsdate.getUTCHours()*36e2
|
||||
var i=jsdate.getUTCMinutes()*60
|
||||
var s=jsdate.getUTCSeconds()
|
||||
return _pad(Math.floor((H+i+s+36e2)/86.4)%1e3,3)},g:function(){return f.G()%12||12},G:function(){return jsdate.getHours()},h:function(){return _pad(f.g(),2)},H:function(){return _pad(f.G(),2)},i:function(){return _pad(jsdate.getMinutes(),2)},s:function(){return _pad(jsdate.getSeconds(),2)},u:function(){return _pad(jsdate.getMilliseconds()*1000,6)},e:function(){var msg='Not supported (see source code of date() for timezone on how to add support)'
|
||||
throw new Error(msg)},I:function(){var a=new Date(f.Y(),0)
|
||||
var c=Date.UTC(f.Y(),0)
|
||||
var b=new Date(f.Y(),6)
|
||||
var d=Date.UTC(f.Y(),6)
|
||||
return((a-c)!==(b-d))?1:0},O:function(){var tzo=jsdate.getTimezoneOffset()
|
||||
var a=Math.abs(tzo)
|
||||
return(tzo>0?'-':'+')+_pad(Math.floor(a/60)*100+a%60,4)},P:function(){var O=f.O()
|
||||
return(O.substr(0,3)+':'+O.substr(3,2))},T:function(){return'UTC'},Z:function(){return-jsdate.getTimezoneOffset()*60},c:function(){return'Y-m-d\\TH:i:sP'.replace(formatChr,formatChrCb)},r:function(){return'D, d M Y H:i:s O'.replace(formatChr,formatChrCb)},U:function(){return jsdate/1000|0}}
|
||||
var _date=function(format,timestamp){jsdate=(timestamp===undefined?new Date():(timestamp instanceof Date)?new Date(timestamp):new Date(timestamp*1000))
|
||||
return format.replace(formatChr,formatChrCb)}
|
||||
return _date(format,timestamp)}
|
||||
function strtotime(text,now){var parsed
|
||||
var match
|
||||
var today
|
||||
var year
|
||||
var date
|
||||
var days
|
||||
var ranges
|
||||
var len
|
||||
var times
|
||||
var regex
|
||||
var i
|
||||
var fail=false
|
||||
if(!text){return fail}
|
||||
text=text.replace(/^\s+|\s+$/g,'').replace(/\s{2,}/g,' ').replace(/[\t\r\n]/g,'').toLowerCase()
|
||||
var pattern=new RegExp(['^(\\d{1,4})','([\\-\\.\\/:])','(\\d{1,2})','([\\-\\.\\/:])','(\\d{1,4})','(?:\\s(\\d{1,2}):(\\d{2})?:?(\\d{2})?)?','(?:\\s([A-Z]+)?)?$'].join(''))
|
||||
match=text.match(pattern)
|
||||
if(match&&match[2]===match[4]){if(match[1]>1901){switch(match[2]){case'-':if(match[3]>12||match[5]>31){return fail}
|
||||
return new Date(match[1],parseInt(match[3],10)-1,match[5],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000
|
||||
case'.':return fail
|
||||
case'/':if(match[3]>12||match[5]>31){return fail}
|
||||
return new Date(match[1],parseInt(match[3],10)-1,match[5],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000}}else if(match[5]>1901){switch(match[2]){case'-':if(match[3]>12||match[1]>31){return fail}
|
||||
return new Date(match[5],parseInt(match[3],10)-1,match[1],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000
|
||||
case'.':if(match[3]>12||match[1]>31){return fail}
|
||||
return new Date(match[5],parseInt(match[3],10)-1,match[1],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000
|
||||
case'/':if(match[1]>12||match[3]>31){return fail}
|
||||
return new Date(match[5],parseInt(match[1],10)-1,match[3],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000}}else{switch(match[2]){case'-':if(match[3]>12||match[5]>31||(match[1]<70&&match[1]>38)){return fail}
|
||||
year=match[1]>=0&&match[1]<=38?+match[1]+2000:match[1]
|
||||
return new Date(year,parseInt(match[3],10)-1,match[5],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000
|
||||
case'.':if(match[5]>=70){if(match[3]>12||match[1]>31){return fail}
|
||||
return new Date(match[5],parseInt(match[3],10)-1,match[1],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000}
|
||||
if(match[5]<60&&!match[6]){if(match[1]>23||match[3]>59){return fail}
|
||||
today=new Date()
|
||||
return new Date(today.getFullYear(),today.getMonth(),today.getDate(),match[1]||0,match[3]||0,match[5]||0,match[9]||0)/1000}
|
||||
return fail
|
||||
case'/':if(match[1]>12||match[3]>31||(match[5]<70&&match[5]>38)){return fail}
|
||||
year=match[5]>=0&&match[5]<=38?+match[5]+2000:match[5]
|
||||
return new Date(year,parseInt(match[1],10)-1,match[3],match[6]||0,match[7]||0,match[8]||0,match[9]||0)/1000
|
||||
case':':if(match[1]>23||match[3]>59||match[5]>59){return fail}
|
||||
today=new Date()
|
||||
return new Date(today.getFullYear(),today.getMonth(),today.getDate(),match[1]||0,match[3]||0,match[5]||0)/1000}}}
|
||||
if(text==='now'){return now===null||isNaN(now)?new Date().getTime()/1000|0:now|0}
|
||||
if(!isNaN(parsed=Date.parse(text))){return parsed/1000|0}
|
||||
pattern=new RegExp(['^([0-9]{4}-[0-9]{2}-[0-9]{2})','[ t]','([0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?)','([\\+-][0-9]{2}(:[0-9]{2})?|z)'].join(''))
|
||||
match=text.match(pattern)
|
||||
if(match){if(match[4]==='z'){match[4]='Z'}else if(match[4].match(/^([+-][0-9]{2})$/)){match[4]=match[4]+':00'}
|
||||
if(!isNaN(parsed=Date.parse(match[1]+'T'+match[2]+match[4]))){return parsed/1000|0}}
|
||||
date=now?new Date(now*1000):new Date()
|
||||
days={'sun':0,'mon':1,'tue':2,'wed':3,'thu':4,'fri':5,'sat':6}
|
||||
ranges={'yea':'FullYear','mon':'Month','day':'Date','hou':'Hours','min':'Minutes','sec':'Seconds'}
|
||||
function lastNext(type,range,modifier){var diff
|
||||
var day=days[range]
|
||||
if(typeof day!=='undefined'){diff=day-date.getDay()
|
||||
if(diff===0){diff=7*modifier}else if(diff>0&&type==='last'){diff-=7}else if(diff<0&&type==='next'){diff+=7}
|
||||
date.setDate(date.getDate()+diff)}}
|
||||
function process(val){var splt=val.split(' ')
|
||||
var type=splt[0]
|
||||
var range=splt[1].substring(0,3)
|
||||
var typeIsNumber=/\d+/.test(type)
|
||||
var ago=splt[2]==='ago'
|
||||
var num=(type==='last'?-1:1)*(ago?-1:1)
|
||||
if(typeIsNumber){num*=parseInt(type,10)}
|
||||
if(ranges.hasOwnProperty(range)&&!splt[1].match(/^mon(day|\.)?$/i)){return date['set'+ranges[range]](date['get'+ranges[range]]()+num)}
|
||||
if(range==='wee'){return date.setDate(date.getDate()+(num*7))}
|
||||
if(type==='next'||type==='last'){lastNext(type,range,num)}else if(!typeIsNumber){return false}
|
||||
return true}
|
||||
times='(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec'+'|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?'+'|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)'
|
||||
regex='([+-]?\\d+\\s'+times+'|'+'(last|next)\\s'+times+')(\\sago)?'
|
||||
match=text.match(new RegExp(regex,'gi'))
|
||||
if(!match){return fail}
|
||||
for(i=0,len=match.length;i<len;i++){if(!process(match[i])){return fail}}
|
||||
return(date.getTime()/1000)}
|
||||
return{format:format,strtotime:strtotime}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,to){if(value&&Array.isArray(value)&&to!=='array'){value=value.map(element=>cast(element,to));return value;}
|
||||
request.onerror=function(){reject(new Error("Network Error"));};request.send(params);})};return{'get':function(path,headers={},params={}){return call('GET',path+((params.length>0)?'?'+buildQuery(params):''),headers,{});},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress);},'put':function(path,headers={},params={},progress=null){return call('PUT',headers,params,progress);},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress);},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress);},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let analytics={create:function(id,source,activity,url){return http.post('/analytics',{'content-type':'application/json'},{id:id,source:source,activity:activity,url:url,version:env.VERSION,setup:env.SETUP});},};return{analytics:analytics,};},true);})(window);(function(window){"use strict";window.ls.container.set('console',function(window){var client=new Appwrite.Client();var endpoint=window.location.origin+'/v1';client.setEndpoint(endpoint).setProject('console').setLocale(APP_ENV.LOCALE);return{client:client,account:new Appwrite.Account(client),avatars:new Appwrite.Avatars(client),databases:new Appwrite.Databases(client),functions:new Appwrite.Functions(client),health:new Appwrite.Health(client),locale:new Appwrite.Locale(client),projects:new Appwrite.Projects(client),storage:new Appwrite.Storage(client),teams:new Appwrite.Teams(client),users:new Appwrite.Users(client)}},true);})(window);(function(window){"use strict";window.ls.container.set('date',function(){function format(format,datetime){if(!datetime){return null;}
|
||||
return new Intl.DateTimeFormat('en-US',{timeZone:'UTC',hourCycle:'h24',...format}).format(new Date(datetime));}
|
||||
return{format:format,}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,from,to,){if(value&&Array.isArray(value)&&to!=='array'){value=value.map(element=>cast(element,from,to));return value;}
|
||||
switch(to){case'int':case'integer':value=parseInt(value);break;case'numeric':value=Number(value);break;case'float':value=parseFloat(value);break;case'string':value=value.toString();if(value.length===0){value=null;}
|
||||
break;case'json':value=(value)?JSON.parse(value):[];break;case'array':value=(value&&value.constructor&&value.constructor===Array)?value:[value];break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
|
||||
break;case'json':value=(value)?JSON.parse(value):[];break;case'array':if(value&&value.constructor&&value.constructor===Array){break;}
|
||||
if(from==='csv'){if(value.length===0){value=[];}else{value=value.split(',');}}else{value=[value];}
|
||||
break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
|
||||
return value;}
|
||||
function toJson(element,json){json=json||{};let name=element.getAttribute('name');let type=element.getAttribute('type');let castTo=element.getAttribute('data-cast-to');let ref=json;if(name&&'FORM'!==element.tagName){if(name.startsWith('[')){let splitName=name.split('.');if(splitName.length>1&&splitName[0].endsWith(']')){name=splitName[splitName.length-1];}}
|
||||
function toJson(element,json){json=json||{};let name=element.getAttribute('name');let type=element.getAttribute('type');let castTo=element.getAttribute('data-cast-to');let castFrom=element.getAttribute('data-cast-from');let ref=json;if(name&&'FORM'!==element.tagName){if(name.startsWith('[')){let splitName=name.split('.');if(splitName.length>1&&splitName[0].endsWith(']')){name=splitName[splitName.length-1];}}
|
||||
if('FIELDSET'===element.tagName){if(castTo==='object'){if(json[name]===undefined){json[name]={};}
|
||||
ref=json[name];}
|
||||
else{if(!Array.isArray(json[name])){json[name]=[];}
|
||||
|
@ -642,7 +542,7 @@ else if('file'===type){json[name]=element.files[0];}
|
|||
else if(undefined!==element.value){if((json[name]!==undefined)&&(!Array.isArray(json[name]))){json[name]=[json[name]];}
|
||||
if(Array.isArray(json[name])){json[name].push(element.value);}
|
||||
else{json[name]=element.value;}}
|
||||
json[name]=cast(json[name],castTo);}}
|
||||
json[name]=cast(json[name],castFrom,castTo);}}
|
||||
for(let i=0;i<element.children.length;i++){if(Array.isArray(ref)){ref.push({});toJson(element.children[i],ref[ref.length]);}
|
||||
else{toJson(element.children[i],ref);}}
|
||||
return json;}
|
||||
|
@ -655,7 +555,7 @@ return false;};return{isRTL:isRTL,};},true);})(window);(function(window){"use st
|
|||
let size=element.dataset["size"]||80;let name=$value.name||$value||"";name=(typeof name!=='string')?'--':name;return def="/v1/avatars/initials?project=console"+"&name="+
|
||||
encodeURIComponent(name)+"&width="+
|
||||
size+"&height="+
|
||||
size;}).add("selectedCollection",function($value,router){return $value===router.params.collectionId?"selected":"";}).add("selectedDocument",function($value,router){return $value===router.params.documentId?"selected":"";}).add("localeString",function($value){$value=parseInt($value);return!Number.isNaN($value)?$value.toLocaleString():"";}).add("date",function($value,date){return $value?date.format("Y-m-d",$value):"";}).add("dateTime",function($value,date){return $value?date.format("Y-m-d H:i",$value):"";}).add("dateText",function($value,date){return $value?date.format("d M Y",$value):"";}).add("timeSince",function($value){$value=$value*1000;let seconds=Math.floor((Date.now()-$value)/1000);let unit="second";let direction="ago";if(seconds<0){seconds=-seconds;direction="from now";}
|
||||
size;}).add("selectedCollection",function($value,router){return $value===router.params.collectionId?"selected":"";}).add("selectedDocument",function($value,router){return $value===router.params.documentId?"selected":"";}).add("localeString",function($value){$value=parseInt($value);return!Number.isNaN($value)?$value.toLocaleString():"";}).add("dateTime",function($value,date){return $value?date.format({year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit'},$value):"";}).add("date",function($value,date){return $value?date.format({year:'numeric',month:'short',day:'2-digit',},$value):"";}).add("timeSince",function($value){$value=new Date($value).getTime();let now=new Date();now.setMinutes(now.getMinutes()+now.getTimezoneOffset());let timestamp=new Date(now.toISOString()).getTime();let seconds=Math.floor((timestamp-$value)/1000);let unit="second";let direction="ago";if(seconds<0){seconds=-seconds;direction="from now";}
|
||||
let value=seconds;if(seconds>=31536000){value=Math.floor(seconds/31536000);unit="year";}
|
||||
else if(seconds>=86400){value=Math.floor(seconds/86400);unit="day";}
|
||||
else if(seconds>=3600){value=Math.floor(seconds/3600);unit="hour";}
|
||||
|
@ -693,11 +593,10 @@ if(forcePlaces!==false){rounded=Number(rounded).toFixed(forcePlaces);}
|
|||
return rounded+abbr;}
|
||||
window.ls.container.get("view").add({selector:"data-acl",controller:function(element,document,router,alerts){document.body.classList.remove("console");document.body.classList.remove("home");document.body.classList.add(router.getCurrent().view.scope);if(!router.getCurrent().view.project){document.body.classList.add("hide-nav");document.body.classList.remove("show-nav");}else{document.body.classList.add("show-nav");document.body.classList.remove("hide-nav");}
|
||||
if("/console"===router.getCurrent().path){document.body.classList.add("index");}else{document.body.classList.remove("index");}}}).add({selector:"data-prism",controller:function(window,document,element,alerts){Prism.highlightElement(element);let copy=document.createElement("i");copy.className="icon-docs copy";copy.title="Copy to Clipboard";copy.textContent="Click Here to Copy";copy.addEventListener("click",function(){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
|
||||
window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendChild(copy);}});(function(window){document.addEventListener('alpine:init',()=>{Alpine.store('uploader',{_files:[],files(){return(this._files??[]).filter((file)=>!file.cancelled);},isOpen:true,init(){window.addEventListener('beforeunload',(event)=>{if(this.hasOngoingUploads()){let confirmationMessage="There are incomplete uploads, are you sure you want to leave?";event.returnValue=confirmationMessage;return confirmationMessage;}});},cancelAll(){if(this.hasOngoingUploads()?confirm("Are you sure? This will cancel and remove any ongoing uploads?"):true){this._files.forEach(file=>{if(file.completed||file.failed){this.removeFile(file.id);}else{this.updateFile(file.id,{cancelled:true});}});}},hasOngoingUploads(){let ongoing=false;this._files.some((file)=>{if(!file.completed&&!file.failed){ongoing=true;return;}});return ongoing;},toggle(){this.isOpen=!this.isOpen;},addFile(file){this._files.push(file);},updateFile(id,file){this._files=this._files.map((oldFile)=>id==oldFile.id?{...oldFile,...file}:oldFile);},removeFile(id){const file=this.getFile(id)??{};if(file.completed||file.failed){this._files=this._files.filter((file)=>file.id!==id);}else{if(confirm("Are you sure you want to cancel the upload?")){this.updateFile(id,{cancelled:true});}}},getFile(id){return this._files.find((file)=>file.id===id);},async uploadFile(target){const formData=new FormData(target);const sdk=window.ls.container.get('sdk');const bucketId=formData.get('bucketId');const file=formData.get('file');const fileId=formData.get('fileId');let id=fileId==='unique()'?performance.now():fileId;let read=formData.get('read');if(!file||!fileId){return;}
|
||||
if(read){read=JSON.parse(read);}
|
||||
let write=formData.get('write');if(write){write=JSON.parse(write);}
|
||||
window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendChild(copy);}});(function(window){document.addEventListener('alpine:init',()=>{Alpine.store('uploader',{_files:[],files(){return(this._files??[]).filter((file)=>!file.cancelled);},isOpen:true,init(){window.addEventListener('beforeunload',(event)=>{if(this.hasOngoingUploads()){let confirmationMessage="There are incomplete uploads, are you sure you want to leave?";event.returnValue=confirmationMessage;return confirmationMessage;}});},cancelAll(){if(this.hasOngoingUploads()?confirm("Are you sure? This will cancel and remove any ongoing uploads?"):true){this._files.forEach(file=>{if(file.completed||file.failed){this.removeFile(file.id);}else{this.updateFile(file.id,{cancelled:true});}});}},hasOngoingUploads(){let ongoing=false;this._files.some((file)=>{if(!file.completed&&!file.failed){ongoing=true;return;}});return ongoing;},toggle(){this.isOpen=!this.isOpen;},addFile(file){this._files.push(file);},updateFile(id,file){this._files=this._files.map((oldFile)=>id==oldFile.id?{...oldFile,...file}:oldFile);},removeFile(id){const file=this.getFile(id)??{};if(file.completed||file.failed){this._files=this._files.filter((file)=>file.id!==id);}else{if(confirm("Are you sure you want to cancel the upload?")){this.updateFile(id,{cancelled:true});}}},getFile(id){return this._files.find((file)=>file.id===id);},async uploadFile(target){const formData=new FormData(target);const sdk=window.ls.container.get('sdk');const bucketId=formData.get('bucketId');const file=formData.get('file');const fileId=formData.get('fileId');let id=fileId==='unique()'?performance.now():fileId;if(!file||!fileId){return;}
|
||||
let permissions=formData.get('permissions');if(permissions){permissions=permissions.split(',');}
|
||||
if(this.getFile(id)){this.updateFile(id,{name:file.name,completed:false,failed:false,cancelled:false,error:"",});}else{this.addFile({id:id,name:file.name,progress:0,completed:false,failed:false,cancelled:false,error:"",});}
|
||||
target.reset();try{const response=await sdk.storage.createFile(bucketId,fileId,file,read,write,(progress)=>{this.updateFile(id,{id:progress.$id,progress:Math.round(progress.progress),error:"",});id=progress.$id;const file=this.getFile(id)??{};if(file.cancelled===true){throw'USER_CANCELLED';}});const existingFile=this.getFile(id)??{};if(existingFile.cancelled){this.updateFile(id,{id:response.$id,name:response.name,failed:false,});id=response.$id;throw'USER_CANCELLED'}else{this.updateFile(id,{id:response.$id,name:response.name,progress:100,completed:true,failed:false,});id=response.$id;}
|
||||
target.reset();try{const response=await sdk.storage.createFile(bucketId,fileId,file,permissions,(progress)=>{this.updateFile(id,{id:progress.$id,progress:Math.round(progress.progress),error:"",});id=progress.$id;const file=this.getFile(id)??{};if(file.cancelled===true){throw'USER_CANCELLED';}});const existingFile=this.getFile(id)??{};if(existingFile.cancelled){this.updateFile(id,{id:response.$id,name:response.name,failed:false,});id=response.$id;throw'USER_CANCELLED'}else{this.updateFile(id,{id:response.$id,name:response.name,progress:100,completed:true,failed:false,});id=response.$id;}
|
||||
document.dispatchEvent(new CustomEvent('storage.createFile'));}catch(error){if(error==='USER_CANCELLED'){await sdk.storage.deleteFile(bucketId,id);this.updateFile(id,{cancelled:false,failed:true,});this.removeFile(id);}else{this.updateFile(id,{id:id,failed:true,error:error.message??error});}
|
||||
document.dispatchEvent(new CustomEvent('storage.createFile'));}}});});})(window);(function(window){document.addEventListener('alpine:init',()=>{Alpine.data('events',()=>({events:new Set(),selected:null,action:null,type:null,subType:null,subSubType:null,resource:null,resourceName:'',subResource:null,subResourceName:'',subSubResource:null,subSubResourceName:'',hasResource:false,hasSubResource:false,hasSubSubResource:false,attribute:null,hasAttribute:false,attributes:[],load(events){this.events=new Set(events);},reset(){this.hasResource=this.hasSubResource=this.hasSubSubResource=this.hasAttribute=false;this.type=this.subType=this.subResource=this.resource=this.attribute=this.selected=this.action=null;},setEvent(){this.hasResource=this.hasSubResource=this.hasSubSubResource=this.hasAttribute=this.action=false;if(!this.selected){this.reset();return;}
|
||||
let[type,action]=this.selected.split('.');switch(type){case'users':if(action==='update'){this.hasAttribute=true;this.attributes=['email','name','password','status','prefs']}
|
||||
|
@ -707,7 +606,14 @@ this.action=action;},showModal(modal){document.documentElement.classList.add("mo
|
|||
if(this.hasSubSubResource){event+=`.${this.subSubType}.${this.subSubResource ? this.subSubResource : '*'}`;}
|
||||
if(this.action){event+=`.${this.action}`;}
|
||||
if(this.attribute){event+=`.${this.attribute}`;}
|
||||
this.events.add(event);this.reset();},removeEvent(value){this.events.delete(value);}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
|
||||
this.events.add(event);this.reset();},removeEvent(value){this.events.delete(value);}}));});})(window);(function(window){document.addEventListener('alpine:init',()=>{Alpine.data('permissionsMatrix',()=>({permissions:[],rawPermissions:[],load(permissions){if(permissions===undefined){return;}
|
||||
this.rawPermissions=permissions;permissions.map(p=>{let{type,role}=this.parsePermission(p);type=this.parseInputPermission(type);let index=-1;let existing=this.permissions.find((p,idx)=>{if(p.role===role){index=idx;return true;}})
|
||||
if(existing===undefined){this.permissions.push({role,[type]:true,});}
|
||||
if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId,role,permissions){if(!document.getElementById(formId).reportValidity()){return;}
|
||||
Object.entries(permissions).forEach(entry=>{let[type,enabled]=entry;type=this.parseOutputPermission(type);if(enabled){this.rawPermissions.push(this.buildPermission(type,role));}});this.permissions.push({role,...permissions,});this.reset();},updatePermission(index){setTimeout(()=>{const permission=this.permissions[index];Object.keys(permission).forEach(key=>{if(key==='role'){return;}
|
||||
const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';}
|
||||
return key;},parseOutputPermission(key){if(key==='xdelete'){return'delete';}
|
||||
return key;}}));Alpine.data('permissionsRow',()=>({role:'',read:false,create:false,update:false,xdelete:false,reset(){this.role='';this.read=this.create=this.update=this.xdelete=false;}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
|
||||
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},6000);};},redirect:function(url){return function(router){if(url==="/console"){window.location=url;return;}
|
||||
router.change(url||"/");};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
|
||||
return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
|
||||
|
@ -753,9 +659,9 @@ button.addEventListener("click",function(){var clone=document.createElement(elem
|
|||
clone.innerHTML=template;clone.className=element.className;var input=clone.querySelector("input, select, textarea");view.render(clone);if(debug){console.log('Debug: clone: ',clone);console.log('Debug: target: ',target);}
|
||||
if(target){target.appendChild(clone);}else{button.parentNode.insertBefore(clone,button);}
|
||||
if(input){input.focus();}
|
||||
Array.prototype.slice.call(clone.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){clone.parentNode.removeChild(clone);obj.scrollIntoView({behavior:"smooth"});});});Array.prototype.slice.call(clone.querySelectorAll("[data-up]")).map(function(obj){obj.addEventListener("click",function(){if(clone.previousElementSibling){clone.parentNode.insertBefore(clone,clone.previousElementSibling);obj.scrollIntoView({behavior:"smooth"});}});});Array.prototype.slice.call(clone.querySelectorAll("[data-down]")).map(function(obj){obj.addEventListener("click",function(){if(clone.nextElementSibling){clone.parentNode.insertBefore(clone.nextElementSibling,clone);obj.scrollIntoView({behavior:"smooth"});}});});});element.parentNode.insertBefore(button,element.nextSibling);element.parentNode.removeChild(element);button.form.addEventListener('reset',function(event){target.innerHTML='';if(first){button.click();}});if(first){button.click();}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-add",repeat:false,controller:function(element,view,container,document){for(var i=0;i<element.children.length;i++){let button=document.createElement("button");let template=element.children[i].cloneNode(true);let as=element.getAttribute('data-ls-as');let counter=0;button.type="button";button.innerText="Add";button.classList.add("reverse");button.classList.add("margin-end-small");button.addEventListener('click',function(){container.addNamespace(as,'new-'+counter++);console.log(container.namespaces,container.get(as),as);container.set(as,null,true,true);let child=template.cloneNode(true);view.render(child);element.appendChild(child);element.style.visibility='visible';let inputs=child.querySelectorAll('input,textarea');for(let index=0;index<inputs.length;++index){if(inputs[index].type!=='hidden'){inputs[index].focus();break;}}});element.after(button);}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-chart",controller:function(element,container,date,document){let wrapper=document.createElement("div");let child=document.createElement("canvas");let sources=element.getAttribute('data-forms-chart');let width=element.getAttribute('data-width')||500;let height=element.getAttribute('data-height')||175;let showXAxis=element.getAttribute('data-show-x-axis')||false;let showYAxis=element.getAttribute('data-show-y-axis')||false;let colors=(element.getAttribute('data-colors')||'blue,green,orange,red').split(',');let themes={'blue':'#29b5d9','green':'#4eb55b','orange':'#fba233','red':'#dc3232','create':'#00b680','read':'#009cde','update':'#696fd7','delete':'#da5d95',};let range={'24h':'H:i','7d':'d F Y','30d':'d F Y','90d':'d F Y'}
|
||||
Array.prototype.slice.call(clone.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){clone.parentNode.removeChild(clone);obj.scrollIntoView({behavior:"smooth"});});});Array.prototype.slice.call(clone.querySelectorAll("[data-up]")).map(function(obj){obj.addEventListener("click",function(){if(clone.previousElementSibling){clone.parentNode.insertBefore(clone,clone.previousElementSibling);obj.scrollIntoView({behavior:"smooth"});}});});Array.prototype.slice.call(clone.querySelectorAll("[data-down]")).map(function(obj){obj.addEventListener("click",function(){if(clone.nextElementSibling){clone.parentNode.insertBefore(clone.nextElementSibling,clone);obj.scrollIntoView({behavior:"smooth"});}});});});element.parentNode.insertBefore(button,element.nextSibling);element.parentNode.removeChild(element);button.form.addEventListener('reset',function(event){target.innerHTML='';if(first){button.click();}});if(first){button.click();}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-add",repeat:false,controller:function(element,view,container,document){for(var i=0;i<element.children.length;i++){let button=document.createElement("button");let template=element.children[i].cloneNode(true);let as=element.getAttribute('data-ls-as');let counter=0;button.type="button";button.innerText="Add";button.classList.add("reverse");button.classList.add("margin-end-small");button.addEventListener('click',function(){container.addNamespace(as,'new-'+counter++);console.log(container.namespaces,container.get(as),as);container.set(as,null,true,true);let child=template.cloneNode(true);view.render(child);element.appendChild(child);element.style.visibility='visible';let inputs=child.querySelectorAll('input,textarea');for(let index=0;index<inputs.length;++index){if(inputs[index].type!=='hidden'){inputs[index].focus();break;}}});element.after(button);}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-chart",controller:function(element,container,date,document){let wrapper=document.createElement("div");let child=document.createElement("canvas");let sources=element.getAttribute('data-forms-chart');let width=element.getAttribute('data-width')||500;let height=element.getAttribute('data-height')||175;let showXAxis=element.getAttribute('data-show-x-axis')||false;let showYAxis=element.getAttribute('data-show-y-axis')||false;let colors=(element.getAttribute('data-colors')||'blue,green,orange,red').split(',');let themes={'blue':'#29b5d9','green':'#4eb55b','orange':'#fba233','red':'#dc3232','create':'#00b680','read':'#009cde','update':'#696fd7','delete':'#da5d95',};let range={'24h':{hour:'2-digit',minute:'2-digit'},'7d':{year:'numeric',month:'short',day:'2-digit',},'30d':{year:'numeric',month:'short',day:'2-digit',},'90d':{year:'numeric',month:'short',day:'2-digit',}}
|
||||
let ticksCount=5;element.parentNode.insertBefore(wrapper,element.nextSibling);wrapper.classList.add('content');child.width=width;child.height=height;sources=sources.split(',');wrapper.appendChild(child);let chart=null;let check=function(){let config={type:"line",data:{labels:[],datasets:[]},options:{animation:{duration:0},responsive:true,hover:{mode:"nearest",intersect:false},scales:{x:{display:showXAxis},y:{display:showYAxis,min:0,ticks:{count:ticksCount,fontColor:"#8f8f8f"},}},plugins:{title:{display:false,text:"Stats"},legend:{display:false},tooltip:{mode:"index",intersect:false,caretPadding:0},}}};let highest=0;for(let i=0;i<sources.length;i++){let label=sources[i].substring(0,sources[i].indexOf('='));let path=sources[i].substring(sources[i].indexOf('=')+1);let usage=container.get('usage');let data=usage[path];let value=JSON.parse(element.value);config.data.labels[i]=label;config.data.datasets[i]={};config.data.datasets[i].label=label;config.data.datasets[i].borderColor=themes[colors[i]];config.data.datasets[i].backgroundColor=themes[colors[i]]+'36';config.data.datasets[i].borderWidth=2;config.data.datasets[i].data=[0,0,0,0,0,0,0];config.data.datasets[i].fill=true;if(!data){return;}
|
||||
let dateFormat=(value.range&&range[value.range])?range[value.range]:'d F Y';for(let x=0;x<data.length;x++){if(data[x].value>highest){highest=data[x].value;}
|
||||
let dateFormat=(value.range&&range[value.range])?range[value.range]:{year:'numeric',month:'short',day:'2-digit',};for(let x=0;x<data.length;x++){if(data[x].value>highest){highest=data[x].value;}
|
||||
config.data.datasets[i].data[x]=data[x].value;config.data.labels[x]=date.format(dateFormat,data[x].date);}}
|
||||
if(highest==0){config.options.scales.y.ticks.stepSize=1;config.options.scales.y.max=ticksCount;}else{highest=Math.ceil(highest/ticksCount)*ticksCount;config.options.scales.y.ticks.stepSize=highest/ticksCount;config.options.scales.y.max=highest;}
|
||||
if(chart){chart.destroy();}
|
||||
|
@ -828,7 +734,8 @@ var file=document.createElement("li");var image=document.createElement("img");im
|
|||
result.bucketId+"/files/"+
|
||||
result.fileId+"/preview?width="+
|
||||
previewWidth+"&height="+
|
||||
previewHeight+"&project="+project+"&mode=admin";image.alt=previewAlt;file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);element.value='';};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=JSON.stringify(result);};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile('default','unique()',files[0],read,write).then(function(response){onComplete(message);render({bucketId:response.bucketId,fileId:response.$id});},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
|
||||
previewHeight+"&project="+project+"&mode=admin";image.alt=previewAlt;file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);element.value='';};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=JSON.stringify(result);};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var permissions=JSON.parse(expression.parse(element.dataset["permissions"]||"[]"))
|
||||
sdk.storage.createFile('default','unique()',files[0],permissions).then(function(response){onComplete(message);render({bucketId:response.bucketId,fileId:response.$id});},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
|
||||
render(element.value);wrapper.scrollIntoView();});upload.addEventListener("keypress",function(){input.click();});element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(preview);wrapper.appendChild(progress);wrapper.appendChild(upload);upload.appendChild(input);render(output);if(searchButton){let searchOpen=document.createElement("button");searchOpen.type='button';searchOpen.innerHTML='<i class="icon icon-search"></i> Search';searchOpen.classList.add('reverse');let path=container.scope(searchButton);searchOpen.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent("open-file-search",{bubbles:false,cancelable:true}));});wrapper.appendChild(searchOpen);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-cookies",controller:function(element,alerts,cookie,env){if(!cookie.get("cookie-alert")){let text=element.dataset["cookies"]||"";alerts.add({text:text,class:"cookie-alert",link:env.HOME+"/policy/cookies",label:'Learn More',callback:function(){cookie.set("cookie-alert","true",365*10);}},0);}}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-copy',repeat:false,controller:function(document,element,alerts){let button=document.createElement("i");button.type="button";button.title="Copy to Clipboard";button.className=element.getAttribute("data-class")||"icon-docs note copy";button.style.cursor="pointer";element.parentNode.insertBefore(button,element.nextSibling);let copy=function(event){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
|
||||
window.getSelection().removeAllRanges();};button.addEventListener("click",copy);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-page-title",repeat:true,controller:function(element,document,expression){document.title=expression.parse(element.getAttribute("data-page-title"))||document.title;}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-to',repeat:false,controller:function(element,window){let button=window.document.createElement('button');button.className='scroll-to icon-up-dir';button.alt='Back To Top';button.title='Back To Top';button.addEventListener('click',function(){element.scrollIntoView(true,{behavior:'smooth'});button.blur();},false);element.appendChild(button);}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-direction',repeat:false,controller:function(element,window){let position=0;let check=function(){let direction=window.document.documentElement.scrollTop;if(direction>position){element.classList.remove('scroll-to-top')
|
||||
element.classList.add('scroll-to-bottom')}
|
||||
|
|
2
public/dist/styles/default-ltr.css
vendored
2
public/dist/styles/default-ltr.css
vendored
File diff suppressed because one or more lines are too long
2
public/dist/styles/default-rtl.css
vendored
2
public/dist/styles/default-rtl.css
vendored
File diff suppressed because one or more lines are too long
|
@ -1,29 +1,29 @@
|
|||
(function (exports, isomorphicFormData, crossFetch) {
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
|
||||
function __awaiter(thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
|
||||
function __awaiter(thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
}
|
||||
|
||||
class Service {
|
||||
|
@ -1867,13 +1867,12 @@
|
|||
* @param {string} databaseId
|
||||
* @param {string} collectionId
|
||||
* @param {string} name
|
||||
* @param {string} permission
|
||||
* @param {string[]} read
|
||||
* @param {string[]} write
|
||||
* @param {string[]} permissions
|
||||
* @param {boolean} documentSecurity
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createCollection(databaseId, collectionId, name, permission, read, write) {
|
||||
createCollection(databaseId, collectionId, name, permissions, documentSecurity) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof databaseId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "databaseId"');
|
||||
|
@ -1884,14 +1883,11 @@
|
|||
if (typeof name === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "name"');
|
||||
}
|
||||
if (typeof permission === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "permission"');
|
||||
if (typeof permissions === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "permissions"');
|
||||
}
|
||||
if (typeof read === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "read"');
|
||||
}
|
||||
if (typeof write === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "write"');
|
||||
if (typeof documentSecurity === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "documentSecurity"');
|
||||
}
|
||||
let path = '/databases/{databaseId}/collections'.replace('{databaseId}', databaseId);
|
||||
let payload = {};
|
||||
|
@ -1901,14 +1897,11 @@
|
|||
if (typeof name !== 'undefined') {
|
||||
payload['name'] = name;
|
||||
}
|
||||
if (typeof permission !== 'undefined') {
|
||||
payload['permission'] = permission;
|
||||
if (typeof permissions !== 'undefined') {
|
||||
payload['permissions'] = permissions;
|
||||
}
|
||||
if (typeof read !== 'undefined') {
|
||||
payload['read'] = read;
|
||||
}
|
||||
if (typeof write !== 'undefined') {
|
||||
payload['write'] = write;
|
||||
if (typeof documentSecurity !== 'undefined') {
|
||||
payload['documentSecurity'] = documentSecurity;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + path);
|
||||
return yield this.client.call('post', uri, {
|
||||
|
@ -1951,14 +1944,13 @@
|
|||
* @param {string} databaseId
|
||||
* @param {string} collectionId
|
||||
* @param {string} name
|
||||
* @param {string} permission
|
||||
* @param {string[]} read
|
||||
* @param {string[]} write
|
||||
* @param {boolean} documentSecurity
|
||||
* @param {string[]} permissions
|
||||
* @param {boolean} enabled
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
updateCollection(databaseId, collectionId, name, permission, read, write, enabled) {
|
||||
updateCollection(databaseId, collectionId, name, documentSecurity, permissions, enabled) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof databaseId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "databaseId"');
|
||||
|
@ -1969,22 +1961,19 @@
|
|||
if (typeof name === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "name"');
|
||||
}
|
||||
if (typeof permission === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "permission"');
|
||||
if (typeof documentSecurity === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "documentSecurity"');
|
||||
}
|
||||
let path = '/databases/{databaseId}/collections/{collectionId}'.replace('{databaseId}', databaseId).replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof name !== 'undefined') {
|
||||
payload['name'] = name;
|
||||
}
|
||||
if (typeof permission !== 'undefined') {
|
||||
payload['permission'] = permission;
|
||||
if (typeof permissions !== 'undefined') {
|
||||
payload['permissions'] = permissions;
|
||||
}
|
||||
if (typeof read !== 'undefined') {
|
||||
payload['read'] = read;
|
||||
}
|
||||
if (typeof write !== 'undefined') {
|
||||
payload['write'] = write;
|
||||
if (typeof documentSecurity !== 'undefined') {
|
||||
payload['documentSecurity'] = documentSecurity;
|
||||
}
|
||||
if (typeof enabled !== 'undefined') {
|
||||
payload['enabled'] = enabled;
|
||||
|
@ -2096,6 +2085,53 @@
|
|||
}, payload);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Create DateTime Attribute
|
||||
*
|
||||
*
|
||||
* @param {string} databaseId
|
||||
* @param {string} collectionId
|
||||
* @param {string} key
|
||||
* @param {boolean} required
|
||||
* @param {string} xdefault
|
||||
* @param {boolean} array
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createDatetimeAttribute(databaseId, collectionId, key, required, xdefault, array) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof databaseId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "databaseId"');
|
||||
}
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
if (typeof required === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "required"');
|
||||
}
|
||||
let path = '/databases/{databaseId}/collections/{collectionId}/attributes/datetime'.replace('{databaseId}', databaseId).replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof key !== 'undefined') {
|
||||
payload['key'] = key;
|
||||
}
|
||||
if (typeof required !== 'undefined') {
|
||||
payload['required'] = required;
|
||||
}
|
||||
if (typeof xdefault !== 'undefined') {
|
||||
payload['default'] = xdefault;
|
||||
}
|
||||
if (typeof array !== 'undefined') {
|
||||
payload['array'] = array;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + path);
|
||||
return yield this.client.call('post', uri, {
|
||||
'content-type': 'application/json',
|
||||
}, payload);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Create Email Attribute
|
||||
*
|
||||
|
@ -2596,12 +2632,11 @@
|
|||
* @param {string} collectionId
|
||||
* @param {string} documentId
|
||||
* @param {Omit<Document, keyof Models.Document>} data
|
||||
* @param {string[]} read
|
||||
* @param {string[]} write
|
||||
* @param {string[]} permissions
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createDocument(databaseId, collectionId, documentId, data, read, write) {
|
||||
createDocument(databaseId, collectionId, documentId, data, permissions) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof databaseId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "databaseId"');
|
||||
|
@ -2623,11 +2658,8 @@
|
|||
if (typeof data !== 'undefined') {
|
||||
payload['data'] = data;
|
||||
}
|
||||
if (typeof read !== 'undefined') {
|
||||
payload['read'] = read;
|
||||
}
|
||||
if (typeof write !== 'undefined') {
|
||||
payload['write'] = write;
|
||||
if (typeof permissions !== 'undefined') {
|
||||
payload['permissions'] = permissions;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + path);
|
||||
return yield this.client.call('post', uri, {
|
||||
|
@ -2676,12 +2708,11 @@
|
|||
* @param {string} collectionId
|
||||
* @param {string} documentId
|
||||
* @param {Partial<Omit<Document, keyof Models.Document>>} data
|
||||
* @param {string[]} read
|
||||
* @param {string[]} write
|
||||
* @param {string[]} permissions
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
updateDocument(databaseId, collectionId, documentId, data, read, write) {
|
||||
updateDocument(databaseId, collectionId, documentId, data, permissions) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof databaseId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "databaseId"');
|
||||
|
@ -2697,11 +2728,8 @@
|
|||
if (typeof data !== 'undefined') {
|
||||
payload['data'] = data;
|
||||
}
|
||||
if (typeof read !== 'undefined') {
|
||||
payload['read'] = read;
|
||||
}
|
||||
if (typeof write !== 'undefined') {
|
||||
payload['write'] = write;
|
||||
if (typeof permissions !== 'undefined') {
|
||||
payload['permissions'] = permissions;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + path);
|
||||
return yield this.client.call('patch', uri, {
|
||||
|
@ -4415,7 +4443,7 @@
|
|||
* @param {string} projectId
|
||||
* @param {string} name
|
||||
* @param {string[]} scopes
|
||||
* @param {number} expire
|
||||
* @param {string} expire
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
@ -4480,7 +4508,7 @@
|
|||
* @param {string} keyId
|
||||
* @param {string} name
|
||||
* @param {string[]} scopes
|
||||
* @param {number} expire
|
||||
* @param {string} expire
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
@ -5068,9 +5096,8 @@
|
|||
*
|
||||
* @param {string} bucketId
|
||||
* @param {string} name
|
||||
* @param {string} permission
|
||||
* @param {string[]} read
|
||||
* @param {string[]} write
|
||||
* @param {boolean} fileSecurity
|
||||
* @param {string[]} permissions
|
||||
* @param {boolean} enabled
|
||||
* @param {number} maximumFileSize
|
||||
* @param {string[]} allowedFileExtensions
|
||||
|
@ -5079,7 +5106,7 @@
|
|||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createBucket(bucketId, name, permission, read, write, enabled, maximumFileSize, allowedFileExtensions, encryption, antivirus) {
|
||||
createBucket(bucketId, name, fileSecurity, permissions, enabled, maximumFileSize, allowedFileExtensions, encryption, antivirus) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof bucketId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "bucketId"');
|
||||
|
@ -5087,8 +5114,8 @@
|
|||
if (typeof name === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "name"');
|
||||
}
|
||||
if (typeof permission === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "permission"');
|
||||
if (typeof fileSecurity === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "fileSecurity"');
|
||||
}
|
||||
let path = '/storage/buckets';
|
||||
let payload = {};
|
||||
|
@ -5098,14 +5125,11 @@
|
|||
if (typeof name !== 'undefined') {
|
||||
payload['name'] = name;
|
||||
}
|
||||
if (typeof permission !== 'undefined') {
|
||||
payload['permission'] = permission;
|
||||
if (typeof permissions !== 'undefined') {
|
||||
payload['permissions'] = permissions;
|
||||
}
|
||||
if (typeof read !== 'undefined') {
|
||||
payload['read'] = read;
|
||||
}
|
||||
if (typeof write !== 'undefined') {
|
||||
payload['write'] = write;
|
||||
if (typeof fileSecurity !== 'undefined') {
|
||||
payload['fileSecurity'] = fileSecurity;
|
||||
}
|
||||
if (typeof enabled !== 'undefined') {
|
||||
payload['enabled'] = enabled;
|
||||
|
@ -5158,9 +5182,8 @@
|
|||
*
|
||||
* @param {string} bucketId
|
||||
* @param {string} name
|
||||
* @param {string} permission
|
||||
* @param {string[]} read
|
||||
* @param {string[]} write
|
||||
* @param {boolean} fileSecurity
|
||||
* @param {string[]} permissions
|
||||
* @param {boolean} enabled
|
||||
* @param {number} maximumFileSize
|
||||
* @param {string[]} allowedFileExtensions
|
||||
|
@ -5169,7 +5192,7 @@
|
|||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
updateBucket(bucketId, name, permission, read, write, enabled, maximumFileSize, allowedFileExtensions, encryption, antivirus) {
|
||||
updateBucket(bucketId, name, fileSecurity, permissions, enabled, maximumFileSize, allowedFileExtensions, encryption, antivirus) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof bucketId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "bucketId"');
|
||||
|
@ -5177,22 +5200,19 @@
|
|||
if (typeof name === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "name"');
|
||||
}
|
||||
if (typeof permission === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "permission"');
|
||||
if (typeof fileSecurity === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "fileSecurity"');
|
||||
}
|
||||
let path = '/storage/buckets/{bucketId}'.replace('{bucketId}', bucketId);
|
||||
let payload = {};
|
||||
if (typeof name !== 'undefined') {
|
||||
payload['name'] = name;
|
||||
}
|
||||
if (typeof permission !== 'undefined') {
|
||||
payload['permission'] = permission;
|
||||
if (typeof permissions !== 'undefined') {
|
||||
payload['permissions'] = permissions;
|
||||
}
|
||||
if (typeof read !== 'undefined') {
|
||||
payload['read'] = read;
|
||||
}
|
||||
if (typeof write !== 'undefined') {
|
||||
payload['write'] = write;
|
||||
if (typeof fileSecurity !== 'undefined') {
|
||||
payload['fileSecurity'] = fileSecurity;
|
||||
}
|
||||
if (typeof enabled !== 'undefined') {
|
||||
payload['enabled'] = enabled;
|
||||
|
@ -5310,12 +5330,11 @@
|
|||
* @param {string} bucketId
|
||||
* @param {string} fileId
|
||||
* @param {File} file
|
||||
* @param {string[]} read
|
||||
* @param {string[]} write
|
||||
* @param {string[]} permissions
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createFile(bucketId, fileId, file, read, write, onProgress = (progress) => { }) {
|
||||
createFile(bucketId, fileId, file, permissions, onProgress = (progress) => { }) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof bucketId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "bucketId"');
|
||||
|
@ -5334,11 +5353,8 @@
|
|||
if (typeof file !== 'undefined') {
|
||||
payload['file'] = file;
|
||||
}
|
||||
if (typeof read !== 'undefined') {
|
||||
payload['read'] = read;
|
||||
}
|
||||
if (typeof write !== 'undefined') {
|
||||
payload['write'] = write;
|
||||
if (typeof permissions !== 'undefined') {
|
||||
payload['permissions'] = permissions;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + path);
|
||||
if (!(file instanceof File)) {
|
||||
|
@ -5426,12 +5442,11 @@
|
|||
*
|
||||
* @param {string} bucketId
|
||||
* @param {string} fileId
|
||||
* @param {string[]} read
|
||||
* @param {string[]} write
|
||||
* @param {string[]} permissions
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
updateFile(bucketId, fileId, read, write) {
|
||||
updateFile(bucketId, fileId, permissions) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof bucketId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "bucketId"');
|
||||
|
@ -5441,11 +5456,8 @@
|
|||
}
|
||||
let path = '/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}', bucketId).replace('{fileId}', fileId);
|
||||
let payload = {};
|
||||
if (typeof read !== 'undefined') {
|
||||
payload['read'] = read;
|
||||
}
|
||||
if (typeof write !== 'undefined') {
|
||||
payload['write'] = write;
|
||||
if (typeof permissions !== 'undefined') {
|
||||
payload['permissions'] = permissions;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + path);
|
||||
return yield this.client.call('put', uri, {
|
||||
|
|
122
public/scripts/permissions-matrix.js
Normal file
122
public/scripts/permissions-matrix.js
Normal file
|
@ -0,0 +1,122 @@
|
|||
(function (window) {
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.data('permissionsMatrix', () => ({
|
||||
permissions: [],
|
||||
rawPermissions: [],
|
||||
load(permissions) {
|
||||
if (permissions === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.rawPermissions = permissions;
|
||||
|
||||
permissions.map(p => {
|
||||
let {type, role} = this.parsePermission(p);
|
||||
type = this.parseInputPermission(type);
|
||||
|
||||
let index = -1;
|
||||
let existing = this.permissions.find((p, idx) => {
|
||||
if (p.role === role) {
|
||||
index = idx;
|
||||
return true;
|
||||
}
|
||||
})
|
||||
if (existing === undefined) {
|
||||
this.permissions.push({
|
||||
role,
|
||||
[type]: true,
|
||||
});
|
||||
}
|
||||
if (index !== -1) {
|
||||
existing[type] = true;
|
||||
this.permissions[index] = existing;
|
||||
}
|
||||
});
|
||||
},
|
||||
addPermission(formId, role, permissions) {
|
||||
if (!document.getElementById(formId).reportValidity()) {
|
||||
return;
|
||||
}
|
||||
Object.entries(permissions).forEach(entry => {
|
||||
let [type, enabled] = entry;
|
||||
type = this.parseOutputPermission(type);
|
||||
if (enabled) {
|
||||
this.rawPermissions.push(this.buildPermission(type, role));
|
||||
}
|
||||
});
|
||||
this.permissions.push({
|
||||
role,
|
||||
...permissions,
|
||||
});
|
||||
this.reset();
|
||||
},
|
||||
updatePermission(index) {
|
||||
// Because the x-model does not update before the click event,
|
||||
// we setTimeout to give Alpine enough time to update the model.
|
||||
setTimeout(() => {
|
||||
const permission = this.permissions[index];
|
||||
|
||||
Object.keys(permission).forEach(key => {
|
||||
if (key === 'role') {
|
||||
return;
|
||||
}
|
||||
const parsedKey = this.parseOutputPermission(key);
|
||||
const permissionString = this.buildPermission(parsedKey, permission.role);
|
||||
if (permission[key]) {
|
||||
if (!this.rawPermissions.includes(permissionString)) {
|
||||
this.rawPermissions.push(permissionString);
|
||||
}
|
||||
} else {
|
||||
this.rawPermissions = this.rawPermissions.filter(p => {
|
||||
return !p.includes(permissionString);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
removePermission(index) {
|
||||
let row = this.permissions.splice(index, 1);
|
||||
if (row.length === 1) {
|
||||
this.rawPermissions = this.rawPermissions.filter(p => !p.includes(row[0].role));
|
||||
}
|
||||
},
|
||||
parsePermission(permission) {
|
||||
let parts = permission.split('(');
|
||||
let type = parts[0];
|
||||
let role = parts[1]
|
||||
.replace(')', '')
|
||||
.replace(' ', '')
|
||||
.replaceAll('"', '');
|
||||
return {type, role};
|
||||
},
|
||||
buildPermission(type, role) {
|
||||
return `${type}("${role}")`
|
||||
},
|
||||
parseInputPermission(key) {
|
||||
// Can't bind to a property named delete
|
||||
if (key === 'delete') {
|
||||
return 'xdelete';
|
||||
}
|
||||
return key;
|
||||
},
|
||||
parseOutputPermission(key) {
|
||||
// Can't bind to a property named delete
|
||||
if (key === 'xdelete') {
|
||||
return 'delete';
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}));
|
||||
Alpine.data('permissionsRow', () => ({
|
||||
role: '',
|
||||
read: false,
|
||||
create: false,
|
||||
update: false,
|
||||
xdelete: false,
|
||||
reset() {
|
||||
this.role = '';
|
||||
this.read = this.create = this.update = this.xdelete = false;
|
||||
}
|
||||
}));
|
||||
});
|
||||
})(window);
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
window.ls.container.set('form', function () {
|
||||
|
||||
function cast(value, to) {
|
||||
function cast(value, from, to,) {
|
||||
if (value && Array.isArray(value) && to !== 'array') {
|
||||
value = value.map(element => cast(element, to));
|
||||
value = value.map(element => cast(element, from, to));
|
||||
return value;
|
||||
}
|
||||
switch (to) {
|
||||
|
@ -29,7 +29,18 @@
|
|||
value = (value) ? JSON.parse(value) : [];
|
||||
break;
|
||||
case 'array':
|
||||
value = (value && value.constructor && value.constructor === Array) ? value : [value];
|
||||
if (value && value.constructor && value.constructor === Array) {
|
||||
break;
|
||||
}
|
||||
if (from === 'csv') {
|
||||
if (value.length === 0) {
|
||||
value = [];
|
||||
} else {
|
||||
value = value.split(',');
|
||||
}
|
||||
} else {
|
||||
value = [value];
|
||||
}
|
||||
break;
|
||||
case 'array-empty':
|
||||
value = [];
|
||||
|
@ -49,6 +60,7 @@
|
|||
let name = element.getAttribute('name');
|
||||
let type = element.getAttribute('type');
|
||||
let castTo = element.getAttribute('data-cast-to');
|
||||
let castFrom = element.getAttribute('data-cast-from');
|
||||
let ref = json;
|
||||
|
||||
if (name && 'FORM' !== element.tagName) {
|
||||
|
@ -121,7 +133,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
json[name] = cast(json[name], castTo); // Apply casting
|
||||
json[name] = cast(json[name], castFrom, castTo); // Apply casting
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,16 +65,12 @@
|
|||
const file = formData.get('file');
|
||||
const fileId = formData.get('fileId');
|
||||
let id = fileId === 'unique()' ? performance.now() : fileId;
|
||||
let read = formData.get('read');
|
||||
if(!file || !fileId) {
|
||||
return;
|
||||
}
|
||||
if(read) {
|
||||
read = JSON.parse(read);
|
||||
}
|
||||
let write = formData.get('write');
|
||||
if(write) {
|
||||
write = JSON.parse(write);
|
||||
let permissions = formData.get('permissions');
|
||||
if(permissions) {
|
||||
permissions = permissions.split(',');
|
||||
}
|
||||
|
||||
if(this.getFile(id)) {
|
||||
|
@ -103,8 +99,7 @@
|
|||
bucketId,
|
||||
fileId,
|
||||
file,
|
||||
read,
|
||||
write,
|
||||
permissions,
|
||||
(progress) => {
|
||||
this.updateFile(id, {
|
||||
id: progress.$id,
|
||||
|
|
|
@ -109,14 +109,11 @@
|
|||
input.addEventListener("change", function() {
|
||||
var message = alerts.add({ text: labelLoading, class: "" }, 0);
|
||||
var files = input.files;
|
||||
var read = JSON.parse(
|
||||
expression.parse(element.dataset["read"] || "[]")
|
||||
);
|
||||
var write = JSON.parse(
|
||||
expression.parse(element.dataset["write"] || "[]")
|
||||
);
|
||||
var permissions = JSON.parse(
|
||||
expression.parse(element.dataset["permissions"] || "[]")
|
||||
)
|
||||
|
||||
sdk.storage.createFile('default', 'unique()', files[0], read, write).then(
|
||||
sdk.storage.createFile('default', 'unique()', files[0], permissions).then(
|
||||
function(response) {
|
||||
onComplete(message);
|
||||
|
||||
|
|
24
public/styles/comps/permissions-matrix.less
Normal file
24
public/styles/comps/permissions-matrix.less
Normal file
|
@ -0,0 +1,24 @@
|
|||
.permissions-matrix {
|
||||
th:first-child, td:first-child {
|
||||
width: 100px;
|
||||
}
|
||||
th:not(:first-child):not(:last-child), td:not(:first-child):not(:last-child) {
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
th:last-child, td:last-child {
|
||||
width: 20px;
|
||||
}
|
||||
td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
input, p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
margin-left: 15px;
|
||||
}
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@ img[src=""] {
|
|||
@import "comps/preview-box";
|
||||
@import "comps/upload-box";
|
||||
@import "comps/pill";
|
||||
@import "comps/permissions-matrix";
|
||||
|
||||
html {
|
||||
padding: 0;
|
||||
|
|
|
@ -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'
|
||||
]));
|
||||
|
|
62
src/Appwrite/Permissions/PermissionsProcessor.php
Normal file
62
src/Appwrite/Permissions/PermissionsProcessor.php
Normal file
|
@ -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', [
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
namespace Tests\E2E\Scopes;
|
||||
|
||||
use Utopia\Database\ID;
|
||||
|
||||
trait ProjectConsole
|
||||
{
|
||||
public function getProject(): array
|
||||
{
|
||||
return [
|
||||
'$id' => 'console',
|
||||
'$id' => ID::custom('console'),
|
||||
'name' => 'Appwrite',
|
||||
'apiKey' => '',
|
||||
];
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Tests\E2E\Scopes;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
trait ProjectCustom
|
||||
{
|
||||
|
@ -26,7 +27,7 @@ trait ProjectCustom
|
|||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Demo Project Team',
|
||||
]);
|
||||
$this->assertEquals(201, $team['headers']['status-code']);
|
||||
|
@ -39,7 +40,7 @@ trait ProjectCustom
|
|||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'projectId' => 'unique()',
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Demo Project',
|
||||
'teamId' => $team['body']['$id'],
|
||||
'description' => 'Demo Project Description',
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Tests\E2E\Scopes;
|
|||
|
||||
use Tests\E2E\Client;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
abstract class Scope extends TestCase
|
||||
{
|
||||
|
@ -87,7 +88,7 @@ abstract class Scope extends TestCase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
@ -107,7 +108,7 @@ abstract class Scope extends TestCase
|
|||
$session = $this->client->parseCookie((string)$session['headers']['set-cookie'])['a_session_console'];
|
||||
|
||||
self::$root = [
|
||||
'$id' => $root['body']['$id'],
|
||||
'$id' => ID::custom($root['body']['$id']),
|
||||
'name' => $root['body']['name'],
|
||||
'email' => $root['body']['email'],
|
||||
'session' => $session,
|
||||
|
@ -139,7 +140,7 @@ abstract class Scope extends TestCase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
@ -159,7 +160,7 @@ abstract class Scope extends TestCase
|
|||
$session = $this->client->parseCookie((string)$session['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
||||
|
||||
self::$user[$this->getProject()['$id']] = [
|
||||
'$id' => $user['body']['$id'],
|
||||
'$id' => ID::custom($user['body']['$id']),
|
||||
'name' => $user['body']['name'],
|
||||
'email' => $user['body']['email'],
|
||||
'session' => $session,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Tests\E2E\Services\Account;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\DateTime;
|
||||
|
||||
trait AccountBase
|
||||
|
@ -21,7 +22,7 @@ trait AccountBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
@ -44,7 +45,7 @@ trait AccountBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
@ -57,7 +58,7 @@ trait AccountBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => '',
|
||||
'password' => '',
|
||||
]);
|
||||
|
@ -69,7 +70,7 @@ trait AccountBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => '',
|
||||
]);
|
||||
|
@ -81,7 +82,7 @@ trait AccountBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => '',
|
||||
'password' => $password,
|
||||
]);
|
||||
|
@ -664,7 +665,7 @@ trait AccountBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $data['email'],
|
||||
'password' => $data['password'],
|
||||
'name' => $data['name'],
|
||||
|
@ -824,7 +825,7 @@ trait AccountBase
|
|||
$this->assertEquals('Account Verification', $lastEmail['subject']);
|
||||
|
||||
$verification = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256);
|
||||
$expireTime = strpos($lastEmail['text'], 'expire=' . urlencode($response['body']['expire']), 0);
|
||||
$expireTime = strpos($lastEmail['text'], 'expire=' . urlencode(DateTime::format(new \DateTime($response['body']['expire']))), 0);
|
||||
$this->assertNotFalse($expireTime);
|
||||
|
||||
$secretTest = strpos($lastEmail['text'], 'secret=' . $response['body']['secret'], 0);
|
||||
|
@ -898,7 +899,7 @@ trait AccountBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'userId' => 'ewewe',
|
||||
'userId' => ID::custom('ewewe'),
|
||||
'secret' => $verification,
|
||||
]);
|
||||
|
||||
|
@ -1127,7 +1128,7 @@ trait AccountBase
|
|||
|
||||
$recovery = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256);
|
||||
|
||||
$expireTime = strpos($lastEmail['text'], 'expire=' . urlencode($response['body']['expire']), 0);
|
||||
$expireTime = strpos($lastEmail['text'], 'expire=' . urlencode(DateTime::format(new \DateTime($response['body']['expire']))), 0);
|
||||
|
||||
$this->assertNotFalse($expireTime);
|
||||
|
||||
|
@ -1213,7 +1214,7 @@ trait AccountBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'ewewe',
|
||||
'userId' => ID::custom('ewewe'),
|
||||
'secret' => $recovery,
|
||||
'password' => $newPassowrd,
|
||||
'passwordAgain' => $newPassowrd,
|
||||
|
@ -1262,7 +1263,7 @@ trait AccountBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
// 'url' => 'http://localhost/magiclogin',
|
||||
]);
|
||||
|
@ -1280,7 +1281,7 @@ trait AccountBase
|
|||
|
||||
$token = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256);
|
||||
|
||||
$expireTime = strpos($lastEmail['text'], 'expire=' . urlencode($response['body']['expire']), 0);
|
||||
$expireTime = strpos($lastEmail['text'], 'expire=' . urlencode(DateTime::format(new \DateTime($response['body']['expire']))), 0);
|
||||
|
||||
$this->assertNotFalse($expireTime);
|
||||
|
||||
|
@ -1300,7 +1301,7 @@ trait AccountBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'url' => 'localhost/magiclogin',
|
||||
]);
|
||||
|
@ -1312,7 +1313,7 @@ trait AccountBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'url' => 'http://remotehost/magiclogin',
|
||||
]);
|
||||
|
@ -1388,7 +1389,7 @@ trait AccountBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'ewewe',
|
||||
'userId' => ID::custom('ewewe'),
|
||||
'secret' => $token,
|
||||
]);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ use Tests\E2E\Scopes\Scope;
|
|||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
use function sleep;
|
||||
|
||||
|
@ -71,7 +72,7 @@ class AccountCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
@ -152,7 +153,7 @@ class AccountCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
@ -231,7 +232,7 @@ class AccountCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
@ -411,7 +412,7 @@ class AccountCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password
|
||||
]);
|
||||
|
@ -690,7 +691,7 @@ class AccountCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'phone' => $number,
|
||||
]);
|
||||
|
||||
|
@ -709,7 +710,7 @@ class AccountCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()'
|
||||
'userId' => ID::unique()
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
@ -738,7 +739,7 @@ class AccountCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'ewewe',
|
||||
'userId' => ID::custom('ewewe'),
|
||||
'secret' => $token,
|
||||
]);
|
||||
|
||||
|
@ -964,7 +965,7 @@ class AccountCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'userId' => 'ewewe',
|
||||
'userId' => ID::custom('ewewe'),
|
||||
'secret' => Mock::$digits,
|
||||
]);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
class AccountCustomServerTest extends Scope
|
||||
{
|
||||
|
@ -26,7 +27,7 @@ class AccountCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,6 +6,9 @@ use Tests\E2E\Scopes\Scope;
|
|||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\SideConsole;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class DatabasesConsoleClientTest extends Scope
|
||||
{
|
||||
|
@ -19,7 +22,7 @@ class DatabasesConsoleClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'invalidDocumentDatabase',
|
||||
]);
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
|
@ -33,14 +36,18 @@ class DatabasesConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'collectionId' => 'unique()',
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Movies',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document',
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals($movies['headers']['status-code'], 201);
|
||||
$this->assertEquals(201, $movies['headers']['status-code']);
|
||||
$this->assertEquals($movies['body']['name'], 'Movies');
|
||||
|
||||
return ['moviesId' => $movies['body']['$id'], 'databaseId' => $databaseId];
|
||||
|
@ -63,7 +70,7 @@ class DatabasesConsoleClientTest extends Scope
|
|||
// 'range' => '32h'
|
||||
// ]);
|
||||
|
||||
// $this->assertEquals($response['headers']['status-code'], 400);
|
||||
// $this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// /**
|
||||
// * Test for SUCCESS
|
||||
|
@ -76,7 +83,7 @@ class DatabasesConsoleClientTest extends Scope
|
|||
// 'range' => '24h'
|
||||
// ]);
|
||||
|
||||
// $this->assertEquals($response['headers']['status-code'], 200);
|
||||
// $this->assertEquals(200, $response['headers']['status-code']);
|
||||
// $this->assertEquals(count($response['body']), 11);
|
||||
// $this->assertEquals($response['body']['range'], '24h');
|
||||
// $this->assertIsArray($response['body']['documentsCount']);
|
||||
|
@ -109,7 +116,7 @@ class DatabasesConsoleClientTest extends Scope
|
|||
'range' => '32h'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/randomCollectionId/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -118,7 +125,7 @@ class DatabasesConsoleClientTest extends Scope
|
|||
'range' => '24h'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 404);
|
||||
$this->assertEquals(404, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -130,7 +137,7 @@ class DatabasesConsoleClientTest extends Scope
|
|||
'range' => '24h'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(count($response['body']), 6);
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['documentsCount']);
|
||||
|
@ -154,7 +161,7 @@ class DatabasesConsoleClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertEquals(200, $logs['headers']['status-code']);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertIsNumeric($logs['body']['total']);
|
||||
|
||||
|
@ -165,7 +172,7 @@ class DatabasesConsoleClientTest extends Scope
|
|||
'limit' => 1
|
||||
]);
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertEquals(200, $logs['headers']['status-code']);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||
$this->assertIsNumeric($logs['body']['total']);
|
||||
|
@ -177,7 +184,7 @@ class DatabasesConsoleClientTest extends Scope
|
|||
'offset' => 1
|
||||
]);
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertEquals(200, $logs['headers']['status-code']);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertIsNumeric($logs['body']['total']);
|
||||
|
||||
|
@ -189,7 +196,7 @@ class DatabasesConsoleClientTest extends Scope
|
|||
'limit' => 1
|
||||
]);
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertEquals(200, $logs['headers']['status-code']);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||
$this->assertIsNumeric($logs['body']['total']);
|
||||
|
|
|
@ -6,6 +6,9 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class DatabasesCustomClientTest extends Scope
|
||||
{
|
||||
|
@ -32,7 +35,7 @@ class DatabasesCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'permissionCheckDatabase',
|
||||
'databaseId' => ID::custom('permissionCheckDatabase'),
|
||||
'name' => 'Test Database',
|
||||
]);
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
|
@ -45,11 +48,10 @@ class DatabasesCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'permissionCheck',
|
||||
'collectionId' => ID::custom('permissionCheck'),
|
||||
'name' => 'permissionCheck',
|
||||
'read' => [],
|
||||
'write' => [],
|
||||
'permission' => 'document'
|
||||
'permissions' => [],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
|
@ -74,13 +76,18 @@ class DatabasesCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'documentId' => 'permissionCheckDocument',
|
||||
'documentId' => ID::custom('permissionCheckDocument'),
|
||||
'data' => [
|
||||
'name' => 'AppwriteBeginner',
|
||||
],
|
||||
'read' => ['user:' . $userId, 'user:user2'],
|
||||
'write' => ['user:' . $userId],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user(ID::custom('user2'))),
|
||||
Permission::read(Role::user($userId)),
|
||||
Permission::update(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Update document
|
||||
|
@ -93,6 +100,7 @@ class DatabasesCustomClientTest extends Scope
|
|||
'name' => 'AppwriteExpert',
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
// Get name of the document, should be the new one
|
||||
|
|
|
@ -7,6 +7,9 @@ use Tests\E2E\Scopes\Scope;
|
|||
use Tests\E2E\Scopes\SideServer;
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class DatabasesCustomServerTest extends Scope
|
||||
{
|
||||
|
@ -21,7 +24,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'first',
|
||||
'databaseId' => ID::custom('first'),
|
||||
'name' => 'Test 1',
|
||||
]);
|
||||
$this->assertEquals(201, $test1['headers']['status-code']);
|
||||
|
@ -32,7 +35,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'second',
|
||||
'databaseId' => ID::custom('second'),
|
||||
'name' => 'Test 2',
|
||||
]);
|
||||
$this->assertEquals(201, $test2['headers']['status-code']);
|
||||
|
@ -172,7 +175,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'name' => 'Test 1',
|
||||
'databaseId' => 'first',
|
||||
'databaseId' => ID::custom('first'),
|
||||
]);
|
||||
|
||||
$this->assertEquals(409, $response['headers']['status-code']);
|
||||
|
@ -233,7 +236,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'invalidDocumentDatabase',
|
||||
]);
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
|
@ -249,10 +252,14 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'name' => 'Test 1',
|
||||
'collectionId' => 'first',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document'
|
||||
'collectionId' => ID::custom('first'),
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$test2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
|
@ -261,10 +268,14 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'name' => 'Test 2',
|
||||
'collectionId' => 'second',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document'
|
||||
'collectionId' => ID::custom('second'),
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
|
@ -392,7 +403,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'cursor' => 'unknown',
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// This collection already exists
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
|
@ -401,13 +412,17 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'name' => 'Test 1',
|
||||
'collectionId' => 'first',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document'
|
||||
'collectionId' => ID::custom('first'),
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 409);
|
||||
$this->assertEquals(409, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
public function testDeleteAttribute(): array
|
||||
|
@ -417,7 +432,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'invalidDocumentDatabase',
|
||||
]);
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
|
@ -434,14 +449,18 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'unique()',
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Actors',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document'
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals($actors['headers']['status-code'], 201);
|
||||
$this->assertEquals(201, $actors['headers']['status-code']);
|
||||
$this->assertEquals($actors['body']['name'], 'Actors');
|
||||
|
||||
$firstName = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'] . '/attributes/string', array_merge([
|
||||
|
@ -483,14 +502,17 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'firstName' => 'lorem',
|
||||
'lastName' => 'ipsum',
|
||||
'unneeded' => 'dolor'
|
||||
],
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$index = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'] . '/indexes', array_merge([
|
||||
|
@ -516,7 +538,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
|
||||
$unneededId = $unneeded['body']['key'];
|
||||
|
||||
$this->assertEquals($collection['headers']['status-code'], 200);
|
||||
$this->assertEquals(200, $collection['headers']['status-code']);
|
||||
$this->assertIsArray($collection['body']['attributes']);
|
||||
$this->assertCount(3, $collection['body']['attributes']);
|
||||
$this->assertEquals($collection['body']['attributes'][0]['key'], $firstName['body']['key']);
|
||||
|
@ -532,7 +554,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals($attribute['headers']['status-code'], 204);
|
||||
$this->assertEquals(204, $attribute['headers']['status-code']);
|
||||
|
||||
sleep(2);
|
||||
|
||||
|
@ -551,7 +573,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), []);
|
||||
|
||||
$this->assertEquals($collection['headers']['status-code'], 200);
|
||||
$this->assertEquals(200, $collection['headers']['status-code']);
|
||||
$this->assertIsArray($collection['body']['attributes']);
|
||||
$this->assertCount(2, $collection['body']['attributes']);
|
||||
$this->assertEquals($collection['body']['attributes'][0]['key'], $firstName['body']['key']);
|
||||
|
@ -576,7 +598,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals($index['headers']['status-code'], 204);
|
||||
$this->assertEquals(204, $index['headers']['status-code']);
|
||||
|
||||
// Wait for database worker to finish deleting index
|
||||
sleep(2);
|
||||
|
@ -660,7 +682,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals($deleted['headers']['status-code'], 204);
|
||||
$this->assertEquals(204, $deleted['headers']['status-code']);
|
||||
|
||||
// wait for database worker to complete
|
||||
sleep(2);
|
||||
|
@ -686,7 +708,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals($deleted['headers']['status-code'], 204);
|
||||
$this->assertEquals(204, $deleted['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
@ -698,7 +720,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'invalidDocumentDatabase',
|
||||
]);
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
|
@ -710,11 +732,15 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'unique()',
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestCleanupDuplicateIndexOnDeleteAttribute',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document',
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
|
@ -784,7 +810,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals($deleted['headers']['status-code'], 204);
|
||||
$this->assertEquals(204, $deleted['headers']['status-code']);
|
||||
|
||||
// wait for database worker to complete
|
||||
sleep(2);
|
||||
|
@ -810,7 +836,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals($deleted['headers']['status-code'], 204);
|
||||
$this->assertEquals(204, $deleted['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -826,41 +852,43 @@ class DatabasesCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'firstName' => 'Tom',
|
||||
'lastName' => 'Holland',
|
||||
],
|
||||
'read' => ['user:' . $this->getUser()['$id']],
|
||||
'write' => ['user:' . $this->getUser()['$id']],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
]);
|
||||
|
||||
$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' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'firstName' => 'Samuel',
|
||||
'lastName' => 'Jackson',
|
||||
],
|
||||
'read' => ['user:' . $this->getUser()['$id']],
|
||||
'write' => ['user:' . $this->getUser()['$id']],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals($document1['headers']['status-code'], 201);
|
||||
$this->assertIsArray($document1['body']['$read']);
|
||||
$this->assertIsArray($document1['body']['$write']);
|
||||
$this->assertCount(1, $document1['body']['$read']);
|
||||
$this->assertCount(1, $document1['body']['$write']);
|
||||
$this->assertEquals(201, $document1['headers']['status-code']);
|
||||
$this->assertIsArray($document1['body']['$permissions']);
|
||||
$this->assertCount(3, $document1['body']['$permissions']);
|
||||
$this->assertEquals($document1['body']['firstName'], 'Tom');
|
||||
$this->assertEquals($document1['body']['lastName'], 'Holland');
|
||||
|
||||
$this->assertEquals($document2['headers']['status-code'], 201);
|
||||
$this->assertIsArray($document2['body']['$read']);
|
||||
$this->assertIsArray($document2['body']['$write']);
|
||||
$this->assertCount(1, $document2['body']['$read']);
|
||||
$this->assertCount(1, $document2['body']['$write']);
|
||||
$this->assertEquals(201, $document2['headers']['status-code']);
|
||||
$this->assertIsArray($document2['body']['$permissions']);
|
||||
$this->assertCount(3, $document2['body']['$permissions']);
|
||||
$this->assertEquals($document2['body']['firstName'], 'Samuel');
|
||||
$this->assertEquals($document2['body']['lastName'], 'Jackson');
|
||||
|
||||
|
@ -871,7 +899,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 204);
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
$this->assertEquals($response['body'], "");
|
||||
|
||||
// Try to get the collection and check if it has been deleted
|
||||
|
@ -880,7 +908,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 404);
|
||||
$this->assertEquals(404, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
// Adds several minutes to test to replicate coverage in Utopia\Database unit tests
|
||||
|
@ -899,11 +927,11 @@ class DatabasesCustomServerTest extends Scope
|
|||
// 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
// 'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
// ]), [
|
||||
// 'collectionId' => 'unique()',
|
||||
// 'collectionId' => ID::unique(),
|
||||
// 'name' => 'attributeCountLimit',
|
||||
// 'read' => ['role:all'],
|
||||
// 'write' => ['role:all'],
|
||||
// 'permission' => 'document',
|
||||
// 'read' => ['any'],
|
||||
// 'write' => ['any'],
|
||||
// 'documentSecurity' => true,
|
||||
// ]);
|
||||
|
||||
// $collectionId = $collection['body']['$id'];
|
||||
|
@ -944,7 +972,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'invalidDocumentDatabase',
|
||||
]);
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
|
@ -956,14 +984,18 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'attributeRowWidthLimit',
|
||||
'collectionId' => ID::custom('attributeRowWidthLimit'),
|
||||
'name' => 'attributeRowWidthLimit',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document',
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals($collection['headers']['status-code'], 201);
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertEquals($collection['body']['name'], 'attributeRowWidthLimit');
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
@ -980,7 +1012,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals($attribute['headers']['status-code'], 202);
|
||||
$this->assertEquals(202, $attribute['headers']['status-code']);
|
||||
}
|
||||
|
||||
sleep(5);
|
||||
|
@ -1006,7 +1038,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'invalidDocumentDatabase',
|
||||
]);
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
|
@ -1018,14 +1050,18 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'testLimitException',
|
||||
'collectionId' => ID::custom('testLimitException'),
|
||||
'name' => 'testLimitException',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document',
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals($collection['headers']['status-code'], 201);
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertEquals($collection['body']['name'], 'testLimitException');
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
@ -1043,7 +1079,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals($attribute['headers']['status-code'], 202);
|
||||
$this->assertEquals(202, $attribute['headers']['status-code']);
|
||||
}
|
||||
|
||||
sleep(20);
|
||||
|
@ -1054,7 +1090,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals($collection['headers']['status-code'], 200);
|
||||
$this->assertEquals(200, $collection['headers']['status-code']);
|
||||
$this->assertEquals($collection['body']['name'], 'testLimitException');
|
||||
$this->assertIsArray($collection['body']['attributes']);
|
||||
$this->assertIsArray($collection['body']['indexes']);
|
||||
|
@ -1092,7 +1128,7 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals($collection['headers']['status-code'], 200);
|
||||
$this->assertEquals(200, $collection['headers']['status-code']);
|
||||
$this->assertEquals($collection['body']['name'], 'testLimitException');
|
||||
$this->assertIsArray($collection['body']['attributes']);
|
||||
$this->assertIsArray($collection['body']['indexes']);
|
||||
|
|
|
@ -6,6 +6,9 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class DatabasesPermissionsGuestTest extends Scope
|
||||
{
|
||||
|
@ -20,7 +23,7 @@ class DatabasesPermissionsGuestTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'InvalidDocumentDatabase',
|
||||
]);
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
|
@ -28,11 +31,15 @@ class DatabasesPermissionsGuestTest extends Scope
|
|||
|
||||
$databaseId = $database['body']['$id'];
|
||||
$movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [
|
||||
'collectionId' => 'unique()',
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Movies',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document',
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$collection = ['id' => $movies['body']['$id']];
|
||||
|
@ -49,36 +56,36 @@ class DatabasesPermissionsGuestTest extends Scope
|
|||
}
|
||||
|
||||
/**
|
||||
* [string[] $read, string[] $write]
|
||||
* [string[] $permissions]
|
||||
*/
|
||||
public function readDocumentsProvider()
|
||||
{
|
||||
return [
|
||||
[['role:all'], []],
|
||||
[['role:member'], []],
|
||||
[[] ,['role:all']],
|
||||
[['role:all'], ['role:all']],
|
||||
[['role:member'], ['role:member']],
|
||||
[['role:all'], ['role:member']],
|
||||
[[Permission::read(Role::any())]],
|
||||
[[Permission::read(Role::users())]],
|
||||
[[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())]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider readDocumentsProvider
|
||||
*/
|
||||
public function testReadDocuments($read, $write)
|
||||
public function testReadDocuments($permissions)
|
||||
{
|
||||
$data = $this->createCollection();
|
||||
$collectionId = $data['collectionId'];
|
||||
$databaseId = $data['databaseId'];
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', $this->getServerHeader(), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Lorem',
|
||||
],
|
||||
'read' => $read,
|
||||
'write' => $write,
|
||||
'permissions' => $permissions,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [
|
||||
|
@ -87,7 +94,13 @@ class DatabasesPermissionsGuestTest extends Scope
|
|||
]);
|
||||
|
||||
foreach ($documents['body']['documents'] as $document) {
|
||||
$this->assertContains('role:all', $document['$read']);
|
||||
foreach ($document['$permissions'] as $permission) {
|
||||
$permission = Permission::parse($permission);
|
||||
if ($permission->getPermission() != 'read') {
|
||||
continue;
|
||||
}
|
||||
$this->assertEquals($permission->getRole(), Role::any()->toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class DatabasesPermissionsMemberTest extends Scope
|
||||
{
|
||||
|
@ -23,38 +26,36 @@ class DatabasesPermissionsMemberTest extends Scope
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* [string[] $read, string[] $write]
|
||||
*/
|
||||
public function readDocumentsProvider()
|
||||
public function permissionsProvider(): array
|
||||
{
|
||||
return [
|
||||
[['role:all'], []],
|
||||
[['role:member'], []],
|
||||
[['user:random'], []],
|
||||
[['user:lorem'] ,['user:lorem']],
|
||||
[['user:dolor'] ,['user:dolor']],
|
||||
[['user:dolor', 'user:lorem'] ,['user:dolor']],
|
||||
[[], ['role:all']],
|
||||
[['role:all'], ['role:all']],
|
||||
[['role:member'], ['role:member']],
|
||||
[['role:all'], ['role:member']],
|
||||
[[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())]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup database
|
||||
*
|
||||
* Data providers lose object state
|
||||
* so explicitly pass [$users, $collections] to each iteration
|
||||
* Data providers lose object state so explicitly pass [$users, $collections] to each iteration
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testSetupDatabase(): array
|
||||
{
|
||||
$this->createUsers();
|
||||
|
||||
$db = $this->client->call(Client::METHOD_POST, '/databases', $this->getServerHeader(), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Test Database',
|
||||
]);
|
||||
$this->assertEquals(201, $db['headers']['status-code']);
|
||||
|
@ -62,11 +63,15 @@ class DatabasesPermissionsMemberTest extends Scope
|
|||
$databaseId = $db['body']['$id'];
|
||||
|
||||
$public = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [
|
||||
'collectionId' => 'unique()',
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Movies',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document',
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
$this->assertEquals(201, $public['headers']['status-code']);
|
||||
|
||||
|
@ -80,11 +85,15 @@ class DatabasesPermissionsMemberTest extends Scope
|
|||
$this->assertEquals(202, $response['headers']['status-code']);
|
||||
|
||||
$private = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [
|
||||
'collectionId' => 'unique()',
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Private Movies',
|
||||
'read' => ['role:member'],
|
||||
'write' => ['role:member'],
|
||||
'permission' => 'document',
|
||||
'permissions' => [
|
||||
Permission::read(Role::users()),
|
||||
Permission::create(Role::users()),
|
||||
Permission::update(Role::users()),
|
||||
Permission::delete(Role::users()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
$this->assertEquals(201, $private['headers']['status-code']);
|
||||
|
||||
|
@ -108,37 +117,35 @@ class DatabasesPermissionsMemberTest extends Scope
|
|||
|
||||
/**
|
||||
* Data provider params are passed before test dependencies
|
||||
* @dataProvider readDocumentsProvider
|
||||
* @dataProvider permissionsProvider
|
||||
* @depends testSetupDatabase
|
||||
*/
|
||||
public function testReadDocuments($read, $write, $data)
|
||||
public function testReadDocuments($permissions, $data)
|
||||
{
|
||||
$users = $data['users'];
|
||||
$collections = $data['collections'];
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collections['public'] . '/documents', $this->getServerHeader(), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Lorem',
|
||||
],
|
||||
'read' => $read,
|
||||
'write' => $write,
|
||||
'permissions' => $permissions
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collections['private'] . '/documents', $this->getServerHeader(), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Lorem',
|
||||
],
|
||||
'read' => $read,
|
||||
'write' => $write,
|
||||
'permissions' => $permissions
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Check role:all collection
|
||||
* Check "any" collection
|
||||
*/
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collections['public'] . '/documents', [
|
||||
'origin' => 'http://localhost',
|
||||
|
@ -148,9 +155,23 @@ class DatabasesPermissionsMemberTest extends Scope
|
|||
]);
|
||||
|
||||
foreach ($documents['body']['documents'] as $document) {
|
||||
$hasPermissions = \array_reduce(['role:all', 'role:member', 'user:' . $users['user1']['$id']], function ($carry, $item) use ($document) {
|
||||
return $carry ? true : \in_array($item, $document['$read']);
|
||||
$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);
|
||||
}
|
||||
|
||||
|
@ -165,9 +186,23 @@ class DatabasesPermissionsMemberTest extends Scope
|
|||
]);
|
||||
|
||||
foreach ($documents['body']['documents'] as $document) {
|
||||
$hasPermissions = \array_reduce(['role:all', 'role:member', 'user:' . $users['user1']['$id']], function ($carry, $item) use ($document) {
|
||||
return $carry ? true : \in_array($item, $document['$read']);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class DatabasesPermissionsTeamTest extends Scope
|
||||
{
|
||||
|
@ -42,11 +45,14 @@ class DatabasesPermissionsTeamTest extends Scope
|
|||
$this->assertEquals(201, $db['headers']['status-code']);
|
||||
|
||||
$collection1 = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/collections', $this->getServerHeader(), [
|
||||
'collectionId' => 'collection1',
|
||||
'collectionId' => ID::custom('collection1'),
|
||||
'name' => 'Collection 1',
|
||||
'read' => ['team:' . $teams['team1']['$id']],
|
||||
'write' => ['team:' . $teams['team1']['$id'] . '/admin'],
|
||||
'permission' => 'collection',
|
||||
'permissions' => [
|
||||
Permission::read(Role::team($teams['team1']['$id'])),
|
||||
Permission::create(Role::team($teams['team1']['$id'], 'admin')),
|
||||
Permission::update(Role::team($teams['team1']['$id'], 'admin')),
|
||||
Permission::delete(Role::team($teams['team1']['$id'], 'admin')),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->collections['collection1'] = $collection1['body']['$id'];
|
||||
|
@ -58,11 +64,14 @@ class DatabasesPermissionsTeamTest extends Scope
|
|||
]);
|
||||
|
||||
$collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/collections', $this->getServerHeader(), [
|
||||
'collectionId' => 'collection2',
|
||||
'collectionId' => ID::custom('collection2'),
|
||||
'name' => 'Collection 2',
|
||||
'read' => ['team:' . $teams['team2']['$id']],
|
||||
'write' => ['team:' . $teams['team2']['$id'] . '/owner'],
|
||||
'permission' => 'collection',
|
||||
'permissions' => [
|
||||
Permission::read(Role::team($teams['team2']['$id'])),
|
||||
Permission::create(Role::team($teams['team2']['$id'], 'owner')),
|
||||
Permission::update(Role::team($teams['team2']['$id'], 'owner')),
|
||||
Permission::delete(Role::team($teams['team2']['$id'], 'owner')),
|
||||
]
|
||||
]);
|
||||
|
||||
$this->collections['collection2'] = $collection2['body']['$id'];
|
||||
|
@ -132,7 +141,7 @@ class DatabasesPermissionsTeamTest extends Scope
|
|||
$this->createCollections($this->teams);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/collections/' . $this->collections['collection1'] . '/documents', $this->getServerHeader(), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Lorem',
|
||||
],
|
||||
|
@ -140,7 +149,7 @@ class DatabasesPermissionsTeamTest extends Scope
|
|||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/collections/' . $this->collections['collection2'] . '/documents', $this->getServerHeader(), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Ipsum',
|
||||
],
|
||||
|
@ -183,7 +192,7 @@ class DatabasesPermissionsTeamTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users[$user]['session'],
|
||||
], [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Ipsum',
|
||||
],
|
||||
|
|
|
@ -6,6 +6,7 @@ use Tests\E2E\Scopes\Scope;
|
|||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\SideConsole;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
class FunctionsConsoleClientTest extends Scope
|
||||
{
|
||||
|
@ -18,9 +19,9 @@ class FunctionsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => ['user:' . $this->getUser()['$id']],
|
||||
'execute' => ["user:{$this->getUser()['$id']}"],
|
||||
'runtime' => 'php-8.0',
|
||||
'vars' => [
|
||||
'funcKey1' => 'funcValue1',
|
||||
|
@ -41,7 +42,7 @@ class FunctionsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Failure',
|
||||
'execute' => ['some-random-string'],
|
||||
'runtime' => 'php-8.0'
|
||||
|
|
|
@ -9,6 +9,8 @@ use Tests\E2E\Scopes\Scope;
|
|||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class FunctionsCustomClientTest extends Scope
|
||||
{
|
||||
|
@ -25,7 +27,7 @@ class FunctionsCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'vars' => [
|
||||
'funcKey1' => 'funcValue1',
|
||||
|
@ -55,9 +57,9 @@ class FunctionsCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => ['user:' . $this->getUser()['$id']],
|
||||
'execute' => ["user:{$this->getUser()['$id']}"],
|
||||
'runtime' => 'php-8.0',
|
||||
'vars' => [
|
||||
'funcKey1' => 'funcValue1',
|
||||
|
@ -145,9 +147,9 @@ class FunctionsCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $projectId,
|
||||
'x-appwrite-key' => $apikey,
|
||||
], [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => ['role:all'],
|
||||
'execute' => [Role::any()->toString()],
|
||||
'runtime' => 'php-8.0',
|
||||
'vars' => [
|
||||
'funcKey1' => 'funcValue1',
|
||||
|
@ -236,7 +238,7 @@ class FunctionsCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => [],
|
||||
'runtime' => 'php-8.0',
|
||||
|
@ -330,9 +332,9 @@ class FunctionsCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $projectId,
|
||||
'x-appwrite-key' => $apikey,
|
||||
], [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => ['role:all'],
|
||||
'execute' => [Role::any()->toString()],
|
||||
'runtime' => 'php-8.0',
|
||||
'vars' => [
|
||||
'funcKey1' => 'funcValue1',
|
||||
|
|
|
@ -10,6 +10,7 @@ use Tests\E2E\Scopes\SideServer;
|
|||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
|
@ -26,7 +27,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'runtime' => 'php-8.0',
|
||||
'vars' => [
|
||||
|
@ -124,7 +125,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test 2',
|
||||
'runtime' => 'php-8.0',
|
||||
'vars' => [
|
||||
|
@ -712,7 +713,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test ' . $name,
|
||||
'runtime' => $name,
|
||||
'vars' => [],
|
||||
|
@ -797,7 +798,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test ' . $name,
|
||||
'runtime' => $name,
|
||||
'vars' => [],
|
||||
|
@ -907,7 +908,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test ' . $name,
|
||||
'runtime' => $name,
|
||||
'vars' => [
|
||||
|
@ -1012,7 +1013,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test ' . $name,
|
||||
'runtime' => $name,
|
||||
'vars' => [
|
||||
|
@ -1117,7 +1118,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test ' . $name,
|
||||
'runtime' => $name,
|
||||
'vars' => [
|
||||
|
@ -1222,7 +1223,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test ' . $name,
|
||||
'runtime' => $name,
|
||||
'vars' => [
|
||||
|
@ -1327,7 +1328,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
// ], $this->getHeaders()), [
|
||||
// 'functionId' => 'unique()',
|
||||
// 'functionId' => ID::unique(),
|
||||
// 'name' => 'Test '.$name,
|
||||
// 'runtime' => $name,
|
||||
// 'vars' => [
|
||||
|
|
|
@ -10,6 +10,7 @@ use Tests\E2E\Services\Projects\ProjectsBase;
|
|||
use Tests\E2E\Client;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
class ProjectsConsoleClientTest extends Scope
|
||||
{
|
||||
|
@ -26,7 +27,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
]);
|
||||
|
||||
|
@ -38,7 +39,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => 'unique()',
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
'teamId' => $team['body']['$id'],
|
||||
]);
|
||||
|
@ -60,7 +61,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => 'unique()',
|
||||
'projectId' => ID::unique(),
|
||||
'name' => '',
|
||||
'teamId' => $team['body']['$id'],
|
||||
]);
|
||||
|
@ -71,7 +72,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => 'unique()',
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
]);
|
||||
|
||||
|
@ -137,7 +138,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Project Test 2',
|
||||
]);
|
||||
|
||||
|
@ -149,7 +150,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => 'unique()',
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Project Test 2',
|
||||
'teamId' => $team['body']['$id'],
|
||||
]);
|
||||
|
@ -316,7 +317,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => 'unique()',
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Project Test 2',
|
||||
]);
|
||||
|
||||
|
@ -337,7 +338,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => 'unique()',
|
||||
'projectId' => ID::unique(),
|
||||
'name' => '',
|
||||
]);
|
||||
|
||||
|
@ -395,7 +396,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'provider' => 'unknown',
|
||||
'appId' => 'AppId',
|
||||
'appId' => ID::custom('AppId'),
|
||||
'secret' => 'Secret',
|
||||
]);
|
||||
|
||||
|
@ -421,7 +422,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $id,
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $originalEmail,
|
||||
'password' => $originalPassword,
|
||||
'name' => $originalName,
|
||||
|
@ -474,7 +475,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $id,
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
@ -487,7 +488,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'x-appwrite-project' => $id,
|
||||
'cookie' => 'a_session_' . $id . '=' . $session,
|
||||
]), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Arsenal'
|
||||
]);
|
||||
|
||||
|
@ -581,7 +582,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $id,
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
@ -607,7 +608,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $id,
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
@ -625,7 +626,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
]), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
]);
|
||||
$this->assertEquals(201, $team['headers']['status-code']);
|
||||
|
@ -636,7 +637,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
]), [
|
||||
'projectId' => 'unique()',
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
'teamId' => $team['body']['$id'],
|
||||
]);
|
||||
|
@ -769,7 +770,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $id,
|
||||
]), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Arsenal'
|
||||
]);
|
||||
|
||||
|
@ -859,7 +860,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'x-appwrite-project' => $id,
|
||||
'x-appwrite-key' => $keySecret,
|
||||
]), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Arsenal'
|
||||
]);
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideConsole;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class RealtimeConsoleClientTest extends Scope
|
||||
{
|
||||
|
@ -145,7 +148,7 @@ class RealtimeConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Actors DB',
|
||||
]);
|
||||
|
||||
|
@ -157,11 +160,14 @@ class RealtimeConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'collectionId' => 'unique()',
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Actors',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'collection'
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$actorsId = $actors['body']['$id'];
|
||||
|
|
|
@ -8,6 +8,9 @@ use Tests\E2E\Scopes\Scope;
|
|||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
use WebSocket\ConnectionException;
|
||||
|
||||
class RealtimeCustomClientTest extends Scope
|
||||
|
@ -628,7 +631,7 @@ class RealtimeCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Actors DB',
|
||||
]);
|
||||
|
||||
|
@ -642,11 +645,15 @@ class RealtimeCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'unique()',
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Actors',
|
||||
'read' => [],
|
||||
'write' => [],
|
||||
'permission' => 'document'
|
||||
'permissions' => [
|
||||
Permission::read(Role::users()),
|
||||
Permission::create(Role::users()),
|
||||
Permission::update(Role::users()),
|
||||
Permission::delete(Role::users()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$actorsId = $actors['body']['$id'];
|
||||
|
@ -676,12 +683,15 @@ class RealtimeCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'name' => 'Chris Evans'
|
||||
],
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
@ -719,12 +729,15 @@ class RealtimeCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'name' => 'Chris Evans 2'
|
||||
],
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
@ -761,12 +774,15 @@ class RealtimeCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'name' => 'Bradley Cooper'
|
||||
],
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$client->receive();
|
||||
|
@ -838,7 +854,7 @@ class RealtimeCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Actors DB',
|
||||
]);
|
||||
|
||||
|
@ -852,11 +868,14 @@ class RealtimeCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'unique()',
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Actors',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'collection'
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
]
|
||||
]);
|
||||
|
||||
$actorsId = $actors['body']['$id'];
|
||||
|
@ -886,12 +905,11 @@ class RealtimeCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'name' => 'Chris Evans'
|
||||
],
|
||||
'read' => [],
|
||||
'write' => [],
|
||||
'permissions' => [],
|
||||
]);
|
||||
|
||||
$documentId = $document['body']['$id'];
|
||||
|
@ -932,8 +950,7 @@ class RealtimeCustomClientTest extends Scope
|
|||
'data' => [
|
||||
'name' => 'Chris Evans 2'
|
||||
],
|
||||
'read' => [],
|
||||
'write' => [],
|
||||
'permissions' => [],
|
||||
]);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
@ -970,12 +987,11 @@ class RealtimeCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'name' => 'Bradley Cooper'
|
||||
],
|
||||
'read' => [],
|
||||
'write' => [],
|
||||
'permissions' => [],
|
||||
]);
|
||||
|
||||
$documentId = $document['body']['$id'];
|
||||
|
@ -1045,11 +1061,14 @@ class RealtimeCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'bucketId' => 'unique()',
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Bucket 1',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'bucket'
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
]
|
||||
]);
|
||||
|
||||
$bucketId = $bucket1['body']['$id'];
|
||||
|
@ -1061,10 +1080,13 @@ class RealtimeCustomClientTest extends Scope
|
|||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$fileId = $file['body']['$id'];
|
||||
|
@ -1101,8 +1123,12 @@ class RealtimeCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
@ -1192,9 +1218,9 @@ class RealtimeCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => ['role:member'],
|
||||
'execute' => ['users'],
|
||||
'runtime' => 'php-8.0',
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
@ -1335,7 +1361,7 @@ class RealtimeCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Arsenal'
|
||||
]);
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@ namespace Tests\E2E\Services\Storage;
|
|||
use CURLFile;
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
trait StorageBase
|
||||
{
|
||||
|
@ -18,13 +21,17 @@ trait StorageBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'bucketId' => 'unique()',
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket',
|
||||
'permission' => 'file',
|
||||
'fileSecurity' => true,
|
||||
'maximumFileSize' => 2000000, //2MB
|
||||
'allowedFileExtensions' => ["jpg", "png"],
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(201, $bucket['headers']['status-code']);
|
||||
$this->assertNotEmpty($bucket['body']['$id']);
|
||||
|
@ -35,10 +42,13 @@ trait StorageBase
|
|||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(201, $file['headers']['status-code']);
|
||||
$this->assertNotEmpty($file['body']['$id']);
|
||||
|
@ -58,11 +68,15 @@ trait StorageBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'bucketId' => 'unique()',
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket 2',
|
||||
'permission' => 'file',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'fileSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(201, $bucket2['headers']['status-code']);
|
||||
$this->assertNotEmpty($bucket2['body']['$id']);
|
||||
|
@ -93,8 +107,11 @@ trait StorageBase
|
|||
$largeFile = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucket2['body']['$id'] . '/files', array_merge($headers, $this->getHeaders()), [
|
||||
'fileId' => $fileId,
|
||||
'file' => $curlFile,
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
$counter++;
|
||||
$id = $largeFile['body']['$id'];
|
||||
|
@ -131,8 +148,11 @@ trait StorageBase
|
|||
$res = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucket2['body']['$id'] . '/files', $this->getHeaders(), [
|
||||
'fileId' => $fileId,
|
||||
'file' => $curlFile,
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
@fclose($handle);
|
||||
|
||||
|
@ -147,10 +167,13 @@ trait StorageBase
|
|||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(404, $res['headers']['status-code']);
|
||||
|
||||
|
@ -162,10 +185,13 @@ trait StorageBase
|
|||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/disk-b/kitten-1.png'), 'image/png', 'kitten-1.png'),
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $res['headers']['status-code']);
|
||||
|
@ -179,17 +205,18 @@ trait StorageBase
|
|||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/disk-a/kitten-3.gif'), 'image/gif', 'kitten-3.gif'),
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $res['headers']['status-code']);
|
||||
$this->assertEquals('File extension not allowed', $res['body']['message']);
|
||||
|
||||
return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id']];
|
||||
|
||||
/**
|
||||
* Test for FAILURE create bucket with too high limit (bigger then _APP_STORAGE_LIMIT)
|
||||
*/
|
||||
|
@ -198,15 +225,22 @@ trait StorageBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'bucketId' => 'unique()',
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket 2',
|
||||
'permission' => 'file',
|
||||
'fileSecurity' => true,
|
||||
'maximumFileSize' => 200000000, //200MB
|
||||
'allowedFileExtensions' => ["jpg", "png"],
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $failedBucket['headers']['status-code']);
|
||||
|
||||
return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id']];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -258,16 +292,8 @@ trait StorageBase
|
|||
$this->assertEquals('logo.png', $file1['body']['name']);
|
||||
$this->assertEquals('image/png', $file1['body']['mimeType']);
|
||||
$this->assertEquals(47218, $file1['body']['sizeOriginal']);
|
||||
//$this->assertEquals(54944, $file1['body']['sizeActual']);
|
||||
//$this->assertEquals('gzip', $file1['body']['algorithm']);
|
||||
//$this->assertEquals('1', $file1['body']['fileOpenSSLVersion']);
|
||||
//$this->assertEquals('aes-128-gcm', $file1['body']['fileOpenSSLCipher']);
|
||||
//$this->assertNotEmpty($file1['body']['fileOpenSSLTag']);
|
||||
//$this->assertNotEmpty($file1['body']['fileOpenSSLIV']);
|
||||
$this->assertIsArray($file1['body']['$read']);
|
||||
$this->assertIsArray($file1['body']['$write']);
|
||||
$this->assertCount(1, $file1['body']['$read']);
|
||||
$this->assertCount(1, $file1['body']['$write']);
|
||||
$this->assertIsArray($file1['body']['$permissions']);
|
||||
$this->assertCount(3, $file1['body']['$permissions']);
|
||||
|
||||
$file2 = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $data['fileId'] . '/preview', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -454,10 +480,13 @@ trait StorageBase
|
|||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'testcache',
|
||||
'fileId' => ID::custom('testcache'),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(201, $file['headers']['status-code']);
|
||||
$this->assertNotEmpty($file['body']['$id']);
|
||||
|
@ -497,10 +526,13 @@ trait StorageBase
|
|||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'testcache',
|
||||
'fileId' => ID::custom('testcache'),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/disk-b/kitten-2.png'), 'image/png', 'logo.png'),
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(201, $file['headers']['status-code']);
|
||||
$this->assertNotEmpty($file['body']['$id']);
|
||||
|
@ -542,8 +574,11 @@ trait StorageBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'read' => ['user:' . $this->getUser()['$id']],
|
||||
'write' => ['user:' . $this->getUser()['$id']],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id'])),
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
@ -558,10 +593,8 @@ trait StorageBase
|
|||
//$this->assertEquals('aes-128-gcm', $file['body']['fileOpenSSLCipher']);
|
||||
//$this->assertNotEmpty($file['body']['fileOpenSSLTag']);
|
||||
//$this->assertNotEmpty($file['body']['fileOpenSSLIV']);
|
||||
$this->assertIsArray($file['body']['$read']);
|
||||
$this->assertIsArray($file['body']['$write']);
|
||||
$this->assertCount(1, $file['body']['$read']);
|
||||
$this->assertCount(1, $file['body']['$write']);
|
||||
$this->assertIsArray($file['body']['$permissions']);
|
||||
$this->assertCount(3, $file['body']['$permissions']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE unknown Bucket
|
||||
|
@ -571,8 +604,11 @@ trait StorageBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'read' => ['user:' . $this->getUser()['$id']],
|
||||
'write' => ['user:' . $this->getUser()['$id']],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id'])),
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(404, $file['headers']['status-code']);
|
||||
|
|
|
@ -6,6 +6,7 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideConsole;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
class StorageConsoleClientTest extends Scope
|
||||
{
|
||||
|
@ -51,7 +52,7 @@ class StorageConsoleClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'bucketId' => 'unique()',
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket',
|
||||
'permission' => 'file'
|
||||
]);
|
||||
|
|
|
@ -11,6 +11,9 @@ use Tests\E2E\Scopes\Scope;
|
|||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class StorageCustomClientTest extends Scope
|
||||
{
|
||||
|
@ -28,11 +31,14 @@ class StorageCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'bucketId' => 'unique()',
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket',
|
||||
'permission' => 'bucket',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:member'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::users()),
|
||||
Permission::update(Role::users()),
|
||||
Permission::delete(Role::users()),
|
||||
],
|
||||
]);
|
||||
|
||||
$bucketId = $bucket['body']['$id'];
|
||||
|
@ -43,7 +49,7 @@ class StorageCustomClientTest extends Scope
|
|||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
|
||||
]);
|
||||
|
||||
|
@ -90,7 +96,7 @@ class StorageCustomClientTest extends Scope
|
|||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'fileId' => 'unique()',
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
|
||||
]);
|
||||
|
||||
|
@ -118,11 +124,15 @@ class StorageCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'bucketId' => 'unique()',
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket',
|
||||
'permission' => 'file',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'fileSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(201, $bucket['headers']['status-code']);
|
||||
$this->assertNotEmpty($bucket['body']['$id']);
|
||||
|
@ -131,14 +141,15 @@ class StorageCustomClientTest extends Scope
|
|||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 201);
|
||||
$this->assertNotEmpty($file['body']['$id']);
|
||||
$this->assertContains('user:' . $this->getUser()['$id'], $file['body']['$read']);
|
||||
$this->assertContains('user:' . $this->getUser()['$id'], $file['body']['$write']);
|
||||
$this->assertContains(Permission::read(Role::user($this->getUser()['$id'])), $file['body']['$permissions']);
|
||||
$this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $file['body']['$permissions']);
|
||||
$this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $file['body']['$permissions']);
|
||||
$this->assertEquals(true, DateTime::isValid($file['body']['$createdAt']));
|
||||
$this->assertEquals('permissions.png', $file['body']['name']);
|
||||
$this->assertEquals('image/png', $file['body']['mimeType']);
|
||||
|
@ -159,49 +170,57 @@ class StorageCustomClientTest extends Scope
|
|||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
|
||||
'folderId' => 'xyz',
|
||||
'read' => ['user:notme']
|
||||
'folderId' => ID::custom('xyz'),
|
||||
'permissions' => [
|
||||
Permission::read(Role::user(ID::custom('notme'))),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $file['headers']['status-code']);
|
||||
$this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:all', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:member', $file['body']['message']);
|
||||
$this->assertEquals(401, $file['headers']['status-code']);
|
||||
$this->assertStringStartsWith('Permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('any', $file['body']['message']);
|
||||
$this->assertStringContainsString('users', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:' . $this->getUser()['$id'], $file['body']['message']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
|
||||
'folderId' => 'xyz',
|
||||
'write' => ['user:notme']
|
||||
'folderId' => ID::custom('xyz'),
|
||||
'permissions' => [
|
||||
Permission::update(Role::user(ID::custom('notme'))),
|
||||
Permission::delete(Role::user(ID::custom('notme'))),
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 400);
|
||||
$this->assertStringStartsWith('Write permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:all', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:member', $file['body']['message']);
|
||||
$this->assertEquals(401, $file['headers']['status-code']);
|
||||
$this->assertStringStartsWith('Permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('any', $file['body']['message']);
|
||||
$this->assertStringContainsString('users', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:' . $this->getUser()['$id'], $file['body']['message']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
|
||||
'folderId' => 'xyz',
|
||||
'read' => ['user:notme'],
|
||||
'write' => ['user:notme']
|
||||
'folderId' => ID::custom('xyz'),
|
||||
'permissions' => [
|
||||
Permission::read(Role::user(ID::custom('notme'))),
|
||||
Permission::update(Role::user(ID::custom('notme'))),
|
||||
Permission::delete(Role::user(ID::custom('notme'))),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 400);
|
||||
$this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:all', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:member', $file['body']['message']);
|
||||
$this->assertEquals(401, $file['headers']['status-code']);
|
||||
$this->assertStringStartsWith('Permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('any', $file['body']['message']);
|
||||
$this->assertStringContainsString('users', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:' . $this->getUser()['$id'], $file['body']['message']);
|
||||
}
|
||||
|
||||
|
@ -217,40 +236,49 @@ class StorageCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'read' => ['user:notme']
|
||||
'permissions' => [
|
||||
Permission::read(Role::user(ID::custom('notme'))),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 400);
|
||||
$this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:all', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:member', $file['body']['message']);
|
||||
$this->assertEquals(401, $file['headers']['status-code']);
|
||||
$this->assertStringStartsWith('Permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('any', $file['body']['message']);
|
||||
$this->assertStringContainsString('users', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:' . $this->getUser()['$id'], $file['body']['message']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'write' => ['user:notme']
|
||||
'permissions' => [
|
||||
Permission::update(Role::user(ID::custom('notme'))),
|
||||
Permission::delete(Role::user(ID::custom('notme'))),
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 400);
|
||||
$this->assertStringStartsWith('Write permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:all', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:member', $file['body']['message']);
|
||||
$this->assertEquals(401, $file['headers']['status-code']);
|
||||
$this->assertStringStartsWith('Permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('any', $file['body']['message']);
|
||||
$this->assertStringContainsString('users', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:' . $this->getUser()['$id'], $file['body']['message']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'read' => ['user:notme'],
|
||||
'write' => ['user:notme']
|
||||
'permissions' => [
|
||||
Permission::read(Role::user(ID::custom('notme'))),
|
||||
Permission::create(Role::user(ID::custom('notme'))),
|
||||
Permission::update(Role::user(ID::custom('notme'))),
|
||||
Permission::delete(Role::user(ID::custom('notme'))),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 400);
|
||||
$this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:all', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:member', $file['body']['message']);
|
||||
$this->assertEquals(401, $file['headers']['status-code']);
|
||||
$this->assertStringStartsWith('Permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('any', $file['body']['message']);
|
||||
$this->assertStringContainsString('users', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:' . $this->getUser()['$id'], $file['body']['message']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use Tests\E2E\Scopes\ProjectCustom;
|
|||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
class StorageCustomServerTest extends Scope
|
||||
{
|
||||
|
@ -23,15 +24,14 @@ class StorageCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'bucketId' => 'unique()',
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket',
|
||||
'permission' => 'file',
|
||||
'fileSecurity' => true,
|
||||
]);
|
||||
$this->assertEquals(201, $bucket['headers']['status-code']);
|
||||
$this->assertNotEmpty($bucket['body']['$id']);
|
||||
$this->assertEquals(true, DateTime::isValid($bucket['body']['$createdAt']));
|
||||
$this->assertIsArray($bucket['body']['$read']);
|
||||
$this->assertIsArray($bucket['body']['$write']);
|
||||
$this->assertIsArray($bucket['body']['$permissions']);
|
||||
$this->assertIsArray($bucket['body']['allowedFileExtensions']);
|
||||
$this->assertEquals('Test Bucket', $bucket['body']['name']);
|
||||
$this->assertEquals(true, $bucket['body']['enabled']);
|
||||
|
@ -46,9 +46,9 @@ class StorageCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'bucketId' => 'bucket1',
|
||||
'bucketId' => ID::custom('bucket1'),
|
||||
'name' => 'Test Bucket',
|
||||
'permission' => 'file',
|
||||
'fileSecurity' => true,
|
||||
]);
|
||||
$this->assertEquals(201, $bucket['headers']['status-code']);
|
||||
$this->assertEquals('bucket1', $bucket['body']['$id']);
|
||||
|
@ -60,9 +60,9 @@ class StorageCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'bucketId' => 'unique()',
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => '',
|
||||
'permission' => 'file',
|
||||
'fileSecurity' => true,
|
||||
]);
|
||||
$this->assertEquals(400, $bucket['headers']['status-code']);
|
||||
|
||||
|
@ -181,16 +181,15 @@ class StorageCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'bucketId' => 'unique()',
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket Updated',
|
||||
'enabled' => false,
|
||||
'permission' => 'file',
|
||||
'fileSecurity' => true,
|
||||
]);
|
||||
$this->assertEquals(200, $bucket['headers']['status-code']);
|
||||
$this->assertNotEmpty($bucket['body']['$id']);
|
||||
$this->assertEquals(true, DateTime::isValid($bucket['body']['$createdAt']));
|
||||
$this->assertIsArray($bucket['body']['$read']);
|
||||
$this->assertIsArray($bucket['body']['$write']);
|
||||
$this->assertIsArray($bucket['body']['$permissions']);
|
||||
$this->assertIsArray($bucket['body']['allowedFileExtensions']);
|
||||
$this->assertEquals('Test Bucket Updated', $bucket['body']['name']);
|
||||
$this->assertEquals(false, $bucket['body']['enabled']);
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Tests\E2E\Services\Teams;
|
|||
use Tests\E2E\Client;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
trait TeamsBase
|
||||
{
|
||||
|
@ -17,7 +18,7 @@ trait TeamsBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Arsenal'
|
||||
]);
|
||||
|
||||
|
@ -31,7 +32,7 @@ trait TeamsBase
|
|||
$teamUid = $response1['body']['$id'];
|
||||
$teamName = $response1['body']['name'];
|
||||
|
||||
$teamId = \uniqid();
|
||||
$teamId = ID::unique();
|
||||
$response2 = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
@ -52,7 +53,7 @@ trait TeamsBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Newcastle'
|
||||
]);
|
||||
|
||||
|
@ -251,7 +252,7 @@ trait TeamsBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Demo'
|
||||
]);
|
||||
|
||||
|
@ -266,7 +267,7 @@ trait TeamsBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Demo New'
|
||||
]);
|
||||
|
||||
|
@ -300,7 +301,7 @@ trait TeamsBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Demo'
|
||||
]);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Tests\E2E\Services\Teams;
|
|||
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
trait TeamsBaseClient
|
||||
{
|
||||
|
@ -341,7 +342,7 @@ trait TeamsBaseClient
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'secret' => $secret,
|
||||
'userId' => 'sdasd',
|
||||
'userId' => ID::custom('sdasd'),
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
|
@ -352,7 +353,7 @@ trait TeamsBaseClient
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'secret' => $secret,
|
||||
'userId' => '',
|
||||
'userId' => ID::custom(''),
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
|
|
@ -6,6 +6,7 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectConsole;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
class TeamsConsoleClientTest extends Scope
|
||||
{
|
||||
|
@ -24,7 +25,7 @@ class TeamsConsoleClientTest extends Scope
|
|||
'x-appwrite-project' => 'console'
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Latest version Team',
|
||||
'teamId' => 'unique()'
|
||||
'teamId' => ID::unique()
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Tests\E2E\Services\Users;
|
|||
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
trait UsersBase
|
||||
{
|
||||
|
@ -16,7 +17,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => 'cristiano.ronaldo@manchester-united.co.uk',
|
||||
'password' => 'password',
|
||||
'name' => 'Cristiano Ronaldo',
|
||||
|
@ -42,7 +43,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'user1',
|
||||
'userId' => ID::custom('user1'),
|
||||
'email' => 'lionel.messi@psg.fr',
|
||||
'password' => 'password',
|
||||
'name' => 'Lionel Messi',
|
||||
|
|
|
@ -5,6 +5,9 @@ namespace Tests\E2E\Services\Webhooks;
|
|||
use CURLFile;
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
trait WebhooksBase
|
||||
{
|
||||
|
@ -26,7 +29,7 @@ trait WebhooksBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Actors DB',
|
||||
]);
|
||||
|
||||
|
@ -40,11 +43,15 @@ trait WebhooksBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'unique()',
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Actors',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document',
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$actorsId = $actors['body']['$id'];
|
||||
|
@ -68,10 +75,8 @@ trait WebhooksBase
|
|||
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true);
|
||||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertEquals($webhook['data']['name'], 'Actors');
|
||||
$this->assertIsArray($webhook['data']['$read']);
|
||||
$this->assertIsArray($webhook['data']['$write']);
|
||||
$this->assertCount(1, $webhook['data']['$read']);
|
||||
$this->assertCount(1, $webhook['data']['$write']);
|
||||
$this->assertIsArray($webhook['data']['$permissions']);
|
||||
$this->assertCount(4, $webhook['data']['$permissions']);
|
||||
|
||||
return array_merge(['actorsId' => $actorsId, 'databaseId' => $databaseId]);
|
||||
}
|
||||
|
@ -188,13 +193,16 @@ trait WebhooksBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'firstName' => 'Chris',
|
||||
'lastName' => 'Evans',
|
||||
],
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$documentId = $document['body']['$id'];
|
||||
|
@ -225,10 +233,8 @@ trait WebhooksBase
|
|||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertEquals($webhook['data']['firstName'], 'Chris');
|
||||
$this->assertEquals($webhook['data']['lastName'], 'Evans');
|
||||
$this->assertIsArray($webhook['data']['$read']);
|
||||
$this->assertIsArray($webhook['data']['$write']);
|
||||
$this->assertCount(1, $webhook['data']['$read']);
|
||||
$this->assertCount(1, $webhook['data']['$write']);
|
||||
$this->assertIsArray($webhook['data']['$permissions']);
|
||||
$this->assertCount(3, $webhook['data']['$permissions']);
|
||||
|
||||
$data['documentId'] = $document['body']['$id'];
|
||||
|
||||
|
@ -254,8 +260,11 @@ trait WebhooksBase
|
|||
'firstName' => 'Chris1',
|
||||
'lastName' => 'Evans2',
|
||||
],
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$documentId = $document['body']['$id'];
|
||||
|
@ -286,10 +295,8 @@ trait WebhooksBase
|
|||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertEquals($webhook['data']['firstName'], 'Chris1');
|
||||
$this->assertEquals($webhook['data']['lastName'], 'Evans2');
|
||||
$this->assertIsArray($webhook['data']['$read']);
|
||||
$this->assertIsArray($webhook['data']['$write']);
|
||||
$this->assertCount(1, $webhook['data']['$read']);
|
||||
$this->assertCount(1, $webhook['data']['$write']);
|
||||
$this->assertIsArray($webhook['data']['$permissions']);
|
||||
$this->assertCount(3, $webhook['data']['$permissions']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
@ -309,14 +316,17 @@ trait WebhooksBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'firstName' => 'Bradly',
|
||||
'lastName' => 'Cooper',
|
||||
|
||||
],
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$documentId = $document['body']['$id'];
|
||||
|
@ -354,10 +364,8 @@ trait WebhooksBase
|
|||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertEquals($webhook['data']['firstName'], 'Bradly');
|
||||
$this->assertEquals($webhook['data']['lastName'], 'Cooper');
|
||||
$this->assertIsArray($webhook['data']['$read']);
|
||||
$this->assertIsArray($webhook['data']['$write']);
|
||||
$this->assertCount(1, $webhook['data']['$read']);
|
||||
$this->assertCount(1, $webhook['data']['$write']);
|
||||
$this->assertIsArray($webhook['data']['$permissions']);
|
||||
$this->assertCount(3, $webhook['data']['$permissions']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
@ -373,11 +381,14 @@ trait WebhooksBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'bucketId' => 'unique()',
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket',
|
||||
'permission' => 'bucket',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all']
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$bucketId = $bucket['body']['$id'];
|
||||
|
@ -402,8 +413,7 @@ trait WebhooksBase
|
|||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertEquals('Test Bucket', $webhook['data']['name']);
|
||||
$this->assertEquals(true, $webhook['data']['enabled']);
|
||||
$this->assertIsArray($webhook['data']['$read']);
|
||||
$this->assertIsArray($webhook['data']['$write']);
|
||||
$this->assertIsArray($webhook['data']['$permissions']);
|
||||
|
||||
return array_merge(['bucketId' => $bucketId]);
|
||||
}
|
||||
|
@ -424,7 +434,7 @@ trait WebhooksBase
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'name' => 'Test Bucket Updated',
|
||||
'permission' => 'file',
|
||||
'fileSecurity' => true,
|
||||
'enabled' => false,
|
||||
]);
|
||||
|
||||
|
@ -448,8 +458,7 @@ trait WebhooksBase
|
|||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertEquals('Test Bucket Updated', $webhook['data']['name']);
|
||||
$this->assertEquals(false, $webhook['data']['enabled']);
|
||||
$this->assertIsArray($webhook['data']['$read']);
|
||||
$this->assertIsArray($webhook['data']['$write']);
|
||||
$this->assertIsArray($webhook['data']['$permissions']);
|
||||
|
||||
return array_merge(['bucketId' => $bucket['body']['$id']]);
|
||||
}
|
||||
|
@ -468,7 +477,7 @@ trait WebhooksBase
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'name' => 'Test Bucket Updated',
|
||||
'permission' => 'file',
|
||||
'fileSecurity' => true,
|
||||
'enabled' => true,
|
||||
]);
|
||||
|
||||
|
@ -480,11 +489,14 @@ trait WebhooksBase
|
|||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'folderId' => 'xyz',
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'folderId' => ID::custom('xyz'),
|
||||
]);
|
||||
|
||||
$fileId = $file['body']['$id'];
|
||||
|
@ -513,8 +525,7 @@ trait WebhooksBase
|
|||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
|
||||
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide()));
|
||||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertIsArray($webhook['data']['$read']);
|
||||
$this->assertIsArray($webhook['data']['$write']);
|
||||
$this->assertIsArray($webhook['data']['$permissions']);
|
||||
$this->assertEquals($webhook['data']['name'], 'logo.png');
|
||||
$this->assertEquals(true, DateTime::isValid($webhook['data']['$createdAt']));
|
||||
$this->assertNotEmpty($webhook['data']['signature']);
|
||||
|
@ -541,8 +552,12 @@ trait WebhooksBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 200);
|
||||
|
@ -569,8 +584,7 @@ trait WebhooksBase
|
|||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
|
||||
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide()));
|
||||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertIsArray($webhook['data']['$read']);
|
||||
$this->assertIsArray($webhook['data']['$write']);
|
||||
$this->assertIsArray($webhook['data']['$permissions']);
|
||||
$this->assertEquals($webhook['data']['name'], 'logo.png');
|
||||
$this->assertEquals(true, DateTime::isValid($webhook['data']['$createdAt']));
|
||||
$this->assertNotEmpty($webhook['data']['signature']);
|
||||
|
@ -620,8 +634,7 @@ trait WebhooksBase
|
|||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
|
||||
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide()));
|
||||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertIsArray($webhook['data']['$read']);
|
||||
$this->assertIsArray($webhook['data']['$write']);
|
||||
$this->assertIsArray($webhook['data']['$permissions']);
|
||||
$this->assertEquals($webhook['data']['name'], 'logo.png');
|
||||
$this->assertEquals(true, DateTime::isValid($webhook['data']['$createdAt']));
|
||||
$this->assertNotEmpty($webhook['data']['signature']);
|
||||
|
@ -666,8 +679,7 @@ trait WebhooksBase
|
|||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertEquals('Test Bucket Updated', $webhook['data']['name']);
|
||||
$this->assertEquals(true, $webhook['data']['enabled']);
|
||||
$this->assertIsArray($webhook['data']['$read']);
|
||||
$this->assertIsArray($webhook['data']['$write']);
|
||||
$this->assertIsArray($webhook['data']['$permissions']);
|
||||
}
|
||||
|
||||
public function testCreateTeam(): array
|
||||
|
@ -679,7 +691,7 @@ trait WebhooksBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Arsenal'
|
||||
]);
|
||||
|
||||
|
@ -768,7 +780,7 @@ trait WebhooksBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => 'unique()',
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Chelsea'
|
||||
]);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ use Tests\E2E\Scopes\Scope;
|
|||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
class WebhooksCustomClientTest extends Scope
|
||||
{
|
||||
|
@ -28,7 +29,7 @@ class WebhooksCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
@ -86,7 +87,7 @@ class WebhooksCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
|
|
@ -9,6 +9,9 @@ use Tests\E2E\Scopes\Scope;
|
|||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class WebhooksCustomServerTest extends Scope
|
||||
{
|
||||
|
@ -33,7 +36,7 @@ class WebhooksCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'name' => 'Actors1',
|
||||
'permission' => 'document',
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals($actors['headers']['status-code'], 200);
|
||||
|
@ -55,10 +58,8 @@ class WebhooksCustomServerTest extends Scope
|
|||
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true);
|
||||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertEquals($webhook['data']['name'], 'Actors1');
|
||||
$this->assertIsArray($webhook['data']['$read']);
|
||||
$this->assertIsArray($webhook['data']['$write']);
|
||||
$this->assertCount(1, $webhook['data']['$read']);
|
||||
$this->assertCount(1, $webhook['data']['$write']);
|
||||
$this->assertIsArray($webhook['data']['$permissions']);
|
||||
$this->assertCount(4, $webhook['data']['$permissions']);
|
||||
|
||||
return array_merge(['actorsId' => $actors['body']['$id']]);
|
||||
}
|
||||
|
@ -144,7 +145,7 @@ class WebhooksCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], $this->getHeaders()), [
|
||||
'databaseId' => 'unique()',
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Actors DB',
|
||||
]);
|
||||
|
||||
|
@ -158,11 +159,15 @@ class WebhooksCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'unique()',
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Demo',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document'
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$id = $actors['body']['$id'];
|
||||
|
@ -194,10 +199,8 @@ class WebhooksCustomServerTest extends Scope
|
|||
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true);
|
||||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertEquals($webhook['data']['name'], 'Demo');
|
||||
$this->assertIsArray($webhook['data']['$read']);
|
||||
$this->assertIsArray($webhook['data']['$write']);
|
||||
$this->assertCount(1, $webhook['data']['$read']);
|
||||
$this->assertCount(1, $webhook['data']['$write']);
|
||||
$this->assertIsArray($webhook['data']['$permissions']);
|
||||
$this->assertCount(4, $webhook['data']['$permissions']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
@ -215,7 +218,7 @@ class WebhooksCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'unique()',
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
|
@ -393,9 +396,9 @@ class WebhooksCustomServerTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => ['role:all'],
|
||||
'execute' => [Role::any()->toString()],
|
||||
'runtime' => 'php-8.0',
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
@ -444,7 +447,7 @@ class WebhooksCustomServerTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'name' => 'Test',
|
||||
'runtime' => 'php-8.0',
|
||||
'execute' => ['role:all'],
|
||||
'execute' => [Role::any()->toString()],
|
||||
'vars' => [
|
||||
'key1' => 'value1',
|
||||
]
|
||||
|
|
|
@ -9,9 +9,9 @@ class TestHook implements AfterTestHook
|
|||
public function executeAfterTest(string $test, float $time): void
|
||||
{
|
||||
printf(
|
||||
"%s ended in %s seconds\n",
|
||||
"%s ended in %s milliseconds\n",
|
||||
$test,
|
||||
$time
|
||||
$time * 1000
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ namespace Tests\Unit\Auth;
|
|||
use Appwrite\Auth\Auth;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\Database\Database;
|
||||
|
@ -17,7 +19,7 @@ class AuthTest extends TestCase
|
|||
public function tearDown(): void
|
||||
{
|
||||
Authorization::cleanRoles();
|
||||
Authorization::setRole('role:all');
|
||||
Authorization::setRole(Role::any()->toString());
|
||||
}
|
||||
|
||||
public function testCookieName(): void
|
||||
|
@ -205,15 +207,15 @@ class AuthTest extends TestCase
|
|||
$hash = Auth::hash($secret);
|
||||
$tokens1 = [
|
||||
new Document([
|
||||
'$id' => 'token1',
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), 60 * 60 * 24),
|
||||
'$id' => ID::custom('token1'),
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), 60 * 60 * 24)),
|
||||
'secret' => $hash,
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => 'test@example.com',
|
||||
]),
|
||||
new Document([
|
||||
'$id' => 'token2',
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), -60 * 60 * 24),
|
||||
'$id' => ID::custom('token2'),
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)),
|
||||
'secret' => 'secret2',
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => 'test@example.com',
|
||||
|
@ -222,15 +224,15 @@ class AuthTest extends TestCase
|
|||
|
||||
$tokens2 = [
|
||||
new Document([ // Correct secret and type time, wrong expire time
|
||||
'$id' => 'token1',
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), -60 * 60 * 24),
|
||||
'$id' => ID::custom('token1'),
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)),
|
||||
'secret' => $hash,
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => 'test@example.com',
|
||||
]),
|
||||
new Document([
|
||||
'$id' => 'token2',
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), -60 * 60 * 24),
|
||||
'$id' => ID::custom('token2'),
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)),
|
||||
'secret' => 'secret2',
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => 'test@example.com',
|
||||
|
@ -249,45 +251,45 @@ class AuthTest extends TestCase
|
|||
$hash = Auth::hash($secret);
|
||||
$tokens1 = [
|
||||
new Document([
|
||||
'$id' => 'token1',
|
||||
'$id' => ID::custom('token1'),
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), 60 * 60 * 24),
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), 60 * 60 * 24)),
|
||||
'secret' => $hash,
|
||||
]),
|
||||
new Document([
|
||||
'$id' => 'token2',
|
||||
'$id' => ID::custom('token2'),
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), -60 * 60 * 24),
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)),
|
||||
'secret' => 'secret2',
|
||||
]),
|
||||
];
|
||||
|
||||
$tokens2 = [
|
||||
new Document([ // Correct secret and type time, wrong expire time
|
||||
'$id' => 'token1',
|
||||
'$id' => ID::custom('token1'),
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), -60 * 60 * 24),
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)),
|
||||
'secret' => $hash,
|
||||
]),
|
||||
new Document([
|
||||
'$id' => 'token2',
|
||||
'$id' => ID::custom('token2'),
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), -60 * 60 * 24),
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)),
|
||||
'secret' => 'secret2',
|
||||
]),
|
||||
];
|
||||
|
||||
$tokens3 = [ // Correct secret and expire time, wrong type
|
||||
new Document([
|
||||
'$id' => 'token1',
|
||||
'$id' => ID::custom('token1'),
|
||||
'type' => Auth::TOKEN_TYPE_INVITE,
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), 60 * 60 * 24),
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), 60 * 60 * 24)),
|
||||
'secret' => $hash,
|
||||
]),
|
||||
new Document([
|
||||
'$id' => 'token2',
|
||||
'$id' => ID::custom('token2'),
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), -60 * 60 * 24),
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)),
|
||||
'secret' => 'secret2',
|
||||
]),
|
||||
];
|
||||
|
@ -303,35 +305,35 @@ class AuthTest extends TestCase
|
|||
public function testIsPrivilegedUser(): void
|
||||
{
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_GUEST]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_MEMBER]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_ADMIN]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_OWNER]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_APP]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_SYSTEM]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([Role::guests()->toString()]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([Role::users()->toString()]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_ADMIN]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_OWNER]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([Auth::USER_ROLE_APPS]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([Auth::USER_ROLE_SYSTEM]));
|
||||
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_APP, 'role:' . Auth::USER_ROLE_APP]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_APP, 'role:' . Auth::USER_ROLE_GUEST]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_OWNER, 'role:' . Auth::USER_ROLE_GUEST]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_OWNER, 'role:' . Auth::USER_ROLE_ADMIN, 'role:' . Auth::USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([Auth::USER_ROLE_APPS, Auth::USER_ROLE_APPS]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([Auth::USER_ROLE_APPS, Role::guests()->toString()]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_OWNER, Role::guests()->toString()]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_OWNER, Auth::USER_ROLE_ADMIN, Auth::USER_ROLE_DEVELOPER]));
|
||||
}
|
||||
|
||||
public function testIsAppUser(): void
|
||||
{
|
||||
$this->assertEquals(false, Auth::isAppUser([]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:' . Auth::USER_ROLE_GUEST]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:' . Auth::USER_ROLE_MEMBER]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:' . Auth::USER_ROLE_ADMIN]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:' . Auth::USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:' . Auth::USER_ROLE_OWNER]));
|
||||
$this->assertEquals(true, Auth::isAppUser(['role:' . Auth::USER_ROLE_APP]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:' . Auth::USER_ROLE_SYSTEM]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Role::guests()->toString()]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Role::users()->toString()]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_ADMIN]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_OWNER]));
|
||||
$this->assertEquals(true, Auth::isAppUser([Auth::USER_ROLE_APPS]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_SYSTEM]));
|
||||
|
||||
$this->assertEquals(true, Auth::isAppUser(['role:' . Auth::USER_ROLE_APP, 'role:' . Auth::USER_ROLE_APP]));
|
||||
$this->assertEquals(true, Auth::isAppUser(['role:' . Auth::USER_ROLE_APP, 'role:' . Auth::USER_ROLE_GUEST]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:' . Auth::USER_ROLE_OWNER, 'role:' . Auth::USER_ROLE_GUEST]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:' . Auth::USER_ROLE_OWNER, 'role:' . Auth::USER_ROLE_ADMIN, 'role:' . Auth::USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(true, Auth::isAppUser([Auth::USER_ROLE_APPS, Auth::USER_ROLE_APPS]));
|
||||
$this->assertEquals(true, Auth::isAppUser([Auth::USER_ROLE_APPS, Role::guests()->toString()]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_OWNER, Role::guests()->toString()]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_OWNER, Auth::USER_ROLE_ADMIN, Auth::USER_ROLE_DEVELOPER]));
|
||||
}
|
||||
|
||||
public function testGuestRoles(): void
|
||||
|
@ -342,23 +344,23 @@ class AuthTest extends TestCase
|
|||
|
||||
$roles = Auth::getRoles($user);
|
||||
$this->assertCount(1, $roles);
|
||||
$this->assertContains('role:guest', $roles);
|
||||
$this->assertContains(Role::guests()->toString(), $roles);
|
||||
}
|
||||
|
||||
public function testUserRoles(): void
|
||||
{
|
||||
$user = new Document([
|
||||
'$id' => '123',
|
||||
'$id' => ID::custom('123'),
|
||||
'memberships' => [
|
||||
[
|
||||
'teamId' => 'abc',
|
||||
'teamId' => ID::custom('abc'),
|
||||
'roles' => [
|
||||
'administrator',
|
||||
'moderator'
|
||||
]
|
||||
],
|
||||
[
|
||||
'teamId' => 'def',
|
||||
'teamId' => ID::custom('def'),
|
||||
'roles' => [
|
||||
'guest'
|
||||
]
|
||||
|
@ -369,7 +371,7 @@ class AuthTest extends TestCase
|
|||
$roles = Auth::getRoles($user);
|
||||
|
||||
$this->assertCount(7, $roles);
|
||||
$this->assertContains('role:member', $roles);
|
||||
$this->assertContains('users', $roles);
|
||||
$this->assertContains('user:123', $roles);
|
||||
$this->assertContains('team:abc', $roles);
|
||||
$this->assertContains('team:abc/administrator', $roles);
|
||||
|
@ -380,19 +382,19 @@ class AuthTest extends TestCase
|
|||
|
||||
public function testPrivilegedUserRoles(): void
|
||||
{
|
||||
Authorization::setRole('role:' . Auth::USER_ROLE_OWNER);
|
||||
Authorization::setRole(Auth::USER_ROLE_OWNER);
|
||||
$user = new Document([
|
||||
'$id' => '123',
|
||||
'$id' => ID::custom('123'),
|
||||
'memberships' => [
|
||||
[
|
||||
'teamId' => 'abc',
|
||||
'teamId' => ID::custom('abc'),
|
||||
'roles' => [
|
||||
'administrator',
|
||||
'moderator'
|
||||
]
|
||||
],
|
||||
[
|
||||
'teamId' => 'def',
|
||||
'teamId' => ID::custom('def'),
|
||||
'roles' => [
|
||||
'guest'
|
||||
]
|
||||
|
@ -403,7 +405,7 @@ class AuthTest extends TestCase
|
|||
$roles = Auth::getRoles($user);
|
||||
|
||||
$this->assertCount(5, $roles);
|
||||
$this->assertNotContains('role:member', $roles);
|
||||
$this->assertNotContains('users', $roles);
|
||||
$this->assertNotContains('user:123', $roles);
|
||||
$this->assertContains('team:abc', $roles);
|
||||
$this->assertContains('team:abc/administrator', $roles);
|
||||
|
@ -414,19 +416,19 @@ class AuthTest extends TestCase
|
|||
|
||||
public function testAppUserRoles(): void
|
||||
{
|
||||
Authorization::setRole('role:' . Auth::USER_ROLE_APP);
|
||||
Authorization::setRole(Auth::USER_ROLE_APPS);
|
||||
$user = new Document([
|
||||
'$id' => '123',
|
||||
'$id' => ID::custom('123'),
|
||||
'memberships' => [
|
||||
[
|
||||
'teamId' => 'abc',
|
||||
'teamId' => ID::custom('abc'),
|
||||
'roles' => [
|
||||
'administrator',
|
||||
'moderator'
|
||||
]
|
||||
],
|
||||
[
|
||||
'teamId' => 'def',
|
||||
'teamId' => ID::custom('def'),
|
||||
'roles' => [
|
||||
'guest'
|
||||
]
|
||||
|
@ -437,7 +439,7 @@ class AuthTest extends TestCase
|
|||
$roles = Auth::getRoles($user);
|
||||
|
||||
$this->assertCount(5, $roles);
|
||||
$this->assertNotContains('role:member', $roles);
|
||||
$this->assertNotContains('users', $roles);
|
||||
$this->assertNotContains('user:123', $roles);
|
||||
$this->assertContains('team:abc', $roles);
|
||||
$this->assertContains('team:abc/administrator', $roles);
|
||||
|
|
|
@ -6,6 +6,8 @@ use Appwrite\Auth\Auth;
|
|||
use Utopia\Database\Document;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class MessagingChannelsTest extends TestCase
|
||||
{
|
||||
|
@ -49,12 +51,14 @@ class MessagingChannelsTest extends TestCase
|
|||
for ($i = 0; $i < $this->connectionsPerChannel; $i++) {
|
||||
foreach ($this->allChannels as $index => $channel) {
|
||||
$user = new Document([
|
||||
'$id' => 'user' . $this->connectionsCount,
|
||||
'$id' => ID::custom('user' . $this->connectionsCount),
|
||||
'memberships' => [
|
||||
[
|
||||
'teamId' => 'team' . $i,
|
||||
'teamId' => ID::custom('team' . $i),
|
||||
'roles' => [
|
||||
empty($index % 2) ? 'admin' : 'member'
|
||||
empty($index % 2)
|
||||
? Auth::USER_ROLE_ADMIN
|
||||
: Role::users()->toString(),
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -118,8 +122,8 @@ class MessagingChannelsTest extends TestCase
|
|||
* - XXX users
|
||||
* - XXX teams
|
||||
* - XXX team roles (2 roles per team)
|
||||
* - 1 role:guest
|
||||
* - 1 role:member
|
||||
* - 1 guests
|
||||
* - 1 users
|
||||
*/
|
||||
$this->assertCount(($this->connectionsAuthenticated + (3 * $this->connectionsPerChannel) + 2), $this->realtime->subscriptions['1']);
|
||||
|
||||
|
@ -146,14 +150,14 @@ class MessagingChannelsTest extends TestCase
|
|||
}
|
||||
|
||||
/**
|
||||
* Tests Wildcard (role:all) Permissions on every channel.
|
||||
* Tests Wildcard ("any") Permissions on every channel.
|
||||
*/
|
||||
public function testWildcardPermission(): void
|
||||
{
|
||||
foreach ($this->allChannels as $index => $channel) {
|
||||
$event = [
|
||||
'project' => '1',
|
||||
'roles' => ['role:all'],
|
||||
'roles' => [Role::any()->toString()],
|
||||
'data' => [
|
||||
'channels' => [
|
||||
0 => $channel,
|
||||
|
@ -179,7 +183,10 @@ class MessagingChannelsTest extends TestCase
|
|||
|
||||
public function testRolePermissions(): void
|
||||
{
|
||||
$roles = ['role:guest', 'role:member'];
|
||||
$roles = [
|
||||
Role::guests()->toString(),
|
||||
Role::users()->toString()
|
||||
];
|
||||
foreach ($this->allChannels as $index => $channel) {
|
||||
foreach ($roles as $role) {
|
||||
$permissions = [$role];
|
||||
|
@ -216,7 +223,7 @@ class MessagingChannelsTest extends TestCase
|
|||
foreach ($this->allChannels as $index => $channel) {
|
||||
$permissions = [];
|
||||
for ($i = 0; $i < $this->connectionsPerChannel; $i++) {
|
||||
$permissions[] = 'user:user' . (!empty($i) ? $i : '') . $index;
|
||||
$permissions[] = Role::user(ID::custom('user' . (!empty($i) ? $i : '') . $index))->toString();
|
||||
}
|
||||
$event = [
|
||||
'project' => '1',
|
||||
|
@ -250,7 +257,7 @@ class MessagingChannelsTest extends TestCase
|
|||
$permissions = [];
|
||||
|
||||
for ($i = 0; $i < $this->connectionsPerChannel; $i++) {
|
||||
$permissions[] = 'team:team' . $i;
|
||||
$permissions[] = Role::team(ID::custom('team' . $i))->toString();
|
||||
}
|
||||
$event = [
|
||||
'project' => '1',
|
||||
|
@ -276,7 +283,14 @@ class MessagingChannelsTest extends TestCase
|
|||
$this->assertStringEndsWith($index, $receiver);
|
||||
}
|
||||
|
||||
$permissions = ['team:team' . $index . '/' . (empty($index % 2) ? 'admin' : 'member')];
|
||||
$permissions = [
|
||||
Role::team(
|
||||
ID::custom('team' . $index),
|
||||
(empty($index % 2)
|
||||
? Auth::USER_ROLE_ADMIN
|
||||
: Role::users()->toString())
|
||||
)->toString()
|
||||
];
|
||||
|
||||
$event = [
|
||||
'project' => '1',
|
||||
|
|
|
@ -4,6 +4,8 @@ namespace Tests\Unit\Messaging;
|
|||
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
class MessagingGuestTest extends TestCase
|
||||
{
|
||||
|
@ -14,13 +16,13 @@ class MessagingGuestTest extends TestCase
|
|||
$realtime->subscribe(
|
||||
'1',
|
||||
1,
|
||||
['role:guest'],
|
||||
[Role::guests()->toString()],
|
||||
['files' => 0, 'documents' => 0, 'documents.789' => 0, 'account.123' => 0]
|
||||
);
|
||||
|
||||
$event = [
|
||||
'project' => '1',
|
||||
'roles' => ['role:all'],
|
||||
'roles' => [Role::any()->toString()],
|
||||
'data' => [
|
||||
'channels' => [
|
||||
0 => 'documents',
|
||||
|
@ -34,68 +36,68 @@ class MessagingGuestTest extends TestCase
|
|||
$this->assertCount(1, $receivers);
|
||||
$this->assertEquals(1, $receivers[0]);
|
||||
|
||||
$event['roles'] = ['role:guest'];
|
||||
$event['roles'] = [Role::guests()->toString()];
|
||||
|
||||
$receivers = $realtime->getSubscribers($event);
|
||||
|
||||
$this->assertCount(1, $receivers);
|
||||
$this->assertEquals(1, $receivers[0]);
|
||||
|
||||
$event['roles'] = ['role:member'];
|
||||
$event['roles'] = [Role::users()->toString()];
|
||||
|
||||
$receivers = $realtime->getSubscribers($event);
|
||||
|
||||
$this->assertEmpty($receivers);
|
||||
|
||||
$event['roles'] = ['user:123'];
|
||||
$event['roles'] = [Role::user(ID::custom('123'))->toString()];
|
||||
|
||||
$receivers = $realtime->getSubscribers($event);
|
||||
|
||||
$this->assertEmpty($receivers);
|
||||
|
||||
$event['roles'] = ['team:abc'];
|
||||
$event['roles'] = [Role::team(ID::custom('abc'))->toString()];
|
||||
|
||||
$receivers = $realtime->getSubscribers($event);
|
||||
|
||||
$this->assertEmpty($receivers);
|
||||
|
||||
$event['roles'] = ['team:abc/administrator'];
|
||||
$event['roles'] = [Role::team(ID::custom('abc'), 'administrator')->toString()];
|
||||
|
||||
$receivers = $realtime->getSubscribers($event);
|
||||
|
||||
$this->assertEmpty($receivers);
|
||||
|
||||
$event['roles'] = ['team:abc/god'];
|
||||
$event['roles'] = [Role::team(ID::custom('abc'), 'god')->toString()];
|
||||
|
||||
$receivers = $realtime->getSubscribers($event);
|
||||
|
||||
$this->assertEmpty($receivers);
|
||||
|
||||
$event['roles'] = ['team:def'];
|
||||
$event['roles'] = [Role::team(ID::custom('def'))->toString()];
|
||||
|
||||
$receivers = $realtime->getSubscribers($event);
|
||||
|
||||
$this->assertEmpty($receivers);
|
||||
|
||||
$event['roles'] = ['team:def/guest'];
|
||||
$event['roles'] = [Role::team(ID::custom('def'), 'guest')->toString()];
|
||||
|
||||
$receivers = $realtime->getSubscribers($event);
|
||||
|
||||
$this->assertEmpty($receivers);
|
||||
|
||||
$event['roles'] = ['user:456'];
|
||||
$event['roles'] = [Role::user(ID::custom('456'))->toString()];
|
||||
|
||||
$receivers = $realtime->getSubscribers($event);
|
||||
|
||||
$this->assertEmpty($receivers);
|
||||
|
||||
$event['roles'] = ['team:def/member'];
|
||||
$event['roles'] = [Role::team(ID::custom('def'), 'member')->toString()];
|
||||
|
||||
$receivers = $realtime->getSubscribers($event);
|
||||
|
||||
$this->assertEmpty($receivers);
|
||||
|
||||
$event['roles'] = ['role:all'];
|
||||
$event['roles'] = [Role::any()->toString()];
|
||||
$event['data']['channels'] = ['documents.123'];
|
||||
|
||||
$receivers = $realtime->getSubscribers($event);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue