Merge branch '1.5.x' into feat-rc-sdks
This commit is contained in:
commit
50c2ac9cd6
|
@ -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 => [
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
]);
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
],
|
||||
[
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue