mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch '1.5.x' into feat-ssr-revert-scopes
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -1891,6 +1891,17 @@ $commonCollections = [
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('subscribe'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 128,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => true,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('total'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
|
||||
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
+1
-1
Submodule app/console updated: 74f09e341a...1d942975d1
@@ -3425,10 +3425,10 @@ App::get('/v1/account/mfa/factors')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'listFactors')
|
||||
->label('sdk.description', '/docs/references/account/get.md')
|
||||
->label('sdk.description', '/docs/references/account/list-factors.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_MFA_PROVIDERS)
|
||||
->label('sdk.response.model', Response::MODEL_MFA_FACTORS)
|
||||
->label('sdk.offline.model', '/account')
|
||||
->label('sdk.offline.key', 'current')
|
||||
->inject('response')
|
||||
@@ -3441,10 +3441,10 @@ App::get('/v1/account/mfa/factors')
|
||||
'phone' => $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false)
|
||||
]);
|
||||
|
||||
$response->dynamic($providers, Response::MODEL_MFA_PROVIDERS);
|
||||
$response->dynamic($providers, Response::MODEL_MFA_FACTORS);
|
||||
});
|
||||
|
||||
App::post('/v1/account/mfa/:factor')
|
||||
App::post('/v1/account/mfa/:type')
|
||||
->desc('Add Authenticator')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.mfa')
|
||||
@@ -3455,24 +3455,24 @@ App::post('/v1/account/mfa/:factor')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'addAuthenticator')
|
||||
->label('sdk.description', '/docs/references/account/update-mfa.md')
|
||||
->label('sdk.description', '/docs/references/account/add-authenticator.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_MFA_PROVIDER)
|
||||
->label('sdk.response.model', Response::MODEL_MFA_TYPE)
|
||||
->label('sdk.offline.model', '/account')
|
||||
->label('sdk.offline.key', 'current')
|
||||
->param('factor', null, new WhiteList(['totp']), 'Factor.')
|
||||
->param('type', null, new WhiteList(['totp']), 'Type of authenticator.')
|
||||
->inject('requestTimestamp')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $factor, ?\DateTime $requestTimestamp, Response $response, Document $project, Document $user, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $type, ?\DateTime $requestTimestamp, Response $response, Document $project, Document $user, Database $dbForProject, Event $queueForEvents) {
|
||||
|
||||
$otp = match ($factor) {
|
||||
$otp = match ($type) {
|
||||
'totp' => new TOTP(),
|
||||
default => throw new Exception(Exception::GENERAL_UNKNOWN, 'Unknown provider.')
|
||||
default => throw new Exception(Exception::GENERAL_UNKNOWN, 'Unknown type.')
|
||||
};
|
||||
|
||||
$otp->setLabel($user->getAttribute('email'));
|
||||
@@ -3481,7 +3481,7 @@ App::post('/v1/account/mfa/:factor')
|
||||
$backups = Provider::generateBackupCodes();
|
||||
|
||||
if ($user->getAttribute('totp') && $user->getAttribute('totpVerification')) {
|
||||
throw new Exception(Exception::GENERAL_UNKNOWN, 'TOTP already exists.');
|
||||
throw new Exception(Exception::GENERAL_UNKNOWN, 'TOTP already exists on this account.');
|
||||
}
|
||||
|
||||
$user
|
||||
@@ -3500,10 +3500,10 @@ App::post('/v1/account/mfa/:factor')
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
$response->dynamic($model, Response::MODEL_MFA_PROVIDER);
|
||||
$response->dynamic($model, Response::MODEL_MFA_TYPE);
|
||||
});
|
||||
|
||||
App::put('/v1/account/mfa/:factor')
|
||||
App::put('/v1/account/mfa/:type')
|
||||
->desc('Verify Authenticator')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.mfa')
|
||||
@@ -3514,13 +3514,13 @@ App::put('/v1/account/mfa/:factor')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'verifyAuthenticator')
|
||||
->label('sdk.description', '/docs/references/account/update-mfa.md')
|
||||
->label('sdk.description', '/docs/references/account/verify-authenticator.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->label('sdk.offline.model', '/account')
|
||||
->label('sdk.offline.key', 'current')
|
||||
->param('factor', null, new WhiteList(['totp']), 'Factor.')
|
||||
->param('type', null, new WhiteList(['totp']), 'Type of authenticator.')
|
||||
->param('otp', '', new Text(256), 'Valid verification token.')
|
||||
->inject('requestTimestamp')
|
||||
->inject('response')
|
||||
@@ -3528,9 +3528,9 @@ App::put('/v1/account/mfa/:factor')
|
||||
->inject('project')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $factor, string $otp, ?\DateTime $requestTimestamp, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $type, string $otp, ?\DateTime $requestTimestamp, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) {
|
||||
|
||||
$success = match ($factor) {
|
||||
$success = match ($type) {
|
||||
'totp' => Challenge\TOTP::verify($user, $otp),
|
||||
default => false
|
||||
};
|
||||
@@ -3540,9 +3540,9 @@ App::put('/v1/account/mfa/:factor')
|
||||
}
|
||||
|
||||
if (!$user->getAttribute('totp')) {
|
||||
throw new Exception(Exception::GENERAL_UNKNOWN, 'TOTP not added.');
|
||||
throw new Exception(Exception::GENERAL_UNKNOWN, 'Authenticator needs to be added first.');
|
||||
} elseif ($user->getAttribute('totpVerification')) {
|
||||
throw new Exception(Exception::GENERAL_UNKNOWN, 'TOTP already verified.');
|
||||
throw new Exception(Exception::GENERAL_UNKNOWN, 'Authenticator already verified on this account.');
|
||||
}
|
||||
|
||||
$user->setAttribute('totpVerification', true);
|
||||
@@ -3552,14 +3552,14 @@ App::put('/v1/account/mfa/:factor')
|
||||
$authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$sessionId = Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret, $authDuration);
|
||||
$session = $dbForProject->getDocument('sessions', $sessionId);
|
||||
$dbForProject->updateDocument('sessions', $sessionId, $session->setAttribute('factors', $provider, Document::SET_TYPE_APPEND));
|
||||
$dbForProject->updateDocument('sessions', $sessionId, $session->setAttribute('factors', $type, Document::SET_TYPE_APPEND));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
$response->dynamic($user, Response::MODEL_ACCOUNT);
|
||||
});
|
||||
|
||||
App::delete('/v1/account/mfa/:provider')
|
||||
App::delete('/v1/account/mfa/:type')
|
||||
->desc('Delete Authenticator')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].delete.mfa')
|
||||
@@ -3574,16 +3574,16 @@ App::delete('/v1/account/mfa/:provider')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('provider', null, new WhiteList(['totp']), 'Provider.')
|
||||
->param('type', null, new WhiteList(['totp']), 'Type of authenticator.')
|
||||
->param('otp', '', new Text(256), 'Valid verification token.')
|
||||
->inject('requestTimestamp')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $provider, string $otp, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $type, string $otp, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
|
||||
|
||||
$success = match ($provider) {
|
||||
$success = match ($type) {
|
||||
'totp' => Challenge\TOTP::verify($user, $otp),
|
||||
default => false
|
||||
};
|
||||
@@ -3610,40 +3610,38 @@ App::delete('/v1/account/mfa/:provider')
|
||||
});
|
||||
|
||||
App::post('/v1/account/mfa/challenge')
|
||||
->desc('Create MFA Challenge')
|
||||
->desc('Create 2FA Challenge')
|
||||
->groups(['api', 'account', 'mfa'])
|
||||
->label('scope', 'account')
|
||||
->label('event', 'users.[userId].challenges.[challengeId].create')
|
||||
->label('auth.type', 'createChallenge')
|
||||
->label('audits.event', 'challenge.create')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'createChallenge')
|
||||
->label('sdk.description', '/docs/references/account/create-challenge.md')
|
||||
->label('sdk.method', 'create2FAChallenge')
|
||||
->label('sdk.description', '/docs/references/account/create-2fa-challenge.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_MFA_CHALLENGE)
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},token:{param-token}')
|
||||
->param('provider', '', new WhiteList(['totp', 'phone', 'email']), 'provider.')
|
||||
->param('factor', '', new WhiteList(['totp', 'phone', 'email']), 'Factor used for verification.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('user')
|
||||
->inject('project')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMessaging')
|
||||
->inject('queueForMails')
|
||||
->inject('locale')
|
||||
->action(function (string $provider, Response $response, Database $dbForProject, Document $user, Document $project, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails, Locale $locale) {
|
||||
->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails, Locale $locale) {
|
||||
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
$code = Auth::codeGenerator();
|
||||
$challenge = new Document([
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => $provider,
|
||||
'provider' => $factor,
|
||||
'token' => Auth::tokenGenerator(),
|
||||
'code' => $code,
|
||||
'expire' => $expire,
|
||||
@@ -3656,7 +3654,7 @@ App::post('/v1/account/mfa/challenge')
|
||||
|
||||
$challenge = $dbForProject->createDocument('challenges', $challenge);
|
||||
|
||||
switch ($provider) {
|
||||
switch ($factor) {
|
||||
case 'phone':
|
||||
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||
@@ -3720,7 +3718,7 @@ App::put('/v1/account/mfa/challenge')
|
||||
->label('sdk.response.model', Response::MODEL_SESSION)
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'userId:{param-userId}')
|
||||
->param('challengeId', '', new Text(256), 'Valid verification token.')
|
||||
->param('challengeId', '', new Text(256), 'ID of the challenge.')
|
||||
->param('otp', '', new Text(256), 'Valid verification token.')
|
||||
->inject('project')
|
||||
->inject('response')
|
||||
|
||||
@@ -30,6 +30,7 @@ use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\Roles;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Validator\ArrayList;
|
||||
@@ -1920,15 +1921,17 @@ App::post('/v1/messaging/topics')
|
||||
->label('sdk.response.model', Response::MODEL_TOPIC)
|
||||
->param('topicId', '', new CustomId(), 'Topic ID. Choose a custom Topic ID or a new Topic ID.')
|
||||
->param('name', '', new Text(128), 'Topic Name.')
|
||||
->param('subscribe', [Role::users()], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with subscribe permission. By default all users are granted with any subscribe permission. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $topicId, string $name, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $topicId, string $name, array $subscribe, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$topicId = $topicId == 'unique()' ? ID::unique() : $topicId;
|
||||
|
||||
$topic = new Document([
|
||||
'$id' => $topicId,
|
||||
'name' => $name,
|
||||
'subscribe' => $subscribe,
|
||||
]);
|
||||
|
||||
try {
|
||||
@@ -2220,6 +2223,12 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||
throw new Exception(Exception::TOPIC_NOT_FOUND);
|
||||
}
|
||||
|
||||
$validator = new Authorization('subscribe');
|
||||
|
||||
if (!$validator->isValid($topic->getAttribute('subscribe'))) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription());
|
||||
}
|
||||
|
||||
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
|
||||
|
||||
if ($target->isEmpty()) {
|
||||
@@ -2228,25 +2237,23 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||
|
||||
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
|
||||
|
||||
$userId = $user->getId();
|
||||
|
||||
$subscriber = new Document([
|
||||
'$id' => $subscriberId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
],
|
||||
'topicId' => $topicId,
|
||||
'topicInternalId' => $topic->getInternalId(),
|
||||
'targetId' => $targetId,
|
||||
'targetInternalId' => $target->getInternalId(),
|
||||
'userId' => $userId,
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'providerType' => $target->getAttribute('providerType'),
|
||||
'search' => implode(' ', [
|
||||
$subscriberId,
|
||||
$targetId,
|
||||
$userId,
|
||||
$user->getId(),
|
||||
$target->getAttribute('providerType'),
|
||||
]),
|
||||
]);
|
||||
|
||||
@@ -1523,18 +1523,18 @@ App::patch('/v1/users/:userId/mfa')
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/providers')
|
||||
->desc('List Providers')
|
||||
App::get('/v1/users/:userId/mfa/factors')
|
||||
->desc('List Factors')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'listProviders')
|
||||
->label('sdk.description', '/docs/references/users/list-providers.md')
|
||||
->label('sdk.method', 'listFactors')
|
||||
->label('sdk.description', '/docs/references/users/list-factors.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_MFA_PROVIDERS)
|
||||
->label('sdk.response.model', Response::MODEL_MFA_FACTORS)
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
@@ -1551,10 +1551,10 @@ App::get('/v1/users/:userId/providers')
|
||||
'phone' => $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false)
|
||||
]);
|
||||
|
||||
$response->dynamic($providers, Response::MODEL_MFA_PROVIDERS);
|
||||
$response->dynamic($providers, Response::MODEL_MFA_FACTORS);
|
||||
});
|
||||
|
||||
App::delete('/v1/users/:userId/mfa/:provider')
|
||||
App::delete('/v1/users/:userId/mfa/:type')
|
||||
->desc('Delete Authenticator')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].delete.mfa')
|
||||
@@ -1571,20 +1571,20 @@ App::delete('/v1/users/:userId/mfa/:provider')
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('provider', null, new WhiteList(['totp']), 'Provider.')
|
||||
->param('type', null, new WhiteList(['totp']), 'Type of authenticator.')
|
||||
->param('otp', '', new Text(256), 'Valid verification token.')
|
||||
->inject('requestTimestamp')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $userId, string $provider, string $otp, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $userId, string $type, string $otp, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$success = match ($provider) {
|
||||
$success = match ($type) {
|
||||
'totp' => Challenge\TOTP::verify($user, $otp),
|
||||
default => false
|
||||
};
|
||||
|
||||
@@ -316,81 +316,6 @@ App::init()
|
||||
}
|
||||
});
|
||||
|
||||
App::init()
|
||||
->groups(['auth'])
|
||||
->inject('utopia')
|
||||
->inject('request')
|
||||
->inject('project')
|
||||
->inject('geodb')
|
||||
->action(function (App $utopia, Request $request, Document $project, Reader $geodb) {
|
||||
$denylist = App::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', '');
|
||||
if (!empty($denylist) && $project->getId() === 'console') {
|
||||
$countries = explode(',', $denylist);
|
||||
$record = $geodb->get($request->getIP()) ?? [];
|
||||
$country = $record['country']['iso_code'] ?? '';
|
||||
if (in_array($country, $countries)) {
|
||||
throw new Exception(Exception::GENERAL_REGION_ACCESS_DENIED);
|
||||
}
|
||||
}
|
||||
|
||||
$route = $utopia->getRoute();
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
|
||||
if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
|
||||
return;
|
||||
}
|
||||
|
||||
$auths = $project->getAttribute('auths', []);
|
||||
switch ($route->getLabel('auth.type', '')) {
|
||||
case 'emailPassword':
|
||||
if (($auths['emailPassword'] ?? true) === false) {
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Email / Password authentication is disabled for this project');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'magic-url':
|
||||
if (($auths['usersAuthMagicURL'] ?? true) === false) {
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Magic URL authentication is disabled for this project');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'anonymous':
|
||||
if (($auths['anonymous'] ?? true) === false) {
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Anonymous authentication is disabled for this project');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'phone':
|
||||
if (($auths['phone'] ?? true) === false) {
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Phone authentication is disabled for this project');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'invites':
|
||||
if (($auths['invites'] ?? true) === false) {
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Invites authentication is disabled for this project');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'jwt':
|
||||
if (($auths['JWT'] ?? true) === false) {
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'JWT authentication is disabled for this project');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'email-otp':
|
||||
if (($auths['emailOTP'] ?? true) === false) {
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Email OTP authentication is disabled for this project');
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Unsupported authentication type: ' . $route->getLabel('auth.type', ''));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Limit user session
|
||||
*
|
||||
|
||||
+1
-1
@@ -940,7 +940,7 @@ $register->set('smtp', function () {
|
||||
return $mail;
|
||||
});
|
||||
$register->set('geodb', function () {
|
||||
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2023-01.mmdb');
|
||||
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.mmdb');
|
||||
});
|
||||
$register->set('passwordsDictionary', function () {
|
||||
$content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords');
|
||||
|
||||
@@ -77,12 +77,6 @@
|
||||
"webonyx/graphql-php": "14.11.*",
|
||||
"league/csv": "9.14.*"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"url": "https://github.com/appwrite/runtimes.git",
|
||||
"type": "git"
|
||||
}
|
||||
],
|
||||
"require-dev": {
|
||||
"ext-fileinfo": "*",
|
||||
"appwrite/sdk-generator": "0.36.*",
|
||||
|
||||
Generated
+24
-13
@@ -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": "55d2ed1081591f2b3b3af9999236b109",
|
||||
"content-hash": "609062319cc652e2760367f39604ac77",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -162,6 +162,12 @@
|
||||
"url": "https://github.com/appwrite/runtimes.git",
|
||||
"reference": "214a37c2c66e0f2bc9c30fdfde66955d9fd084a1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/214a37c2c66e0f2bc9c30fdfde66955d9fd084a1",
|
||||
"reference": "214a37c2c66e0f2bc9c30fdfde66955d9fd084a1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"utopia-php/system": "0.7.*"
|
||||
@@ -176,6 +182,7 @@
|
||||
"Appwrite\\Runtimes\\": "src/Runtimes"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
@@ -195,6 +202,10 @@
|
||||
"php",
|
||||
"runtimes"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/runtimes/issues",
|
||||
"source": "https://github.com/appwrite/runtimes/tree/0.13.2"
|
||||
},
|
||||
"time": "2023-11-22T15:36:00+00:00"
|
||||
},
|
||||
{
|
||||
@@ -1900,16 +1911,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/messaging",
|
||||
"version": "0.9.0",
|
||||
"version": "0.9.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/messaging.git",
|
||||
"reference": "df54ba51570e886724590edeb03dbd455bb0464d"
|
||||
"reference": "7beec07684e9e1dfcf4ab5b1ba731fa396dccbdf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/df54ba51570e886724590edeb03dbd455bb0464d",
|
||||
"reference": "df54ba51570e886724590edeb03dbd455bb0464d",
|
||||
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/7beec07684e9e1dfcf4ab5b1ba731fa396dccbdf",
|
||||
"reference": "7beec07684e9e1dfcf4ab5b1ba731fa396dccbdf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1944,9 +1955,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/messaging/issues",
|
||||
"source": "https://github.com/utopia-php/messaging/tree/0.9.0"
|
||||
"source": "https://github.com/utopia-php/messaging/tree/0.9.1"
|
||||
},
|
||||
"time": "2024-01-31T11:51:27+00:00"
|
||||
"time": "2024-02-15T03:44:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
@@ -2768,16 +2779,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "0.36.2",
|
||||
"version": "0.36.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "0aa67479d75f0e0cb7b60454031534d7f0abaece"
|
||||
"reference": "8d308f7f492545da3e51ea5b91c0778392c40b93"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/0aa67479d75f0e0cb7b60454031534d7f0abaece",
|
||||
"reference": "0aa67479d75f0e0cb7b60454031534d7f0abaece",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/8d308f7f492545da3e51ea5b91c0778392c40b93",
|
||||
"reference": "8d308f7f492545da3e51ea5b91c0778392c40b93",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2813,9 +2824,9 @@
|
||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.36.2"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.36.3"
|
||||
},
|
||||
"time": "2024-01-19T01:04:35+00:00"
|
||||
"time": "2024-02-14T06:33:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
|
||||
@@ -264,7 +264,7 @@ class Messaging extends Action
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$deliveryErrors[] = 'Failed sending to targets ' . $batchIndex + 1 . '-' . \count($batch) . ' with error: ' . $e->getMessage();
|
||||
$deliveryErrors[] = 'Failed sending to targets ' . $batchIndex + 1 . ' of ' . \count($batch) . ' with error: ' . $e->getMessage();
|
||||
} finally {
|
||||
$batchIndex++;
|
||||
|
||||
@@ -288,6 +288,10 @@ class Messaging extends Action
|
||||
$deliveryErrors = \array_merge($deliveryErrors, $result['deliveryErrors']);
|
||||
}
|
||||
|
||||
if (empty($deliveryErrors) && $deliveredTotal === 0) {
|
||||
$deliveryErrors[] = 'Unknown error';
|
||||
}
|
||||
|
||||
$message->setAttribute('deliveryErrors', $deliveryErrors);
|
||||
|
||||
if (\count($message->getAttribute('deliveryErrors')) > 0) {
|
||||
|
||||
@@ -76,13 +76,13 @@ use Appwrite\Utopia\Response\Model\HealthStatus;
|
||||
use Appwrite\Utopia\Response\Model\HealthTime;
|
||||
use Appwrite\Utopia\Response\Model\HealthVersion;
|
||||
use Appwrite\Utopia\Response\Model\MFAChallenge;
|
||||
use Appwrite\Utopia\Response\Model\MFAProvider;
|
||||
use Appwrite\Utopia\Response\Model\MFAProviders;
|
||||
use Appwrite\Utopia\Response\Model\Installation;
|
||||
use Appwrite\Utopia\Response\Model\LocaleCode;
|
||||
use Appwrite\Utopia\Response\Model\MetricBreakdown;
|
||||
use Appwrite\Utopia\Response\Model\Provider;
|
||||
use Appwrite\Utopia\Response\Model\Message;
|
||||
use Appwrite\Utopia\Response\Model\MFAFactors;
|
||||
use Appwrite\Utopia\Response\Model\MFAType;
|
||||
use Appwrite\Utopia\Response\Model\Subscriber;
|
||||
use Appwrite\Utopia\Response\Model\Topic;
|
||||
use Appwrite\Utopia\Response\Model\ProviderRepository;
|
||||
@@ -170,8 +170,8 @@ class Response extends SwooleResponse
|
||||
public const MODEL_PREFERENCES = 'preferences';
|
||||
|
||||
// MFA
|
||||
public const MODEL_MFA_PROVIDER = 'mfaProvider';
|
||||
public const MODEL_MFA_PROVIDERS = 'mfaProviders';
|
||||
public const MODEL_MFA_TYPE = 'mfaType';
|
||||
public const MODEL_MFA_FACTORS = 'mfaFactors';
|
||||
public const MODEL_MFA_OTP = 'mfaTotp';
|
||||
public const MODEL_MFA_CHALLENGE = 'mfaChallenge';
|
||||
|
||||
@@ -443,8 +443,8 @@ class Response extends SwooleResponse
|
||||
->setModel(new TemplateEmail())
|
||||
->setModel(new ConsoleVariables())
|
||||
->setModel(new MFAChallenge())
|
||||
->setModel(new MFAProvider())
|
||||
->setModel(new MFAProviders())
|
||||
->setModel(new MFAType())
|
||||
->setModel(new MFAFactors())
|
||||
->setModel(new Provider())
|
||||
->setModel(new Message())
|
||||
->setModel(new Topic())
|
||||
|
||||
+3
-3
@@ -5,7 +5,7 @@ namespace Appwrite\Utopia\Response\Model;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class MFAProviders extends Model
|
||||
class MFAFactors extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
@@ -38,7 +38,7 @@ class MFAProviders extends Model
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'MFAProviders';
|
||||
return 'MFAFactors';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,6 +48,6 @@ class MFAProviders extends Model
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_MFA_PROVIDERS;
|
||||
return Response::MODEL_MFA_FACTORS;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -5,7 +5,7 @@ namespace Appwrite\Utopia\Response\Model;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class MFAProvider extends Model
|
||||
class MFAType extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
@@ -39,7 +39,7 @@ class MFAProvider extends Model
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'MFAProvider';
|
||||
return 'MFAType';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,6 +49,6 @@ class MFAProvider extends Model
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_MFA_PROVIDER;
|
||||
return Response::MODEL_MFA_TYPE;
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,13 @@ class Topic extends Model
|
||||
'description' => 'Total count of subscribers subscribed to topic.',
|
||||
'default' => 0,
|
||||
'example' => 100,
|
||||
])
|
||||
->addRule('subscribe', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Subscribe permissions.',
|
||||
'default' => ['users'],
|
||||
'example' => 'users',
|
||||
'array' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use Tests\E2E\Client;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\DSN\DSN;
|
||||
|
||||
@@ -295,7 +296,7 @@ trait MessagingBase
|
||||
|
||||
public function testCreateTopic(): array
|
||||
{
|
||||
$response = $this->client->call(Client::METHOD_POST, '/messaging/topics', [
|
||||
$response1 = $this->client->call(Client::METHOD_POST, '/messaging/topics', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
@@ -303,18 +304,34 @@ trait MessagingBase
|
||||
'topicId' => ID::unique(),
|
||||
'name' => 'my-app',
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertEquals('my-app', $response['body']['name']);
|
||||
$this->assertEquals(201, $response1['headers']['status-code']);
|
||||
$this->assertEquals('my-app', $response1['body']['name']);
|
||||
|
||||
return $response['body'];
|
||||
$response2 = $this->client->call(Client::METHOD_POST, '/messaging/topics', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'topicId' => ID::unique(),
|
||||
'name' => 'my-app2',
|
||||
'subscribe' => [Role::user('invalid')->toString()],
|
||||
]);
|
||||
$this->assertEquals(201, $response2['headers']['status-code']);
|
||||
$this->assertEquals('my-app2', $response2['body']['name']);
|
||||
$this->assertEquals(1, \count($response2['body']['subscribe']));
|
||||
|
||||
return [
|
||||
'public' => $response1['body'],
|
||||
'private' => $response2['body'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateTopic
|
||||
*/
|
||||
public function testUpdateTopic(array $topic): string
|
||||
public function testUpdateTopic(array $topics): string
|
||||
{
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/messaging/topics/' . $topic['$id'], [
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/messaging/topics/' . $topics['public']['$id'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
@@ -323,6 +340,7 @@ trait MessagingBase
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('android-app', $response['body']['name']);
|
||||
|
||||
return $response['body']['$id'];
|
||||
}
|
||||
|
||||
@@ -342,7 +360,7 @@ trait MessagingBase
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(1, \count($response['body']['topics']));
|
||||
$this->assertEquals(2, \count($response['body']['topics']));
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics', [
|
||||
'content-type' => 'application/json',
|
||||
@@ -378,7 +396,7 @@ trait MessagingBase
|
||||
/**
|
||||
* @depends testCreateTopic
|
||||
*/
|
||||
public function testCreateSubscriber(array $topic)
|
||||
public function testCreateSubscriber(array $topics)
|
||||
{
|
||||
$userId = $this->getUser()['$id'];
|
||||
|
||||
@@ -408,7 +426,7 @@ trait MessagingBase
|
||||
|
||||
$this->assertEquals(201, $target['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topic['$id'] . '/subscribers', \array_merge([
|
||||
$response = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topics['public']['$id'] . '/subscribers', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
@@ -420,7 +438,7 @@ trait MessagingBase
|
||||
$this->assertEquals($target['body']['userId'], $response['body']['target']['userId']);
|
||||
$this->assertEquals($target['body']['providerType'], $response['body']['target']['providerType']);
|
||||
|
||||
$topic = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['$id'], [
|
||||
$topic = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topics['public']['$id'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
@@ -430,6 +448,20 @@ trait MessagingBase
|
||||
$this->assertEquals('android-app', $topic['body']['name']);
|
||||
$this->assertEquals(1, $topic['body']['total']);
|
||||
|
||||
$response2 = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topics['private']['$id'] . '/subscribers', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'subscriberId' => ID::unique(),
|
||||
'targetId' => $target['body']['$id'],
|
||||
]);
|
||||
|
||||
if ($this->getSide() === 'client') {
|
||||
$this->assertEquals(401, $response2['headers']['status-code']);
|
||||
} else {
|
||||
$this->assertEquals(201, $response2['headers']['status-code']);
|
||||
}
|
||||
|
||||
return [
|
||||
'topicId' => $topic['body']['$id'],
|
||||
'targetId' => $target['body']['$id'],
|
||||
|
||||
Reference in New Issue
Block a user