1
0
Fork 0
mirror of synced 2024-06-03 03:14:50 +12:00

Merge branch '1.5.x' into feat-rc-sdks

This commit is contained in:
Torsten Dittmann 2024-02-29 22:14:02 +01:00 committed by GitHub
commit 50c2ac9cd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 243 additions and 112 deletions

View file

@ -178,7 +178,7 @@ return [
],
Exception::USER_SESSION_ALREADY_EXISTS => [
'name' => Exception::USER_SESSION_ALREADY_EXISTS,
'description' => 'Creation of anonymous users is prohibited when a session is active.',
'description' => 'Creation of a session is prohibited when a session is active.',
'code' => 401,
],
Exception::USER_NOT_FOUND => [

View file

@ -228,7 +228,6 @@ App::post('/v1/account/sessions/email')
->inject('queueForEvents')
->inject('hooks')
->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks) {
$email = \strtolower($email);
$protocol = $request->getProtocol();
@ -553,7 +552,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->inject('geodb')
->inject('queueForEvents')
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) {
$protocol = $request->getProtocol();
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
@ -675,6 +673,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
if (!empty($userWithMatchingEmail)) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
$sessionUpgrade = true;
}
$sessions = $user->getAttribute('sessions', []);
@ -704,7 +704,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
}
/**
* Is verified is not used yet, since we don't know after an accout is created anymore if it was verified or not.
* Is verified is not used yet, since we don't know after an account is created anymore if it was verified or not.
*/
$isVerified = $oauth2->isEmailVerified($accessToken);
@ -934,12 +934,12 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->setPayload($response->output($session, Response::MODEL_SESSION))
;
// TODO: Remove this deprecated, undocumented workaround
// TODO: Remove this deprecated workaround - support only token
if ($state['success']['path'] == $oauthDefaultSuccess) {
$query['project'] = $project->getId();
$query['domain'] = Config::getParam('cookieDomain');
$query['key'] = Auth::$cookieName;
$query['secret'] = $secret;
$query['secret'] = Auth::encodeSession($user->getId(), $secret);
}
$response
@ -947,6 +947,20 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'));
}
if (isset($sessionUpgrade) && $sessionUpgrade) {
foreach ($user->getAttribute('targets', []) as $target) {
if ($target->getAttribute('providerType') !== MESSAGE_TYPE_PUSH) {
continue;
}
$target
->setAttribute('sessionId', $session->getId())
->setAttrubte('sessionInternalId', $session->getInternalId());
$dbForProject->updateDocument('targets', $target->getId(), $target);
}
}
$dbForProject->purgeCachedDocument('users', $user->getId());
$state['success']['query'] = URLParser::unparseQuery($query);
@ -1636,7 +1650,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
App::put('/v1/account/sessions/magic-url')
->desc('Update magic URL session')
->label('event', 'users.[userId].sessions.[sessionId].create')
->groups(['api', 'account'])
->groups(['api', 'account', 'session'])
->label('scope', 'sessions.write')
->label('audits.event', 'session.create')
->label('audits.resource', 'user/{response.userId}')
@ -1666,7 +1680,7 @@ App::put('/v1/account/sessions/magic-url')
App::put('/v1/account/sessions/phone')
->desc('Update phone session')
->label('event', 'users.[userId].sessions.[sessionId].create')
->groups(['api', 'account'])
->groups(['api', 'account', 'session'])
->label('scope', 'sessions.write')
->label('audits.event', 'session.create')
->label('audits.resource', 'user/{response.userId}')
@ -1696,7 +1710,7 @@ App::put('/v1/account/sessions/phone')
App::post('/v1/account/sessions/token')
->desc('Create session')
->label('event', 'users.[userId].sessions.[sessionId].create')
->groups(['api', 'account'])
->groups(['api', 'account', 'session'])
->label('scope', 'sessions.write')
->label('audits.event', 'session.create')
->label('audits.resource', 'user/{response.userId}')
@ -1919,7 +1933,6 @@ App::post('/v1/account/sessions/anonymous')
->inject('geodb')
->inject('queueForEvents')
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) {
$protocol = $request->getProtocol();
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
@ -1929,10 +1942,6 @@ App::post('/v1/account/sessions/anonymous')
throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED, 'Failed to create anonymous user');
}
if (!$user->isEmpty()) {
throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS, 'Cannot create an anonymous user when logged in');
}
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0) {

View file

@ -461,6 +461,20 @@ App::init()
}
});
App::init()
->groups(['session'])
->inject('user')
->inject('request')
->action(function (Document $user, Request $request) {
if (\str_contains($request->getURI(), 'oauth2')) {
return;
}
if (!$user->isEmpty()) {
throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS);
}
});
/**
* Limit user session
*
@ -497,6 +511,7 @@ App::shutdown()
$session = array_shift($sessions);
$dbForProject->deleteDocument('sessions', $session->getId());
}
$dbForProject->purgeCachedDocument('users', $userId);
});

View file

@ -676,7 +676,7 @@ services:
- _APP_DB_PASS
appwrite-assistant:
image: appwrite/assistant:0.3.0
image: appwrite/assistant:0.4.0
container_name: appwrite-assistant
<<: *x-logging
restart: unless-stopped

View file

@ -782,7 +782,7 @@ services:
appwrite-assistant:
container_name: appwrite-assistant
image: appwrite/assistant:0.3.0
image: appwrite/assistant:0.4.0
networks:
- appwrite
environment:

View file

@ -252,6 +252,14 @@ class V20 extends Migration
Console::warning("'totpBackup' from {$id}: {$th->getMessage()}");
}
// Create challenges attribute
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'challenges');
$this->projectDB->purgeCachedCollection($id);
} catch (Throwable $th) {
Console::warning("'challenges' from {$id}: {$th->getMessage()}");
}
break;
case 'projects':
// Rename providers authProviders to oAuthProviders
@ -543,9 +551,11 @@ class V20 extends Migration
$document->setAttribute('expire', $expire);
$factors = match ($document->getAttribute('provider')) {
Auth::SESSION_PROVIDER_ANONYMOUS => ['anonymous'],
Auth::SESSION_PROVIDER_EMAIL => ['password'],
Auth::SESSION_PROVIDER_PHONE => ['phone'],
default => ['password'],
Auth::SESSION_PROVIDER_ANONYMOUS => ['anonymous'],
Auth::SESSION_PROVIDER_TOKEN => ['token'],
default => ['email'],
};
$document->setAttribute('factors', $factors);

View file

@ -86,7 +86,7 @@ class Messaging extends Action
$payload = $message->getPayload() ?? [];
if (empty($payload)) {
throw new Exception('Missing payload');
throw new \Exception('Missing payload');
}
$type = $payload['type'] ?? '';
@ -105,7 +105,7 @@ class Messaging extends Action
$this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $deviceForLocalFiles,);
break;
default:
throw new Exception('Unknown message type: ' . $type);
throw new \Exception('Unknown message type: ' . $type);
}
}
@ -118,11 +118,12 @@ class Messaging extends Action
$topicIds = $message->getAttribute('topics', []);
$targetIds = $message->getAttribute('targets', []);
$userIds = $message->getAttribute('users', []);
$providerType = $message->getAttribute('providerType');
/**
* @var array<Document> $recipients
* @var array<Document> $allTargets
*/
$recipients = [];
$allTargets = [];
if (\count($topicIds) > 0) {
$topics = $dbForProject->find('topics', [
@ -130,9 +131,11 @@ class Messaging extends Action
Query::limit(\count($topicIds)),
]);
foreach ($topics as $topic) {
$targets = \array_filter($topic->getAttribute('targets'), fn(Document $target) =>
$target->getAttribute('providerType') === $message->getAttribute('providerType'));
$recipients = \array_merge($recipients, $targets);
$targets = \array_filter($topic->getAttribute('targets'), function (Document $target) use ($providerType) {
return $target->getAttribute('providerType') === $providerType;
});
\array_push($allTargets, ...$targets);
}
}
@ -142,23 +145,25 @@ class Messaging extends Action
Query::limit(\count($userIds)),
]);
foreach ($users as $user) {
$targets = \array_filter($user->getAttribute('targets'), fn(Document $target) =>
$target->getAttribute('providerType') === $message->getAttribute('providerType'));
$recipients = \array_merge($recipients, $targets);
$targets = \array_filter($user->getAttribute('targets'), function (Document $target) use ($providerType) {
return $target->getAttribute('providerType') === $providerType;
});
\array_push($allTargets, ...$targets);
}
}
if (\count($targetIds) > 0) {
$targets = $dbForProject->find('targets', [
Query::equal('$id', $targetIds),
Query::equal('providerType', [$providerType]),
Query::limit(\count($targetIds)),
]);
$targets = \array_filter($targets, fn(Document $target) =>
$target->getAttribute('providerType') === $message->getAttribute('providerType'));
$recipients = \array_merge($recipients, $targets);
\array_push($allTargets, ...$targets);
}
if (empty($recipients)) {
if (empty($allTargets)) {
$dbForProject->updateDocument('messages', $message->getId(), $message->setAttributes([
'status' => MessageStatus::FAILED,
'deliveryErrors' => ['No valid recipients found.']
@ -168,85 +173,82 @@ class Messaging extends Action
return;
}
$fallback = $dbForProject->findOne('providers', [
$default = $dbForProject->findOne('providers', [
Query::equal('enabled', [true]),
Query::equal('type', [$recipients[0]->getAttribute('providerType')]),
Query::equal('type', [$providerType]),
]);
if ($fallback === false || $fallback->isEmpty()) {
if ($default === false || $default->isEmpty()) {
$dbForProject->updateDocument('messages', $message->getId(), $message->setAttributes([
'status' => MessageStatus::FAILED,
'deliveryErrors' => ['No fallback provider found.']
'deliveryErrors' => ['No enabled provider found.']
]));
Console::warning('No fallback provider found.');
Console::warning('No enabled provider found.');
return;
}
/**
* @var array<string, array<string>> $identifiers
* @var array<string, array<string, null>> $identifiers
*/
$identifiers = [];
/**
* @var Document[] $providers
* @var array<Document> $providers
*/
$providers = [
$fallback->getId() => $fallback
$default->getId() => $default
];
foreach ($recipients as $recipient) {
$providerId = $recipient->getAttribute('providerId');
foreach ($allTargets as $target) {
$providerId = $target->getAttribute('providerId');
if (
!$providerId
&& $fallback instanceof Document
&& !$fallback->isEmpty()
&& $fallback->getAttribute('enabled')
) {
$providerId = $fallback->getId();
if (!$providerId) {
$providerId = $default->getId();
}
if ($providerId) {
if (!\array_key_exists($providerId, $identifiers)) {
$identifiers[$providerId] = [];
}
$identifiers[$providerId][] = $recipient->getAttribute('identifier');
// Use null as value to avoid duplicate keys
$identifiers[$providerId][$target->getAttribute('identifier')] = null;
}
}
/**
* @var array<array> $results
*/
$results = batch(\array_map(function ($providerId) use ($identifiers, $providers, $fallback, $message, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
return function () use ($providerId, $identifiers, $providers, $fallback, $message, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
$results = batch(\array_map(function ($providerId) use ($identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
return function () use ($providerId, $identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
if (\array_key_exists($providerId, $providers)) {
$provider = $providers[$providerId];
} else {
$provider = $dbForProject->getDocument('providers', $providerId);
if ($provider->isEmpty() || !$provider->getAttribute('enabled')) {
$provider = $fallback;
$provider = $default;
} else {
$providers[$providerId] = $provider;
}
}
$identifiers = $identifiers[$providerId];
$identifiersForProvider = $identifiers[$providerId];
$adapter = match ($provider->getAttribute('type')) {
MESSAGE_TYPE_SMS => $this->getSmsAdapter($provider),
MESSAGE_TYPE_PUSH => $this->getPushAdapter($provider),
MESSAGE_TYPE_EMAIL => $this->getEmailAdapter($provider),
default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE)
default => throw new \Exception('Provider with the requested ID is of the incorrect type')
};
$maxBatchSize = $adapter->getMaxMessagesPerRequest();
$batches = \array_chunk($identifiers, $maxBatchSize);
$batchIndex = 0;
$batches = \array_chunk(
\array_keys($identifiersForProvider),
$adapter->getMaxMessagesPerRequest()
);
return batch(\array_map(function ($batch) use ($message, $provider, $adapter, &$batchIndex, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
return function () use ($batch, $message, $provider, $adapter, &$batchIndex, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
return batch(\array_map(function ($batch) use ($message, $provider, $adapter, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
return function () use ($batch, $message, $provider, $adapter, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
$deliveredTotal = 0;
$deliveryErrors = [];
$messageData = clone $message;
@ -256,7 +258,7 @@ class Messaging extends Action
MESSAGE_TYPE_SMS => $this->buildSmsMessage($messageData, $provider),
MESSAGE_TYPE_PUSH => $this->buildPushMessage($messageData),
MESSAGE_TYPE_EMAIL => $this->buildEmailMessage($dbForProject, $messageData, $provider, $deviceForFiles, $deviceForLocalFiles),
default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE)
default => throw new \Exception('Provider with the requested ID is of the incorrect type')
};
try {
@ -283,10 +285,8 @@ class Messaging extends Action
}
}
} catch (\Throwable $e) {
$deliveryErrors[] = 'Failed sending to targets ' . $batchIndex + 1 . ' of ' . \count($batch) . ' with error: ' . $e->getMessage();
$deliveryErrors[] = 'Failed sending to targets with error: ' . $e->getMessage();
} finally {
$batchIndex++;
return [
'deliveredTotal' => $deliveredTotal,
'deliveryErrors' => $deliveryErrors,
@ -297,7 +297,7 @@ class Messaging extends Action
};
}, \array_keys($identifiers)));
$results = array_merge(...$results);
$results = \array_merge(...$results);
$deliveredTotal = 0;
$deliveryErrors = [];
@ -330,7 +330,7 @@ class Messaging extends Action
$dbForProject->updateDocument('messages', $message->getId(), $message);
// Delete any attachments that were downloaded to the local cache
// Delete any attachments that were downloaded to local storage
if ($provider->getAttribute('type') === MESSAGE_TYPE_EMAIL) {
if ($deviceForFiles->getType() === Storage::DEVICE_LOCAL) {
return;
@ -345,12 +345,12 @@ class Messaging extends Action
$bucket = $dbForProject->getDocument('buckets', $bucketId);
if ($bucket->isEmpty()) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
throw new \Exception('Storage bucket with the requested ID could not be found');
}
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
throw new \Exception('Storage file with the requested ID could not be found');
}
$path = $file->getAttribute('path', '');
@ -369,7 +369,7 @@ class Messaging extends Action
}
if ($project->isEmpty()) {
throw new Exception('Project not set in payload');
throw new \Exception('Project not set in payload');
}
Console::log('Project: ' . $project->getId());
@ -427,12 +427,13 @@ class Messaging extends Action
$adapter = $this->getSmsAdapter($provider);
$maxBatchSize = $adapter->getMaxMessagesPerRequest();
$batches = \array_chunk($recipients, $maxBatchSize);
$batchIndex = 0;
$batches = \array_chunk(
$recipients,
$adapter->getMaxMessagesPerRequest()
);
batch(\array_map(function ($batch) use ($message, $provider, $adapter, $batchIndex, $project, $queueForUsage) {
return function () use ($batch, $message, $provider, $adapter, $batchIndex, $project, $queueForUsage) {
batch(\array_map(function ($batch) use ($message, $provider, $adapter, $project, $queueForUsage) {
return function () use ($batch, $message, $provider, $adapter, $project, $queueForUsage) {
$message->setAttribute('to', $batch);
$data = $this->buildSmsMessage($message, $provider);
@ -445,7 +446,7 @@ class Messaging extends Action
->addMetric(METRIC_MESSAGES, 1)
->trigger();
} catch (\Throwable $e) {
throw new Exception('Failed sending to targets ' . $batchIndex + 1 . '-' . \count($batch) . ' with error: ' . $e->getMessage(), 500);
throw new \Exception('Failed sending to targets with error: ' . $e->getMessage());
}
};
}, $batches));
@ -556,19 +557,19 @@ class Messaging extends Action
$bucket = $dbForProject->getDocument('buckets', $bucketId);
if ($bucket->isEmpty()) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
throw new \Exception('Storage bucket with the requested ID could not be found');
}
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
throw new \Exception('Storage file with the requested ID could not be found');
}
$mimes = Config::getParam('storage-mimes');
$path = $file->getAttribute('path', '');
if (!$deviceForFiles->exists($path)) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path);
throw new \Exception('File not found in ' . $path);
}
$contentType = 'text/plain';

View file

@ -22,6 +22,15 @@ class V17 extends Filter
case Response::MODEL_TOKEN:
$parsedResponse = $this->parseToken($parsedResponse);
break;
case Response::MODEL_MEMBERSHIP:
$parsedResponse = $this->parseMembership($parsedResponse);
break;
case Response::MODEL_SESSION:
$parsedResponse = $this->parseSession($parsedResponse);
break;
case Response::MODEL_WEBHOOK:
$parsedResponse = $this->parseWebhook($parsedResponse);
break;
}
return $parsedResponse;
@ -30,6 +39,8 @@ class V17 extends Filter
protected function parseUser(array $content)
{
unset($content['targets']);
unset($content['mfa']);
unset($content['totp']);
return $content;
}
@ -45,4 +56,25 @@ class V17 extends Filter
unset($content['phrase']);
return $content;
}
protected function parseMembership(array $content)
{
unset($content['mfa']);
return $content;
}
protected function parseSession(array $content)
{
unset($content['factors']);
unset($content['secret']);
return $content;
}
protected function parseWebhook(array $content)
{
unset($content['enabled']);
unset($content['logs']);
unset($content['attempts']);
return $content;
}
}

View file

@ -74,7 +74,9 @@ class AccountCustomClientTest extends Scope
$this->assertEmpty($response['body']['secret']);
$this->assertNotFalse(\DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $response['body']['expire']));
// already logged in
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
@ -85,11 +87,8 @@ class AccountCustomClientTest extends Scope
'password' => $password,
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals(401, $response['headers']['status-code']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
@ -233,10 +232,7 @@ class AccountCustomClientTest extends Scope
]));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsArray($response['body']);
$this->assertNotEmpty($response['body']);
$this->assertCount(2, $response['body']);
$this->assertEquals(3, $response['body']['total']);
$this->assertEquals(2, $response['body']['total']);
$this->assertEquals($sessionId, $response['body']['sessions'][0]['$id']);
$this->assertEquals('Windows', $response['body']['sessions'][0]['osName']);
@ -293,9 +289,9 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsArray($response['body']['logs']);
$this->assertNotEmpty($response['body']['logs']);
$this->assertCount(4, $response['body']['logs']);
$this->assertCount(3, $response['body']['logs']);
$this->assertIsNumeric($response['body']['total']);
$this->assertEquals("session.create", $response['body']['logs'][2]['event']);
$this->assertEquals("user.create", $response['body']['logs'][2]['event']);
$this->assertEquals(filter_var($response['body']['logs'][2]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][2]['ip']);
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['logs'][2]['time']));
@ -317,10 +313,6 @@ class AccountCustomClientTest extends Scope
$this->assertEquals('--', $response['body']['logs'][1]['countryCode']);
$this->assertEquals('Unknown', $response['body']['logs'][1]['countryName']);
$this->assertEquals("user.create", $response['body']['logs'][3]['event']);
$this->assertEquals(filter_var($response['body']['logs'][3]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][3]['ip']);
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['logs'][2]['time']));
$this->assertEquals('Windows', $response['body']['logs'][2]['osName']);
$this->assertEquals('WIN', $response['body']['logs'][2]['osCode']);
$this->assertEquals('10', $response['body']['logs'][2]['osVersion']);
@ -372,7 +364,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals($responseOffset['headers']['status-code'], 200);
$this->assertIsArray($responseOffset['body']['logs']);
$this->assertNotEmpty($responseOffset['body']['logs']);
$this->assertCount(3, $responseOffset['body']['logs']);
$this->assertCount(2, $responseOffset['body']['logs']);
$this->assertIsNumeric($responseOffset['body']['total']);
$this->assertEquals($response['body']['logs'][1], $responseOffset['body']['logs'][0]);
@ -2239,7 +2231,6 @@ class AccountCustomClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals(201, $response['headers']['status-code']);
@ -2249,6 +2240,9 @@ class AccountCustomClientTest extends Scope
$smsRequest = $this->getLastRequest();
$message = $smsRequest['data']['message'];
$token = substr($message, 0, 6);
return \array_merge($data, [
'token' => \substr($smsRequest['data']['message'], 0, 6)
]);

View file

@ -62,7 +62,9 @@ class AccountCustomServerTest extends Scope
$this->assertNotEmpty($response['body']['secret']);
$this->assertNotFalse(\DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $response['body']['expire']));
// already logged in
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@ -72,11 +74,8 @@ class AccountCustomServerTest extends Scope
'password' => $password,
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals(401, $response['headers']['status-code']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],

View file

@ -281,19 +281,22 @@ class BatchTest extends Scope
public function testQueryBatchedMutations()
{
$projectId = $this->getProject()['$id'];
$email = 'tester' . \uniqid() . '@example.com';
$email1 = 'tester' . \uniqid() . '@example.com';
$email2 = 'tester' . \uniqid() . '@example.com';
$graphQLPayload = [
'query' => 'mutation CreateAndLogin($userId: String!, $email: String!, $password: String!, $name: String) {
accountCreate(userId: $userId, email: $email, password: $password, name: $name) {
name
'query' => 'mutation CreateAndLogin($user1Id: String!, $user2Id: String!, $email1: String!, $email2: String!, $password: String!, $name: String) {
account1: accountCreate(userId: $user1Id, email: $email1, password: $password, name: $name) {
email
}
accountCreateEmailPasswordSession(email: $email, password: $password) {
expire
account2: accountCreate(userId: $user2Id, email: $email2, password: $password, name: $name) {
email
}
}',
'variables' => [
'userId' => ID::unique(),
'email' => $email,
'user1Id' => ID::unique(),
'user2Id' => ID::unique(),
'email1' => $email1,
'email2' => $email2,
'password' => 'password',
'name' => 'Tester',
],
@ -304,12 +307,12 @@ class BatchTest extends Scope
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertIsArray($response['body']['data']);
$this->assertArrayNotHasKey('errors', $response['body']);
$this->assertArrayHasKey('accountCreate', $response['body']['data']);
$this->assertArrayHasKey('accountCreateEmailPasswordSession', $response['body']['data']);
$this->assertEquals('Tester', $response['body']['data']['accountCreate']['name']);
$this->assertArrayHasKey('account1', $response['body']['data']);
$this->assertArrayHasKey('account2', $response['body']['data']);
$this->assertEquals($email1, $response['body']['data']['account1']['email']);
$this->assertEquals($email2, $response['body']['data']['account2']['email']);
}
public function testQueryBatchedMutationsOfSameType()

View file

@ -73,6 +73,8 @@ class V17Test extends TestCase
'remove targets' => [
[
'targets' => 'test',
'mfa' => 'test',
'totp' => 'test',
],
[
],
@ -116,4 +118,70 @@ class V17Test extends TestCase
$this->assertEquals($expected, $result);
}
public function membershipProvider(): array
{
return [
'remove mfa' => [
[
'mfa' => 'test',
],
[
],
],
];
}
/**
* @dataProvider membershipProvider
*/
public function testMembership(array $content, array $expected): void
{
$model = Response::MODEL_MEMBERSHIP;
$result = $this->filter->parse($content, $model);
$this->assertEquals($expected, $result);
}
public function sessionProvider(): array
{
return [
'remove factors and secrets' => [
[
'factors' => 'test',
'secret' => 'test',
],
[
],
]
];
}
/**
* @dataProvider sessionProvider
*/
public function testSession(array $content, array $expected): void
{
$model = Response::MODEL_SESSION;
$result = $this->filter->parse($content, $model);
$this->assertEquals($expected, $result);
}
public function webhookProvider(): array
{
return [
'remove webhook additions' => [
[
'enabled' => true,
'logs' => ['test', 'test'],
'attempts' => 1
],
[
],
],
];
}
}