1
0
Fork 0
mirror of synced 2024-06-03 11:24:48 +12:00

Merge branch '0.14.x' of https://github.com/appwrite/appwrite into feat-runtime-versioning

This commit is contained in:
Torsten Dittmann 2022-05-16 15:08:36 +02:00
commit acaf60efaf
46 changed files with 395 additions and 305 deletions

View file

@ -1,6 +1,57 @@
# Unreleased Version
- Renamed `providers` to `authProviders` in project collection **Breaking Change**
# Version 0.14.0
## Features
- New Event Model **Breaking Change**
- The new Event Model allows you to define events for Webhooks or Functions more granular
- Account and Users events have been merged to just Users
- Examples:
- `database.documents.create` is now `collections.[COLLECTION_ID].documents.[DOCUMENT_ID].create`
- Both placeholders needs to be replaced with either `*` for wildcard or an ID of the respective collection or document
- So you can listen to every document that is created in the `posts` collection with `collections.posts.*.documents.*.create`
- `event` in the Realtime payload has been renamed to `events` and contains all possible events
- `X-Appwrite-Webhook-Event` Webhook header has been renamed to `X-Appwrite-Webhook-Events` and contains all possible events
- Renamed `providers` to `authProviders` in Projects **Breaking Change**
- Renamed `stdout` to `response` in Projects **Breaking Change**
- Removed deleted endpoint from the Accounts API
- Added new endpoint to update user's status from the Accounts API
- Deleted users will now free their ID and not reserve it anymore
- Added new endpoint to list all memberships on the Users API
- Increased Execution response to 1MB
- Added Wildcard support to Platforms
- Added Activity page to Teams console
- Added button to verify/unverify user's e-mail address in the console
- Added Docker log limits to `docker-compose.yaml`
- Renamed `_APP_EXECUTOR_RUNTIME_NETWORK` environment variable to `OPEN_RUNTIMES_NETWORK`
## Bugs
- Fixed issues with `min`, `max` and `default` values for float attributes
- Fixed account created with Magic URL to set a new password
- Fixed Database to respect `null` values
- Fixed missing realtime events from the Users API
- Fixed missing events when all sessions are deleted from the Users and Account API
- Fixed dots in database attributes
- Fixed renewal of SSL certificates
- Fixed errors in the certificates workers
- Fixed HTTPS redirect bug for non GET requests
- Fixed search when a User is updated
- Fixed aspect ratio bug in Avatars API
- Fixed wrong `Fail to Warmup ...` error message in Executor
- Fixed UI when file uploader is covered by jumpt to top button
- Fixed bug that allowed Queries on failed indexes
- Fixed UI when an alert with a lot text disappears too fast by increasing duration
- Fixed issues with cache and case-sensivity on ID's
- Fixed storage stats by upgrading to `BIGINT`
- Fixed `storage.total` stats which now is a sum of `storage.files.total` and `storage.deployments.total`
- Fixed Project logo preview
- Fixed UI for missing icons in Collection attributes
- Fixed UI to allow single-character custom ID's
- Fixed array size validation in the Database Service
- Fixed file preview when file extension is missing
- Fixed `Open an Issue` link in the console
- Fixed missing environment variables on Executor service
- Fixed all endpoints that expect an Array in their params to have not more than 100 items
## Security
- OAuth2 provider now check if the email is verified
# Version 0.13.4
## Features

View file

@ -1090,18 +1090,7 @@ $collections = [
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'deleted',
'type' => Database::VAR_BOOLEAN,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
]
],
'indexes' => [
[
@ -1117,14 +1106,7 @@ $collections = [
'attributes' => ['search'],
'lengths' => [],
'orders' => [],
],
[
'$id' => '_key_deleted_email',
'type' => Database::INDEX_KEY,
'attributes' => ['deleted', 'email'],
'lengths' => [0, 320],
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
],
]
],
],

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

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

View file

@ -81,9 +81,7 @@ App::post('/v1/account')
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0) {
$total = $dbForProject->count('users', [
new Query('deleted', Query::TYPE_EQUAL, [false]),
], APP_LIMIT_USERS);
$total = $dbForProject->count('users', max: APP_LIMIT_USERS);
if ($total >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
@ -108,8 +106,7 @@ App::post('/v1/account')
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
'deleted' => false
'search' => implode(' ', [$userId, $email, $name])
])));
} catch (Duplicate $th) {
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
@ -170,7 +167,6 @@ App::post('/v1/account/sessions')
$protocol = $request->getProtocol();
$profile = $dbForProject->findOne('users', [
new Query('deleted', Query::TYPE_EQUAL, [false]),
new Query('email', Query::TYPE_EQUAL, [$email])]
);
@ -475,21 +471,21 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
if ($user === false || $user->isEmpty()) { // No user logged in or with OAuth2 provider ID, create new one or connect with account with same email
$name = $oauth2->getUserName($accessToken);
$email = $oauth2->getUserEmail($accessToken);
/**
* Is verified is not used yet, since we don't know after an accout is created anymore if it was verified or not.
*/
$isVerified = $oauth2->isEmailVerified($accessToken);
if ($isVerified === true) {
// Get user by email address
$user = $dbForProject->findOne('users', [
new Query('deleted', Query::TYPE_EQUAL, [false]),
new Query('email', Query::TYPE_EQUAL, [$email])]
);
}
$user = $dbForProject->findOne('users', [
new Query('email', Query::TYPE_EQUAL, [$email])]
);
if ($user === false || $user->isEmpty()) { // Last option -> create the user, generate random password
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0) {
$total = $dbForProject->count('users', [new Query('deleted', Query::TYPE_EQUAL, [false])], APP_LIMIT_USERS);
$total = $dbForProject->count('users', max: APP_LIMIT_USERS);
if ($total >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
@ -503,7 +499,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'$read' => ['role:all'],
'$write' => ['user:' . $userId],
'email' => $email,
'emailVerification' => $isVerified,
'emailVerification' => true,
'status' => true, // Email should already be authenticated by OAuth2 provider
'password' => Auth::passwordHash(Auth::passwordGenerator()),
'passwordUpdate' => 0,
@ -514,8 +510,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
'deleted' => false
'search' => implode(' ', [$userId, $email, $name])
])));
} catch (Duplicate $th) {
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
@ -663,9 +658,7 @@ App::post('/v1/account/sessions/magic-url')
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0) {
$total = $dbForProject->count('users', [
new Query('deleted', Query::TYPE_EQUAL, [false]),
], APP_LIMIT_USERS);
$total = $dbForProject->count('users', max: APP_LIMIT_USERS);
if ($total >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
@ -689,8 +682,7 @@ App::post('/v1/account/sessions/magic-url')
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email]),
'deleted' => false
'search' => implode(' ', [$userId, $email])
])));
}
@ -790,7 +782,7 @@ App::put('/v1/account/sessions/magic-url')
$user = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId));
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -925,9 +917,7 @@ App::post('/v1/account/sessions/anonymous')
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0) {
$total = $dbForProject->count('users', [
new Query('deleted', Query::TYPE_EQUAL, [false]),
], APP_LIMIT_USERS);
$total = $dbForProject->count('users', max: APP_LIMIT_USERS);
if ($total >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
@ -951,8 +941,7 @@ App::post('/v1/account/sessions/anonymous')
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => $userId,
'deleted' => false
'search' => $userId
])));
// Create session token
@ -1469,17 +1458,18 @@ App::patch('/v1/account/prefs')
$response->dynamic($user, Response::MODEL_USER);
});
App::delete('/v1/account')
->desc('Delete Account')
App::patch('/v1/account/status')
->desc('Update Account Status')
->groups(['api', 'account'])
->label('event', 'users.[userId].delete')
->label('event', 'users.[userId].update.status')
->label('scope', 'account')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/account/delete.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->label('sdk.method', 'updateStatus')
->label('sdk.description', '/docs/references/account/update-status.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->inject('request')
->inject('response')
->inject('user')
@ -1496,28 +1486,15 @@ App::delete('/v1/account')
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */
$protocol = $request->getProtocol();
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', false));
// TODO Seems to be related to users.php/App::delete('/v1/users/:userId'). Can we share code between these two? Do todos below apply to users.php?
// TODO delete all tokens or only current session?
// TODO delete all user data according to GDPR. Make sure everything is backed up and backups are deleted later
/**
* Data to delete
* * Tokens
* * Memberships
*/
$audits
->setResource('user/' . $user->getId())
->setPayload($response->output($user, Response::MODEL_USER))
;
->setPayload($response->output($user, Response::MODEL_USER));
$events
->setParam('userId', $user->getId())
->setPayload($response->output($user, Response::MODEL_USER))
;
->setPayload($response->output($user, Response::MODEL_USER));
if (!Config::getParam('domainVerification')) {
$response->addHeader('X-Fallback-Cookies', \json_encode([]));
@ -1525,11 +1502,7 @@ App::delete('/v1/account')
$usage->setParam('users.delete', 1);
$response
->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
->noContent()
;
$response->dynamic($user, Response::MODEL_USER);
});
App::delete('/v1/account/sessions/:sessionId')
@ -1839,7 +1812,6 @@ App::post('/v1/account/recovery')
$email = \strtolower($email);
$profile = $dbForProject->findOne('users', [
new Query('deleted', Query::TYPE_EQUAL, [false]),
new Query('email', Query::TYPE_EQUAL, [$email])
]);
@ -1942,7 +1914,7 @@ App::put('/v1/account/recovery')
$profile = $dbForProject->getDocument('users', $userId);
if ($profile->isEmpty() || $profile->getAttribute('deleted')) {
if ($profile->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}

View file

@ -348,8 +348,7 @@ App::post('/v1/teams/:teamId/memberships')
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
'deleted' => false
'search' => implode(' ', [$userId, $email, $name])
])));
} catch (Duplicate $th) {
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);

View file

@ -68,8 +68,7 @@ App::post('/v1/users')
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
'deleted' => false
'search' => implode(' ', [$userId, $email, $name])
]));
} catch (Duplicate $th) {
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
@ -120,9 +119,7 @@ App::get('/v1/users')
}
}
$queries = [
new Query('deleted', Query::TYPE_EQUAL, [false])
];
$queries = [];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
@ -160,7 +157,7 @@ App::get('/v1/users/:userId')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -192,7 +189,7 @@ App::get('/v1/users/:userId/prefs')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -228,7 +225,7 @@ App::get('/v1/users/:userId/sessions')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -274,7 +271,7 @@ App::get('/v1/users/:userId/memberships')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -324,7 +321,7 @@ App::get('/v1/users/:userId/logs')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -409,7 +406,7 @@ App::patch('/v1/users/:userId/status')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -452,7 +449,7 @@ App::patch('/v1/users/:userId/verification')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -495,7 +492,7 @@ App::patch('/v1/users/:userId/name')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -543,7 +540,7 @@ App::patch('/v1/users/:userId/password')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -590,7 +587,7 @@ App::patch('/v1/users/:userId/email')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -650,7 +647,7 @@ App::patch('/v1/users/:userId/prefs')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -692,7 +689,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -743,7 +740,7 @@ App::delete('/v1/users/:userId/sessions')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
@ -795,28 +792,14 @@ App::delete('/v1/users/:userId')
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) {
if ($user->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
/**
* DO NOT DELETE THE USER RECORD ITSELF.
* WE RETAIN THE USER RECORD TO RESERVE THE USER ID AND ENSURE THAT THE USER ID IS NOT REUSED.
*/
// clone user object to send to workers
$clone = clone $user;
$user
->setAttribute("name", null)
->setAttribute("email", null)
->setAttribute("password", null)
->setAttribute("deleted", true)
->setAttribute("tokens", null)
->setAttribute("search", null)
;
$dbForProject->updateDocument('users', $userId, $user);
$dbForProject->deleteDocument('users', $userId);
$deletes
->setType(DELETE_TYPE_DOCUMENT)

View file

@ -31,7 +31,8 @@ class DeletesV1 extends Worker
*/
protected $consoleDB = null;
public function getName(): string {
public function getName(): string
{
return "deletes";
}
@ -202,11 +203,6 @@ class DeletesV1 extends Worker
*/
protected function deleteUser(Document $document, string $projectId): void
{
/**
* DO NOT DELETE THE USER RECORD ITSELF.
* WE RETAIN THE USER RECORD TO RESERVE THE USER ID AND ENSURE THAT THE USER ID IS NOT REUSED.
*/
$userId = $document->getId();
// Delete all sessions of this user from the sessions table and update the sessions field of the user record
@ -225,9 +221,14 @@ class DeletesV1 extends Worker
$teamId = $document->getAttribute('teamId');
$team = $this->getProjectDB($projectId)->getDocument('teams', $teamId);
if (!$team->isEmpty()) {
$team = $this->getProjectDB($projectId)->updateDocument('teams', $teamId, new Document(\array_merge($team->getArrayCopy(), [
'total' => \max($team->getAttribute('total', 0) - 1, 0), // Ensure that total >= 0
])));
$team = $this
->getProjectDB($projectId)
->updateDocument(
'teams',
$teamId,
// Ensure that total >= 0
$team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0))
);
}
}
});
@ -348,7 +349,7 @@ class DeletesV1 extends Worker
*/
Console::info("Deleting builds for function " . $functionId);
$storageBuilds = new Local(APP_STORAGE_BUILDS . '/app-' . $projectId);
foreach ($deploymentIds as $deploymentId) {
foreach ($deploymentIds as $deploymentId) {
$this->deleteByGroup('builds', [
new Query('deploymentId', Query::TYPE_EQUAL, [$deploymentId])
], $dbForProject, function (Document $document) use ($storageBuilds, $deploymentId) {
@ -362,7 +363,7 @@ class DeletesV1 extends Worker
/**
* Delete Executions
*/
*/
Console::info("Deleting executions for function " . $functionId);
$this->deleteByGroup('executions', [
new Query('functionId', Query::TYPE_EQUAL, [$functionId])
@ -380,7 +381,6 @@ class DeletesV1 extends Worker
Console::error($th->getMessage());
}
}
}
/**
@ -474,7 +474,7 @@ class DeletesV1 extends Worker
$chunk++;
/** @var string[] $projectIds */
$projectIds = array_map(fn(Document $project) => $project->getId(), $projects);
$projectIds = array_map(fn (Document $project) => $project->getId(), $projects);
$sum = count($projects);
@ -533,21 +533,21 @@ class DeletesV1 extends Worker
$consoleDB = $this->getConsoleDB();
// If domain has certificate generated
if(isset($document['certificateId'])) {
if (isset($document['certificateId'])) {
$domainUsingCertificate = $consoleDB->findOne('domains', [
new Query('certificateId', Query::TYPE_EQUAL, [$document['certificateId']])
]);
if(!$domainUsingCertificate) {
if (!$domainUsingCertificate) {
$mainDomain = App::getEnv('_APP_DOMAIN_TARGET', '');
if($mainDomain === $document->getAttribute('domain')) {
if ($mainDomain === $document->getAttribute('domain')) {
$domainUsingCertificate = $mainDomain;
}
}
// If certificate is still used by some domain, mark we can't delete.
// Current domain should not be found, because we only have copy. Original domain is already deleted from database.
if($domainUsingCertificate) {
if ($domainUsingCertificate) {
Console::warning("Skipping certificate deletion, because a domain is still using it.");
return;
}
@ -559,7 +559,7 @@ class DeletesV1 extends Worker
if ($domain && $checkTraversal && is_dir($directory)) {
// Delete certificate document, so Appwrite is aware of change
if(isset($document['certificateId'])) {
if (isset($document['certificateId'])) {
$consoleDB->deleteDocument('certificates', $document['certificateId']);
}
@ -577,8 +577,8 @@ class DeletesV1 extends Worker
$dbForProject = $this->getProjectDB($projectId);
$dbForProject->deleteCollection('bucket_' . $document->getInternalId());
$device = new Local(APP_STORAGE_UPLOADS.'/app-'.$projectId);
$device = new Local(APP_STORAGE_UPLOADS . '/app-' . $projectId);
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) {
case Storage::DEVICE_S3:
$s3AccessKey = App::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
@ -597,7 +597,7 @@ class DeletesV1 extends Worker
$device = new DOSpaces(APP_STORAGE_UPLOADS . '/app-' . $projectId, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
break;
}
$device->deletePath($document->getId());
}
}

View file

@ -45,7 +45,7 @@
"utopia-php/cache": "0.6.*",
"utopia-php/cli": "0.12.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.16.*",
"utopia-php/database": "0.17.*",
"utopia-php/locale": "0.4.*",
"utopia-php/registry": "0.5.*",
"utopia-php/preloader": "0.2.*",

14
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "9aecfa40a8ee2573bcbcac04edc46b88",
"content-hash": "cb8163f39b3bac09c2c547d9afb1064b",
"packages": [
{
"name": "adhocore/jwt",
@ -2133,16 +2133,16 @@
},
{
"name": "utopia-php/database",
"version": "0.16.1",
"version": "0.17.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "ab30b2a2ae27b83bd1756928e4ffa47fd8c9e602"
"reference": "a4d001452b78b85335ffbd34176cd45d2b13c1ca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/ab30b2a2ae27b83bd1756928e4ffa47fd8c9e602",
"reference": "ab30b2a2ae27b83bd1756928e4ffa47fd8c9e602",
"url": "https://api.github.com/repos/utopia-php/database/zipball/a4d001452b78b85335ffbd34176cd45d2b13c1ca",
"reference": "a4d001452b78b85335ffbd34176cd45d2b13c1ca",
"shasum": ""
},
"require": {
@ -2190,9 +2190,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.16.1"
"source": "https://github.com/utopia-php/database/tree/0.17.1"
},
"time": "2022-05-12T17:05:57+00:00"
"time": "2022-05-16T09:11:44+00:00"
},
{
"name": "utopia-php/domains",

View file

@ -18,7 +18,7 @@ public class MainActivity extends AppCompatActivity {
Account account = new Account(client);
account.delete(new Continuation<Object>() {
account.updateStatus(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {

View file

@ -17,7 +17,7 @@ class MainActivity : AppCompatActivity() {
val account = Account(client)
GlobalScope.launch {
val response = account.delete()
val response = account.updateStatus()
val json = response.body?.string()
}
}

View file

@ -5,7 +5,7 @@ func main() async throws {
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
let result = try await account.delete()
let user = try await account.updateStatus()
print(String(describing: result)
print(String(describing: user)
}

View file

@ -8,7 +8,7 @@ void main() { // Init SDK
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.delete();
Future result = account.updateStatus();
result
.then((response) {

View file

@ -0,0 +1,14 @@
const sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.updateStatus();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -1 +0,0 @@
appwrite account delete

View file

@ -0,0 +1 @@
appwrite account updateStatus

View file

@ -0,0 +1,14 @@
const sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.updateStatus();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -10,7 +10,7 @@ void main() { // Init SDK
.setJWT('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...') // Your secret JSON Web Token
;
Future result = account.delete();
Future result = account.updateStatus();
result
.then((response) {

View file

@ -12,7 +12,7 @@ client
;
let promise = account.delete();
let promise = account.updateStatus();
promise.then(function (response) {
console.log(response);

View file

@ -8,7 +8,7 @@ public void main() {
.setJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ..."); // Your secret JSON Web Token
Account account = new Account(client);
account.delete(new Continuation<Response>() {
account.updateStatus(new Continuation<Response>() {
@NotNull
@Override
public CoroutineContext getContext() {

View file

@ -8,6 +8,6 @@ suspend fun main() {
.setJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...") // Your secret JSON Web Token
val account = Account(client)
val response = account.delete()
val response = account.updateStatus()
val json = response.body?.string()
}

View file

@ -11,7 +11,7 @@ client
.setJWT('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...') // Your secret JSON Web Token
;
let promise = account.delete();
let promise = account.updateStatus();
promise.then(function (response) {
console.log(response);

View file

@ -13,4 +13,4 @@ $client
$account = new Account($client);
$result = $account->delete();
$result = $account->updateStatus();

View file

@ -11,4 +11,4 @@ client = Client()
account = Account(client)
result = account.delete()
result = account.update_status()

View file

@ -9,6 +9,6 @@ client
account = Appwrite::Account.new(client)
response = account.delete()
response = account.update_status()
puts response.inspect

View file

@ -6,7 +6,7 @@ func main() async throws {
.setProject("5df5acd0d48c2") // Your project ID
.setJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...") // Your secret JSON Web Token
let account = Account(client)
let result = try await account.delete()
let user = try await account.updateStatus()
print(String(describing: result)
print(String(describing: user)
}

View file

@ -1 +0,0 @@
Delete a currently logged in user account. Behind the scene, the user record is not deleted but permanently blocked from any access. This is done to avoid deleted accounts being overtaken by new users with the same email address. Any user-related resources like documents or storage files should be deleted separately.

View file

@ -0,0 +1 @@
Block the currently logged in user account. Behind the scene, the user record is not deleted but permanently blocked from any access. To completely delete a user, use the Users API instead.

View file

@ -1 +1 @@
Delete a user by its unique ID.
Delete a user by its unique ID, thereby releasing it's ID. Since ID is released and can be reused, all user-related resources like documents or storage files should be deleted before user deletion. If you want to keep ID reserved, use the [updateStatus](/docs/server/users#usersUpdateStatus) endpoint instead.

View file

@ -1 +1 @@
Update the user status by its unique ID.
Update the user status by its unique ID. Use this endpoint as an alternative to deleting a user if you want to keep user's ID reserved.

View file

@ -6,7 +6,7 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
stopOnFailure="true"
>
<extensions>
<extension class="Appwrite\Tests\TestHook" />

View file

@ -84,20 +84,10 @@ class V13 extends Migration
Console::log("- {$id}");
switch ($id) {
case 'executions':
try {
/**
* Rename stdout to response
*/
$this->projectDB->renameAttribute($id, 'stdout', 'response');
} catch (\Throwable $th) {
Console::warning("'stdout' from {$id}: {$th->getMessage()}");
}
break;
case 'projects':
try {
/**
* Rename providers to authProviders
* Rename providers to authProviders.
*/
$this->projectDB->renameAttribute($id, 'providers', 'authProviders');
} catch (\Throwable $th) {
@ -106,6 +96,9 @@ class V13 extends Migration
break;
case 'users':
try {
/**
* Recreate sessions for new subquery.
*/
$this->projectDB->deleteAttribute($id, 'sessions');
$this->projectDB->createAttribute(
collection: $id,
@ -120,6 +113,9 @@ class V13 extends Migration
Console::warning("'sessions' from {$id}: {$th->getMessage()}");
}
try {
/**
* Recreate tokens for new subquery.
*/
$this->projectDB->deleteAttribute($id, 'tokens');
$this->projectDB->createAttribute(
collection: $id,
@ -134,6 +130,9 @@ class V13 extends Migration
Console::warning("'tokens' from {$id}: {$th->getMessage()}");
}
try {
/**
* Recreate memberships for new subquery.
*/
$this->projectDB->deleteAttribute($id, 'memberships');
$this->projectDB->createAttribute(
collection: $id,
@ -158,32 +157,64 @@ class V13 extends Migration
Console::warning("'_key_user' from {$id}: {$th->getMessage()}");
}
break;
case 'executions':
// TODO: migrate stdout (size => 1000000)
// TODO: migrate stderr (size => 1000000)
break;
case 'executions':
try {
/**
* Rename stdout to response
*/
$this->projectDB->renameAttribute($id, 'stdout', 'response');
// TODO: migrate response (size => 1000000)
// TODO: migrate stderr (size => 1000000)
} catch (\Throwable $th) {
Console::warning("'stdout' from {$id}: {$th->getMessage()}");
}
break;
case 'stats':
//TODO: migrate value (size => 8)
break;
case 'tokens':
try {
$this->createCollection('tokens');
} catch (\Throwable $th) {
Console::warning("'tokens': {$th->getMessage()}");
}
break;
case 'builds':
try {
/**
* Increase stdout size.
*/
$this->projectDB->updateAttribute($id, 'stdout', size: 1_000_000);
} catch (\Throwable $th) {
Console::warning("'stdout' from {$id}: {$th->getMessage()}");
}
try {
/**
* Increase stderr size.
*/
$this->projectDB->updateAttribute($id, 'stderr', size: 1_000_000);
} catch (\Throwable $th) {
Console::warning("'stderr' from {$id}: {$th->getMessage()}");
}
break;
case 'executions':
try {
/**
* Rename stdout to response.
* Increase response size.
*/
$this->projectDB->renameAttribute($id, 'stdout', 'response');
$this->projectDB->updateAttribute($id, 'response', size: 1_000_000);
} catch (\Throwable $th) {
Console::warning("'stdout' from {$id}: {$th->getMessage()}");
}
try {
/**
* Increase stderr size.
*/
$this->projectDB->updateAttribute($id, 'stderr', size: 1_000_000);
} catch (\Throwable $th) {
Console::warning("'stderr' from {$id}: {$th->getMessage()}");
}
break;
case 'stats':
try {
/**
* Increase value size ot BIGINT.
*/
$this->projectDB->updateAttribute($id, 'value', size: 8);
} catch (\Throwable $th) {
Console::warning("'size' from {$id}: {$th->getMessage()}");
}
break;
case 'tokens':
try {
/**
* Create new Tokens collection.
*/
$this->createCollection('tokens');
} catch (\Throwable $th) {
Console::warning("'tokens': {$th->getMessage()}");
}
break;
}
usleep(100000);
}
@ -225,6 +256,15 @@ class V13 extends Migration
}
break;
case 'users':
/**
* Remove deleted users.
*/
if ($document->getAttribute('deleted', false) === true) {
$this->projectDB->deleteDocument('users', $document->getId());
}
break;
}
return $document;

View file

@ -17,7 +17,7 @@ class AccountCustomClientTest extends Scope
/**
* @depends testCreateAccountSession
*/
public function testCreateOAuth2AccountSession():array
public function testCreateOAuth2AccountSession(): array
{
$provider = 'mock';
$appId = '1';
@ -26,7 +26,7 @@ class AccountCustomClientTest extends Scope
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/'.$this->getProject()['$id'].'/oauth2', array_merge([
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/oauth2', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
@ -39,7 +39,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals($response['headers']['status-code'], 200);
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/'.$provider, array_merge([
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@ -54,9 +54,9 @@ class AccountCustomClientTest extends Scope
return [];
}
public function testBlockedAccount():array
public function testBlockedAccount(): array
{
$email = uniqid().'user@localhost.test';
$email = uniqid() . 'user@localhost.test';
$password = 'password';
$name = 'User Name (blocked)';
@ -77,7 +77,7 @@ class AccountCustomClientTest extends Scope
$id = $response['body']['$id'];
$this->assertEquals($response['headers']['status-code'], 201);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
@ -90,13 +90,13 @@ class AccountCustomClientTest extends Scope
$this->assertEquals($response['headers']['status-code'], 201);
$sessionId = $response['body']['$id'];
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$this->getProject()['$id']];
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 200);
@ -115,7 +115,7 @@ class AccountCustomClientTest extends Scope
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 401);
@ -134,9 +134,89 @@ class AccountCustomClientTest extends Scope
return [];
}
public function testCreateJWT():array
public function testSelfBlockedAccount(): array
{
$email = uniqid().'user@localhost.test';
$email = uniqid() . 'user55@localhost.test';
$password = 'password';
$name = 'User Name (self blocked)';
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'userId' => 'unique()',
'email' => $email,
'password' => $password,
'name' => $name,
]);
$id = $response['body']['$id'];
$this->assertEquals($response['headers']['status-code'], 201);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'email' => $email,
'password' => $password,
]);
$this->assertEquals($response['headers']['status-code'], 201);
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 200);
$response = $this->client->call(Client::METHOD_PATCH, '/account/status', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
], [
'status' => false,
]);
$this->assertEquals($response['headers']['status-code'], 200);
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 401);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'email' => $email,
'password' => $password,
]);
$this->assertEquals($response['headers']['status-code'], 401);
return [];
}
public function testCreateJWT(): array
{
$email = uniqid() . 'user@localhost.test';
$password = 'password';
$name = 'User Name (JWT)';
@ -157,7 +237,7 @@ class AccountCustomClientTest extends Scope
$id = $response['body']['$id'];
$this->assertEquals($response['headers']['status-code'], 201);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
@ -170,13 +250,13 @@ class AccountCustomClientTest extends Scope
$this->assertEquals($response['headers']['status-code'], 201);
$sessionId = $response['body']['$id'];
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$this->getProject()['$id']];
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 200);
@ -185,7 +265,7 @@ class AccountCustomClientTest extends Scope
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 201);
@ -212,11 +292,11 @@ class AccountCustomClientTest extends Scope
$this->assertEquals($response['headers']['status-code'], 200);
$response = $this->client->call(Client::METHOD_DELETE, '/account/sessions/'.$sessionId, array_merge([
$response = $this->client->call(Client::METHOD_DELETE, '/account/sessions/' . $sessionId, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 204);
@ -249,7 +329,7 @@ class AccountCustomClientTest extends Scope
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$this->getProject()['$id']];
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
/**
* Test for FAILURE
@ -258,7 +338,7 @@ class AccountCustomClientTest extends Scope
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]);
$this->assertEquals(401, $response['headers']['status-code']);
@ -278,7 +358,7 @@ class AccountCustomClientTest extends Scope
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'oldPassword' => '',
]);
@ -293,7 +373,7 @@ class AccountCustomClientTest extends Scope
*/
public function testUpdateAnonymousAccountEmail($session)
{
$email = uniqid().'new@localhost.test';
$email = uniqid() . 'new@localhost.test';
/**
* Test for FAILURE
@ -302,7 +382,7 @@ class AccountCustomClientTest extends Scope
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'email' => $email,
'password' => '',
@ -316,7 +396,7 @@ class AccountCustomClientTest extends Scope
public function testConvertAnonymousAccount()
{
$session = $this->testCreateAnonymousAccount();
$email = uniqid().'new@localhost.test';
$email = uniqid() . 'new@localhost.test';
$password = 'new-password';
/**
@ -336,7 +416,7 @@ class AccountCustomClientTest extends Scope
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'email' => $email,
'password' => $password,
@ -347,13 +427,13 @@ class AccountCustomClientTest extends Scope
/**
* Test for SUCCESS
*/
$email = uniqid().'new@localhost.test';
$email = uniqid() . 'new@localhost.test';
$response = $this->client->call(Client::METHOD_PATCH, '/account/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'email' => $email,
'password' => $password,
@ -394,14 +474,14 @@ class AccountCustomClientTest extends Scope
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 200);
$userId = $response['body']['$id'] ?? '';
$response = $this->client->call(Client::METHOD_PATCH, '/projects/'.$this->getProject()['$id'].'/oauth2', array_merge([
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/oauth2', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
@ -414,26 +494,26 @@ class AccountCustomClientTest extends Scope
$this->assertEquals($response['headers']['status-code'], 200);
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/'.$provider, array_merge([
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'success' => 'http://localhost/v1/mock/tests/general/oauth2/success',
'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure',
]);
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$this->getProject()['$id']];
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('success', $response['body']['result']);
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 200);
@ -449,7 +529,7 @@ class AccountCustomClientTest extends Scope
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals(200, $response['headers']['status-code']);
@ -458,14 +538,14 @@ class AccountCustomClientTest extends Scope
$this->assertGreaterThan(\time() + 14400 - 5, $response['body']['providerAccessTokenExpiry']); // 5 seconds allowed networking delay
$initialExpiry = $response['body']['providerAccessTokenExpiry'];
sleep(3);
$response = $this->client->call(Client::METHOD_PATCH, '/account/sessions/current', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals(200, $response['headers']['status-code']);
@ -476,14 +556,15 @@ class AccountCustomClientTest extends Scope
return [];
}
public function testGetSessionByID() {
public function testGetSessionByID()
{
$session = $this->testCreateAnonymousAccount();
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/current', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 200);
@ -491,11 +572,11 @@ class AccountCustomClientTest extends Scope
$sessionID = $response['body']['$id'];
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/'.$sessionID, array_merge([
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/' . $sessionID, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 200);
@ -505,7 +586,7 @@ class AccountCustomClientTest extends Scope
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 404);

View file

@ -12,49 +12,4 @@ class UsersCustomServerTest extends Scope
use UsersBase;
use ProjectCustom;
use SideServer;
public function testDeprecatedUsers():array
{
/**
* Test for FAILURE (don't allow recreating account with same custom ID)
*/
// Create user with custom ID 'meldiron'
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'userId' => 'meldiron',
'email' => 'matej@appwrite.io',
'password' => 'my-superstr0ng-password',
'name' => 'Matej Bačo'
]);
$this->assertEquals(201, $response['headers']['status-code']);
// Delete user with custom ID 'meldiron'
$response = $this->client->call(Client::METHOD_DELETE, '/users/meldiron', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(204, $response['headers']['status-code']);
// Try to create user with custom ID 'meldiron' again, but now it should fail
$response1 = $this->client->call(Client::METHOD_POST, '/users', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'userId' => 'meldiron',
'email' => 'matej2@appwrite.io',
'password' => 'someones-superstr0ng-password',
'name' => 'Matej Bačo Second'
]);
$this->assertEquals(409, $response1['headers']['status-code']);
$this->assertEquals('Account already exists', $response1['body']['message']);
return [];
}
}

View file

@ -101,15 +101,14 @@ class WebhooksCustomClientTest extends Scope
$id = $account['body']['$id'];
$session = $this->client->parseCookie((string)$accountSession['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
$account = $this->client->call(Client::METHOD_DELETE, '/account', array_merge([
$account = $this->client->call(Client::METHOD_PATCH, '/account/status', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($account['headers']['status-code'], 204);
$this->assertEmpty($account['body']);
$this->assertEquals($account['headers']['status-code'], 200);
$webhook = $this->getLastRequest();
@ -117,9 +116,9 @@ class WebhooksCustomClientTest extends Scope
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
$this->assertStringContainsString('users.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
$this->assertStringContainsString('users.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']);
$this->assertStringContainsString('users.*.update.status', $webhook['headers']['X-Appwrite-Webhook-Events']);
$this->assertStringContainsString("users.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
$this->assertStringContainsString("users.{$id}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']);
$this->assertStringContainsString("users.{$id}.update.status", $webhook['headers']['X-Appwrite-Webhook-Events']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], 'not-yet-implemented');
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);