mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
cleanups
This commit is contained in:
@@ -173,7 +173,7 @@ return [
|
||||
'size' => 256,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => Auth::DEFAULT_ALGO,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
@@ -184,7 +184,7 @@ return [
|
||||
'size' => 65535,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
'default' => new \stdClass(),
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
|
||||
@@ -38,7 +38,7 @@ $console = [
|
||||
'mockNumbers' => [],
|
||||
'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled',
|
||||
'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
|
||||
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
|
||||
'duration' => TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
|
||||
'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled'
|
||||
],
|
||||
'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
|
||||
|
||||
@@ -84,7 +84,7 @@ $admins = [
|
||||
];
|
||||
|
||||
return [
|
||||
Auth::USER_ROLE_GUESTS => [
|
||||
USER_ROLE_GUESTS => [
|
||||
'label' => 'Guests',
|
||||
'scopes' => [
|
||||
'global',
|
||||
@@ -102,23 +102,23 @@ return [
|
||||
'execution.write',
|
||||
],
|
||||
],
|
||||
Auth::USER_ROLE_USERS => [
|
||||
USER_ROLE_USERS => [
|
||||
'label' => 'Users',
|
||||
'scopes' => \array_merge($member),
|
||||
],
|
||||
Auth::USER_ROLE_ADMIN => [
|
||||
USER_ROLE_ADMIN => [
|
||||
'label' => 'Admin',
|
||||
'scopes' => \array_merge($admins),
|
||||
],
|
||||
Auth::USER_ROLE_DEVELOPER => [
|
||||
USER_ROLE_DEVELOPER => [
|
||||
'label' => 'Developer',
|
||||
'scopes' => \array_merge($admins),
|
||||
],
|
||||
Auth::USER_ROLE_OWNER => [
|
||||
USER_ROLE_OWNER => [
|
||||
'label' => 'Owner',
|
||||
'scopes' => \array_merge($member, $admins),
|
||||
],
|
||||
Auth::USER_ROLE_APPS => [
|
||||
USER_ROLE_APPS => [
|
||||
'label' => 'Applications',
|
||||
'scopes' => ['global', 'health.read', 'graphql'],
|
||||
],
|
||||
|
||||
@@ -176,17 +176,17 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
||||
|
||||
$user->setAttributes($userFromRequest->getArrayCopy());
|
||||
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
$sessionSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION);
|
||||
$sessionSecret = Auth::tokenGenerator(TOKEN_LENGTH_SESSION);
|
||||
|
||||
$factor = (match ($verifiedToken->getAttribute('type')) {
|
||||
Auth::TOKEN_TYPE_MAGIC_URL,
|
||||
Auth::TOKEN_TYPE_OAUTH2,
|
||||
Auth::TOKEN_TYPE_EMAIL => Type::EMAIL,
|
||||
Auth::TOKEN_TYPE_PHONE => Type::PHONE,
|
||||
Auth::TOKEN_TYPE_GENERIC => 'token',
|
||||
TOKEN_TYPE_MAGIC_URL,
|
||||
TOKEN_TYPE_OAUTH2,
|
||||
TOKEN_TYPE_EMAIL => Type::EMAIL,
|
||||
TOKEN_TYPE_PHONE => Type::PHONE,
|
||||
TOKEN_TYPE_GENERIC => 'token',
|
||||
default => throw new Exception(Exception::USER_INVALID_TOKEN)
|
||||
});
|
||||
|
||||
@@ -221,11 +221,11 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
|
||||
// Magic URL + Email OTP
|
||||
if ($verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_MAGIC_URL || $verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_EMAIL) {
|
||||
if ($verifiedToken->getAttribute('type') === TOKEN_TYPE_MAGIC_URL || $verifiedToken->getAttribute('type') === TOKEN_TYPE_EMAIL) {
|
||||
$user->setAttribute('emailVerification', true);
|
||||
}
|
||||
|
||||
if ($verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_PHONE) {
|
||||
if ($verifiedToken->getAttribute('type') === TOKEN_TYPE_PHONE) {
|
||||
$user->setAttribute('phoneVerification', true);
|
||||
}
|
||||
|
||||
@@ -236,8 +236,8 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
||||
}
|
||||
|
||||
$isAllowedTokenType = match ($verifiedToken->getAttribute('type')) {
|
||||
Auth::TOKEN_TYPE_MAGIC_URL,
|
||||
Auth::TOKEN_TYPE_EMAIL => false,
|
||||
TOKEN_TYPE_MAGIC_URL,
|
||||
TOKEN_TYPE_EMAIL => false,
|
||||
default => true
|
||||
};
|
||||
|
||||
@@ -383,8 +383,8 @@ App::post('/v1/account')
|
||||
'password' => $hash,
|
||||
'passwordHistory' => $passwordHistory > 0 ? [$password] : [],
|
||||
'passwordUpdate' => DateTime::now(),
|
||||
'hash' => Auth::DEFAULT_ALGO,
|
||||
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
'hash' => $proof->getHash()->getName(),
|
||||
'hashOptions' => $proof->getHash()->getOptions(),
|
||||
'registration' => DateTime::now(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
@@ -821,7 +821,7 @@ App::patch('/v1/account/sessions/:sessionId')
|
||||
}
|
||||
|
||||
// Extend session
|
||||
$authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$authDuration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$session->setAttribute('expire', DateTime::addSeconds(new \DateTime(), $authDuration));
|
||||
|
||||
// Refresh OAuth access token
|
||||
@@ -893,7 +893,8 @@ App::post('/v1/account/sessions/email')
|
||||
->inject('queueForMails')
|
||||
->inject('hooks')
|
||||
->inject('store')
|
||||
->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Store $store) {
|
||||
->inject('proofForPassword')
|
||||
->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Store $store, ProofsPassword $proofForPassword) {
|
||||
$email = \strtolower($email);
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
@@ -901,7 +902,9 @@ App::post('/v1/account/sessions/email')
|
||||
Query::equal('email', [$email]),
|
||||
]);
|
||||
|
||||
if ($profile->isEmpty() || empty($profile->getAttribute('passwordUpdate')) || !Auth::passwordVerify($password, $profile->getAttribute('password'), $profile->getAttribute('hash'), $profile->getAttribute('hashOptions'))) {
|
||||
$userProofForPassword = ProofsPassword::createHash($profile->getAttribute('hash'), $profile->getAttribute('hashOptions'));
|
||||
|
||||
if ($profile->isEmpty() || empty($profile->getAttribute('passwordUpdate')) || !$userProofForPassword->verify($password, $profile->getAttribute('password'))) {
|
||||
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
|
||||
}
|
||||
|
||||
@@ -917,16 +920,16 @@ App::post('/v1/account/sessions/email')
|
||||
|
||||
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]);
|
||||
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
$secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION);
|
||||
$secret = Auth::tokenGenerator(TOKEN_LENGTH_SESSION);
|
||||
$session = new Document(array_merge(
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'provider' => SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => $email,
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
@@ -943,11 +946,11 @@ App::post('/v1/account/sessions/email')
|
||||
Authorization::setRole(Role::user($user->getId())->toString());
|
||||
|
||||
// Re-hash if not using recommended algo
|
||||
if ($user->getAttribute('hash') !== Auth::DEFAULT_ALGO) {
|
||||
if ($user->getAttribute('hash') !== $proofForPassword->getHash()->getName()) {
|
||||
$user
|
||||
->setAttribute('password', (new ProofsPassword())->hash($password))
|
||||
->setAttribute('hash', Auth::DEFAULT_ALGO)
|
||||
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS);
|
||||
->setAttribute('hash', $proofForPassword->getHash()->getName())
|
||||
->setAttribute('hashOptions', $proofForPassword->getHash()->getOptions());
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
}
|
||||
|
||||
@@ -1033,7 +1036,8 @@ App::post('/v1/account/sessions/anonymous')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->inject('store')
|
||||
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store) {
|
||||
->inject('proofForPassword')
|
||||
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword) {
|
||||
$protocol = $request->getProtocol();
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
@@ -1065,8 +1069,8 @@ App::post('/v1/account/sessions/anonymous')
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
'password' => null,
|
||||
'hash' => Auth::DEFAULT_ALGO,
|
||||
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
'hash' => $proofForPassword->getHash()->getName(),
|
||||
'hashOptions' => $proofForPassword->getHash()->getOptions(),
|
||||
'passwordUpdate' => null,
|
||||
'registration' => DateTime::now(),
|
||||
'reset' => false,
|
||||
@@ -1084,17 +1088,17 @@ App::post('/v1/account/sessions/anonymous')
|
||||
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
|
||||
|
||||
// Create session token
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
$secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION);
|
||||
$secret = Auth::tokenGenerator(TOKEN_LENGTH_SESSION);
|
||||
|
||||
$session = new Document(array_merge(
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_ANONYMOUS,
|
||||
'provider' => SESSION_PROVIDER_ANONYMOUS,
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
'ip' => $request->getIP(),
|
||||
@@ -1350,7 +1354,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->inject('store')
|
||||
->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, Store $store) use ($oauthDefaultSuccess) {
|
||||
->inject('proofForPassword')
|
||||
->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, Store $store, ProofsPassword $proofForPassword) use ($oauthDefaultSuccess) {
|
||||
$protocol = $request->getProtocol();
|
||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
|
||||
@@ -1568,8 +1573,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
'emailVerification' => true,
|
||||
'status' => true, // Email should already be authenticated by OAuth2 provider
|
||||
'password' => null,
|
||||
'hash' => Auth::DEFAULT_ALGO,
|
||||
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
'hash' => $proofForPassword->getHash()->getName(),
|
||||
'hashOptions' => $proofForPassword->getHash()->getOptions(),
|
||||
'passwordUpdate' => null,
|
||||
'registration' => DateTime::now(),
|
||||
'reset' => false,
|
||||
@@ -1668,17 +1673,17 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
$state['success'] = URLParser::parse($state['success']);
|
||||
$query = URLParser::parseQuery($state['success']['query']);
|
||||
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration));
|
||||
|
||||
// If the `token` param is set, we will return the token in the query string
|
||||
if ($state['token']) {
|
||||
$secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_OAUTH2);
|
||||
$secret = Auth::tokenGenerator(TOKEN_LENGTH_OAUTH2);
|
||||
$token = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_OAUTH2,
|
||||
'type' => TOKEN_TYPE_OAUTH2,
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'expire' => $expire,
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
@@ -1707,7 +1712,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
} else {
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
$secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION);
|
||||
$secret = Auth::tokenGenerator(TOKEN_LENGTH_SESSION);
|
||||
|
||||
$session = new Document(array_merge([
|
||||
'$id' => ID::unique(),
|
||||
@@ -1902,7 +1907,8 @@ App::post('/v1/account/tokens/magic-url')
|
||||
->inject('locale')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMails')
|
||||
->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) {
|
||||
->inject('proofForPassword')
|
||||
->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword) {
|
||||
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
|
||||
}
|
||||
@@ -1951,8 +1957,8 @@ App::post('/v1/account/tokens/magic-url')
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
'password' => null,
|
||||
'hash' => Auth::DEFAULT_ALGO,
|
||||
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
'hash' => $proofForPassword->getHash()->getName(),
|
||||
'hashOptions' => $proofForPassword->getHash()->getOptions(),
|
||||
'passwordUpdate' => null,
|
||||
'registration' => DateTime::now(),
|
||||
'reset' => false,
|
||||
@@ -1970,14 +1976,14 @@ App::post('/v1/account/tokens/magic-url')
|
||||
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
|
||||
}
|
||||
|
||||
$tokenSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_MAGIC_URL);
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM));
|
||||
$tokenSecret = Auth::tokenGenerator(TOKEN_LENGTH_MAGIC_URL);
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_CONFIRM));
|
||||
|
||||
$token = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_MAGIC_URL,
|
||||
'type' => TOKEN_TYPE_MAGIC_URL,
|
||||
'secret' => Auth::hash($tokenSecret), // One way hash encryption to protect DB leak
|
||||
'expire' => $expire,
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
@@ -2150,7 +2156,8 @@ App::post('/v1/account/tokens/email')
|
||||
->inject('locale')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMails')
|
||||
->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) {
|
||||
->inject('proofForPassword')
|
||||
->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword) {
|
||||
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
|
||||
}
|
||||
@@ -2198,8 +2205,8 @@ App::post('/v1/account/tokens/email')
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
'password' => null,
|
||||
'hash' => Auth::DEFAULT_ALGO,
|
||||
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
'hash' => $proofForPassword->getHash()->getName(),
|
||||
'hashOptions' => $proofForPassword->getHash()->getOptions(),
|
||||
'passwordUpdate' => null,
|
||||
'registration' => DateTime::now(),
|
||||
'reset' => false,
|
||||
@@ -2216,13 +2223,13 @@ App::post('/v1/account/tokens/email')
|
||||
}
|
||||
|
||||
$tokenSecret = Auth::codeGenerator(6);
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_OTP));
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_OTP));
|
||||
|
||||
$token = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_EMAIL,
|
||||
'type' => TOKEN_TYPE_EMAIL,
|
||||
'secret' => Auth::hash($tokenSecret), // One way hash encryption to protect DB leak
|
||||
'expire' => $expire,
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
@@ -2549,13 +2556,13 @@ App::post('/v1/account/tokens/phone')
|
||||
}
|
||||
|
||||
$secret ??= Auth::codeGenerator();
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_OTP));
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_OTP));
|
||||
|
||||
$token = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_PHONE,
|
||||
'type' => TOKEN_TYPE_PHONE,
|
||||
'secret' => Auth::hash($secret),
|
||||
'expire' => $expire,
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
@@ -2861,14 +2868,16 @@ App::patch('/v1/account/password')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->inject('hooks')
|
||||
->action(function (string $password, string $oldPassword, ?\DateTime $requestTimestamp, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks) {
|
||||
->inject('proofForPassword')
|
||||
->action(function (string $password, string $oldPassword, ?\DateTime $requestTimestamp, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks, ProofsPassword $proofForPassword) {
|
||||
|
||||
$userProofForPassword = ProofsPassword::createHash($user->getAttribute('hash'), $user->getAttribute('hashOptions'));
|
||||
// Check old password only if its an existing user.
|
||||
if (!empty($user->getAttribute('passwordUpdate')) && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))) { // Double check user password
|
||||
if (!empty($user->getAttribute('passwordUpdate')) && !$userProofForPassword->verify($oldPassword, $user->getAttribute('password'))) { // Double check user password
|
||||
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
|
||||
}
|
||||
|
||||
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
|
||||
$newPassword = $proofForPassword->hash($password);
|
||||
$historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
||||
$history = $user->getAttribute('passwordHistory', []);
|
||||
if ($historyLimit > 0) {
|
||||
@@ -2894,8 +2903,8 @@ App::patch('/v1/account/password')
|
||||
->setAttribute('password', $newPassword)
|
||||
->setAttribute('passwordHistory', $history)
|
||||
->setAttribute('passwordUpdate', DateTime::now())
|
||||
->setAttribute('hash', Auth::DEFAULT_ALGO)
|
||||
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS);
|
||||
->setAttribute('hash', $proofForPassword->getHash()->getName())
|
||||
->setAttribute('hashOptions', $proofForPassword->getHash()->getOptions());
|
||||
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
|
||||
@@ -2938,9 +2947,11 @@ App::patch('/v1/account/email')
|
||||
// passwordUpdate will be empty if the user has never set a password
|
||||
$passwordUpdate = $user->getAttribute('passwordUpdate');
|
||||
|
||||
$userProofForPassword = ProofsPassword::createHash($user->getAttribute('hash'), $user->getAttribute('hashOptions'));
|
||||
|
||||
if (
|
||||
!empty($passwordUpdate) &&
|
||||
!$proofForPassword->verify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))
|
||||
!$userProofForPassword->verify($password, $user->getAttribute('password'))
|
||||
) { // Double check user password
|
||||
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
|
||||
}
|
||||
@@ -2967,9 +2978,9 @@ App::patch('/v1/account/email')
|
||||
|
||||
if (empty($passwordUpdate)) {
|
||||
$user
|
||||
->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS))
|
||||
->setAttribute('hash', Auth::DEFAULT_ALGO)
|
||||
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS)
|
||||
->setAttribute('password', $proofForPassword->hash($password))
|
||||
->setAttribute('hash', $proofForPassword->getHash()->getName())
|
||||
->setAttribute('hashOptions', $proofForPassword->getHash()->getOptions())
|
||||
->setAttribute('passwordUpdate', DateTime::now());
|
||||
}
|
||||
|
||||
@@ -3030,13 +3041,16 @@ App::patch('/v1/account/phone')
|
||||
->inject('queueForEvents')
|
||||
->inject('project')
|
||||
->inject('hooks')
|
||||
->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks) {
|
||||
->inject('proofForPassword')
|
||||
->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, ProofsPassword $proofForPassword) {
|
||||
// passwordUpdate will be empty if the user has never set a password
|
||||
$passwordUpdate = $user->getAttribute('passwordUpdate');
|
||||
|
||||
$userProofForPassword = ProofsPassword::createHash($user->getAttribute('hash'), $user->getAttribute('hashOptions'));
|
||||
|
||||
if (
|
||||
!empty($passwordUpdate) &&
|
||||
!Auth::passwordVerify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))
|
||||
!$userProofForPassword->verify($password, $user->getAttribute('password'))
|
||||
) { // Double check user password
|
||||
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
|
||||
}
|
||||
@@ -3060,9 +3074,9 @@ App::patch('/v1/account/phone')
|
||||
|
||||
if (empty($passwordUpdate)) {
|
||||
$user
|
||||
->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS))
|
||||
->setAttribute('hash', Auth::DEFAULT_ALGO)
|
||||
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS)
|
||||
->setAttribute('password', $proofForPassword->hash($password))
|
||||
->setAttribute('hash', $proofForPassword->getHash()->getName())
|
||||
->setAttribute('hashOptions', $proofForPassword->getHash()->getOptions())
|
||||
->setAttribute('passwordUpdate', DateTime::now());
|
||||
}
|
||||
|
||||
@@ -3233,14 +3247,14 @@ App::post('/v1/account/recovery')
|
||||
throw new Exception(Exception::USER_BLOCKED);
|
||||
}
|
||||
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_RECOVERY);
|
||||
$expire = DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_RECOVERY);
|
||||
|
||||
$secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_RECOVERY);
|
||||
$secret = Auth::tokenGenerator(TOKEN_LENGTH_RECOVERY);
|
||||
$recovery = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $profile->getId(),
|
||||
'userInternalId' => $profile->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
'type' => TOKEN_TYPE_RECOVERY,
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'expire' => $expire,
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
@@ -3391,7 +3405,8 @@ App::put('/v1/account/recovery')
|
||||
->inject('project')
|
||||
->inject('queueForEvents')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks) {
|
||||
->inject('proofForPassword')
|
||||
->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, ProofsPassword $proofForPassword) {
|
||||
$profile = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($profile->isEmpty()) {
|
||||
@@ -3399,7 +3414,7 @@ App::put('/v1/account/recovery')
|
||||
}
|
||||
|
||||
$tokens = $profile->getAttribute('tokens', []);
|
||||
$verifiedToken = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_RECOVERY, $secret);
|
||||
$verifiedToken = Auth::tokenVerify($tokens, TOKEN_TYPE_RECOVERY, $secret);
|
||||
|
||||
if (!$verifiedToken) {
|
||||
throw new Exception(Exception::USER_INVALID_TOKEN);
|
||||
@@ -3407,7 +3422,7 @@ App::put('/v1/account/recovery')
|
||||
|
||||
Authorization::setRole(Role::user($profile->getId())->toString());
|
||||
|
||||
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
|
||||
$newPassword = $proofForPassword->hash($password);
|
||||
|
||||
$historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
||||
$history = $profile->getAttribute('passwordHistory', []);
|
||||
@@ -3427,8 +3442,8 @@ App::put('/v1/account/recovery')
|
||||
->setAttribute('password', $newPassword)
|
||||
->setAttribute('passwordHistory', $history)
|
||||
->setAttribute('passwordUpdate', DateTime::now())
|
||||
->setAttribute('hash', Auth::DEFAULT_ALGO)
|
||||
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS)
|
||||
->setAttribute('hash', $proofForPassword->getHash()->getName())
|
||||
->setAttribute('hashOptions', $proofForPassword->getHash()->getOptions())
|
||||
->setAttribute('emailVerification', true));
|
||||
|
||||
$user->setAttributes($profile->getArrayCopy());
|
||||
@@ -3495,14 +3510,14 @@ App::post('/v1/account/verification')
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
$verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION);
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
$verificationSecret = Auth::tokenGenerator(TOKEN_LENGTH_VERIFICATION);
|
||||
$expire = DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_CONFIRM);
|
||||
|
||||
$verification = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_VERIFICATION,
|
||||
'type' => TOKEN_TYPE_VERIFICATION,
|
||||
'secret' => Auth::hash($verificationSecret), // One way hash encryption to protect DB leak
|
||||
'expire' => $expire,
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
@@ -3658,7 +3673,7 @@ App::put('/v1/account/verification')
|
||||
}
|
||||
|
||||
$tokens = $profile->getAttribute('tokens', []);
|
||||
$verifiedToken = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_VERIFICATION, $secret);
|
||||
$verifiedToken = Auth::tokenVerify($tokens, TOKEN_TYPE_VERIFICATION, $secret);
|
||||
|
||||
if (!$verifiedToken) {
|
||||
throw new Exception(Exception::USER_INVALID_TOKEN);
|
||||
@@ -3750,13 +3765,13 @@ App::post('/v1/account/verification/phone')
|
||||
}
|
||||
|
||||
$secret ??= Auth::codeGenerator();
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
$expire = DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_CONFIRM);
|
||||
|
||||
$verification = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_PHONE,
|
||||
'type' => TOKEN_TYPE_PHONE,
|
||||
'secret' => Auth::hash($secret),
|
||||
'expire' => $expire,
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
@@ -3880,7 +3895,7 @@ App::put('/v1/account/verification/phone')
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$verifiedToken = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_PHONE, $secret);
|
||||
$verifiedToken = Auth::tokenVerify($user->getAttribute('tokens', []), TOKEN_TYPE_PHONE, $secret);
|
||||
|
||||
if (!$verifiedToken) {
|
||||
throw new Exception(Exception::USER_INVALID_TOKEN);
|
||||
@@ -4354,7 +4369,7 @@ App::post('/v1/account/mfa/challenge')
|
||||
->inject('plan')
|
||||
->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Locale $locale, Document $project, Request $request, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) {
|
||||
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
$expire = DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_CONFIRM);
|
||||
$code = Auth::codeGenerator();
|
||||
$challenge = new Document([
|
||||
'userId' => $user->getId(),
|
||||
|
||||
@@ -117,7 +117,7 @@ App::post('/v1/projects')
|
||||
'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT,
|
||||
'passwordHistory' => 0,
|
||||
'passwordDictionary' => false,
|
||||
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
'duration' => TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
'personalDataCheck' => false,
|
||||
'mockNumbers' => [],
|
||||
'sessionAlerts' => false,
|
||||
|
||||
@@ -451,7 +451,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
;
|
||||
$roles = array_keys(Config::getParam('roles', []));
|
||||
array_filter($roles, function ($role) {
|
||||
return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]);
|
||||
return !in_array($role, [USER_ROLE_APPS, USER_ROLE_GUESTS, USER_ROLE_USERS]);
|
||||
});
|
||||
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}
|
||||
@@ -1038,7 +1038,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
;
|
||||
$roles = array_keys(Config::getParam('roles', []));
|
||||
array_filter($roles, function ($role) {
|
||||
return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]);
|
||||
return !in_array($role, [USER_ROLE_APPS, USER_ROLE_GUESTS, USER_ROLE_USERS]);
|
||||
});
|
||||
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}
|
||||
@@ -1184,7 +1184,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
$authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$authDuration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$expire = DateTime::addSeconds(new \DateTime(), $authDuration);
|
||||
$secret = Auth::tokenGenerator();
|
||||
$session = new Document(array_merge([
|
||||
@@ -1196,7 +1196,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'provider' => SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => $user->getAttribute('email'),
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
|
||||
@@ -2022,11 +2022,11 @@ App::post('/v1/users/:userId/sessions')
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION);
|
||||
$secret = Auth::tokenGenerator(TOKEN_LENGTH_SESSION);
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration));
|
||||
|
||||
$session = new Document(array_merge(
|
||||
@@ -2034,7 +2034,7 @@ App::post('/v1/users/:userId/sessions')
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_SERVER,
|
||||
'provider' => SESSION_PROVIDER_SERVER,
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
'factors' => ['server'],
|
||||
@@ -2099,7 +2099,7 @@ App::post('/v1/users/:userId/tokens')
|
||||
))
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('length', 6, new Range(4, 128), 'Token length in characters. The default length is 6 characters', true)
|
||||
->param('expire', Auth::TOKEN_EXPIRATION_GENERIC, new Range(60, Auth::TOKEN_EXPIRATION_LOGIN_LONG), 'Token expiration period in seconds. The default expiration is 15 minutes.', true)
|
||||
->param('expire', TOKEN_EXPIRATION_GENERIC, new Range(60, TOKEN_EXPIRATION_LOGIN_LONG), 'Token expiration period in seconds. The default expiration is 15 minutes.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
@@ -2118,7 +2118,7 @@ App::post('/v1/users/:userId/tokens')
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_GENERIC,
|
||||
'type' => TOKEN_TYPE_GENERIC,
|
||||
'secret' => Auth::hash($secret),
|
||||
'expire' => $expire,
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
|
||||
@@ -282,11 +282,11 @@ App::init()
|
||||
Authorization::setDefaultStatus(false);
|
||||
|
||||
// Handle special app role case
|
||||
if ($apiKey->getRole() === Auth::USER_ROLE_APPS) {
|
||||
if ($apiKey->getRole() === USER_ROLE_APPS) {
|
||||
$user = new Document([
|
||||
'$id' => '',
|
||||
'status' => true,
|
||||
'type' => Auth::ACTIVITY_TYPE_APP,
|
||||
'type' => ACTIVITY_TYPE_APP,
|
||||
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
|
||||
'password' => '',
|
||||
'name' => $apiKey->getName(),
|
||||
@@ -551,7 +551,7 @@ App::init()
|
||||
if (!$user->isEmpty()) {
|
||||
$userClone = clone $user;
|
||||
// $user doesn't support `type` and can cause unintended effects.
|
||||
$userClone->setAttribute('type', Auth::ACTIVITY_TYPE_USER);
|
||||
$userClone->setAttribute('type', ACTIVITY_TYPE_USER);
|
||||
$queueForAudits->setUser($userClone);
|
||||
}
|
||||
|
||||
@@ -773,7 +773,7 @@ App::shutdown()
|
||||
if (!$user->isEmpty()) {
|
||||
$userClone = clone $user;
|
||||
// $user doesn't support `type` and can cause unintended effects.
|
||||
$userClone->setAttribute('type', Auth::ACTIVITY_TYPE_USER);
|
||||
$userClone->setAttribute('type', ACTIVITY_TYPE_USER);
|
||||
$queueForAudits->setUser($userClone);
|
||||
} elseif ($queueForAudits->getUser() === null || $queueForAudits->getUser()->isEmpty()) {
|
||||
/**
|
||||
@@ -787,7 +787,7 @@ App::shutdown()
|
||||
$user = new Document([
|
||||
'$id' => '',
|
||||
'status' => true,
|
||||
'type' => Auth::ACTIVITY_TYPE_GUEST,
|
||||
'type' => ACTIVITY_TYPE_GUEST,
|
||||
'email' => 'guest.' . $project->getId() . '@service.' . $request->getHostname(),
|
||||
'password' => '',
|
||||
'name' => 'Guest',
|
||||
|
||||
@@ -20,7 +20,7 @@ App::init()
|
||||
$lastUpdate = $session->getAttribute('mfaUpdatedAt');
|
||||
if (!empty($lastUpdate)) {
|
||||
$now = DateTime::now();
|
||||
$maxAllowedDate = DateTime::addSeconds(new \DateTime($lastUpdate), Auth::MFA_RECENT_DURATION); // Maximum date until session is considered safe before asking for another challenge
|
||||
$maxAllowedDate = DateTime::addSeconds(new \DateTime($lastUpdate), MFA_RECENT_DURATION); // Maximum date until session is considered safe before asking for another challenge
|
||||
|
||||
$isSessionFresh = DateTime::formatTz($maxAllowedDate) >= DateTime::formatTz($now);
|
||||
}
|
||||
|
||||
+66
-1
@@ -72,6 +72,72 @@ const APP_PLATFORM_SERVER = 'server';
|
||||
const APP_PLATFORM_CLIENT = 'client';
|
||||
const APP_PLATFORM_CONSOLE = 'console';
|
||||
|
||||
// User Roles
|
||||
const USER_ROLE_ANY = 'any';
|
||||
const USER_ROLE_GUESTS = 'guests';
|
||||
const USER_ROLE_USERS = 'users';
|
||||
const USER_ROLE_ADMIN = 'admin';
|
||||
const USER_ROLE_DEVELOPER = 'developer';
|
||||
const USER_ROLE_OWNER = 'owner';
|
||||
const USER_ROLE_APPS = 'apps';
|
||||
const USER_ROLE_SYSTEM = 'system';
|
||||
|
||||
/**
|
||||
* Token Expiration times.
|
||||
*/
|
||||
const TOKEN_EXPIRATION_LOGIN_LONG = 31536000; /* 1 year */
|
||||
const TOKEN_EXPIRATION_LOGIN_SHORT = 3600; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_CONFIRM = 3600 * 1; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_OTP = 60 * 15; /* 15 minutes */
|
||||
const TOKEN_EXPIRATION_GENERIC = 60 * 15; /* 15 minutes */
|
||||
|
||||
/**
|
||||
* Token Lengths.
|
||||
*/
|
||||
const TOKEN_LENGTH_MAGIC_URL = 64;
|
||||
const TOKEN_LENGTH_VERIFICATION = 256;
|
||||
const TOKEN_LENGTH_RECOVERY = 256;
|
||||
const TOKEN_LENGTH_OAUTH2 = 64;
|
||||
const TOKEN_LENGTH_SESSION = 256;
|
||||
|
||||
/**
|
||||
* Token Types.
|
||||
*/
|
||||
const TOKEN_TYPE_LOGIN = 1; // Deprecated
|
||||
const TOKEN_TYPE_VERIFICATION = 2;
|
||||
const TOKEN_TYPE_RECOVERY = 3;
|
||||
const TOKEN_TYPE_INVITE = 4;
|
||||
const TOKEN_TYPE_MAGIC_URL = 5;
|
||||
const TOKEN_TYPE_PHONE = 6;
|
||||
const TOKEN_TYPE_OAUTH2 = 7;
|
||||
const TOKEN_TYPE_GENERIC = 8;
|
||||
const TOKEN_TYPE_EMAIL = 9; // OTP
|
||||
|
||||
/**
|
||||
* Session Providers.
|
||||
*/
|
||||
const SESSION_PROVIDER_EMAIL = 'email';
|
||||
const SESSION_PROVIDER_ANONYMOUS = 'anonymous';
|
||||
const SESSION_PROVIDER_MAGIC_URL = 'magic-url';
|
||||
const SESSION_PROVIDER_PHONE = 'phone';
|
||||
const SESSION_PROVIDER_OAUTH2 = 'oauth2';
|
||||
const SESSION_PROVIDER_TOKEN = 'token';
|
||||
const SESSION_PROVIDER_SERVER = 'server';
|
||||
|
||||
/**
|
||||
* Activity associated with user or the app.
|
||||
*/
|
||||
const ACTIVITY_TYPE_APP = 'app';
|
||||
const ACTIVITY_TYPE_USER = 'user';
|
||||
const ACTIVITY_TYPE_GUEST = 'guest';
|
||||
|
||||
/**
|
||||
* MFA
|
||||
*/
|
||||
const MFA_RECENT_DURATION = 1800; // 30 mins
|
||||
|
||||
|
||||
// Database Reconnect
|
||||
const DATABASE_RECONNECT_SLEEP = 2;
|
||||
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
|
||||
@@ -244,7 +310,6 @@ const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId
|
||||
const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
|
||||
|
||||
// Resource types
|
||||
|
||||
const RESOURCE_TYPE_PROJECTS = 'projects';
|
||||
const RESOURCE_TYPE_FUNCTIONS = 'functions';
|
||||
const RESOURCE_TYPE_SITES = 'sites';
|
||||
|
||||
Generated
+17
-17
@@ -3512,12 +3512,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/auth.git",
|
||||
"reference": "966fbfefb27be94e3363f07279787d5cf8a66b95"
|
||||
"reference": "ed49b9e481030ba5e589140b41a9f4be1486310f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/auth/zipball/966fbfefb27be94e3363f07279787d5cf8a66b95",
|
||||
"reference": "966fbfefb27be94e3363f07279787d5cf8a66b95",
|
||||
"url": "https://api.github.com/repos/utopia-php/auth/zipball/ed49b9e481030ba5e589140b41a9f4be1486310f",
|
||||
"reference": "ed49b9e481030ba5e589140b41a9f4be1486310f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3559,7 +3559,7 @@
|
||||
"issues": "https://github.com/utopia-php/auth/issues",
|
||||
"source": "https://github.com/utopia-php/auth/tree/dev"
|
||||
},
|
||||
"time": "2025-03-16T18:32:00+00:00"
|
||||
"time": "2025-03-17T19:57:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/cache",
|
||||
@@ -4860,16 +4860,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/telemetry",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/telemetry.git",
|
||||
"reference": "d35f2f0632f4ee0be63fb7ace6a94a6adda71a80"
|
||||
"reference": "437f0021777f0e575dfb9e8a1a081b3aed75e33f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/telemetry/zipball/d35f2f0632f4ee0be63fb7ace6a94a6adda71a80",
|
||||
"reference": "d35f2f0632f4ee0be63fb7ace6a94a6adda71a80",
|
||||
"url": "https://api.github.com/repos/utopia-php/telemetry/zipball/437f0021777f0e575dfb9e8a1a081b3aed75e33f",
|
||||
"reference": "437f0021777f0e575dfb9e8a1a081b3aed75e33f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4890,7 +4890,7 @@
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Utopia\\": "src/"
|
||||
"Utopia\\Telemetry\\": "src/Telemetry"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -4904,9 +4904,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/telemetry/issues",
|
||||
"source": "https://github.com/utopia-php/telemetry/tree/0.1.0"
|
||||
"source": "https://github.com/utopia-php/telemetry/tree/0.1.1"
|
||||
},
|
||||
"time": "2024-11-13T10:29:53+00:00"
|
||||
"time": "2025-03-17T11:57:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/vcs",
|
||||
@@ -5143,16 +5143,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "0.40.7",
|
||||
"version": "0.40.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "9e89b0bc4d8e6c81817d27096629f34a149fa873"
|
||||
"reference": "dbb45a5db22cdc3368fe2573c07ba6088f188fa4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9e89b0bc4d8e6c81817d27096629f34a149fa873",
|
||||
"reference": "9e89b0bc4d8e6c81817d27096629f34a149fa873",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/dbb45a5db22cdc3368fe2573c07ba6088f188fa4",
|
||||
"reference": "dbb45a5db22cdc3368fe2573c07ba6088f188fa4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5188,9 +5188,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.40.7"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.40.9"
|
||||
},
|
||||
"time": "2025-03-12T08:43:55+00:00"
|
||||
"time": "2025-03-17T18:39:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
|
||||
+15
-202
@@ -2,13 +2,6 @@
|
||||
|
||||
namespace Appwrite\Auth;
|
||||
|
||||
use Appwrite\Auth\Hash\Argon2;
|
||||
use Appwrite\Auth\Hash\Bcrypt;
|
||||
use Appwrite\Auth\Hash\Md5;
|
||||
use Appwrite\Auth\Hash\Phpass;
|
||||
use Appwrite\Auth\Hash\Scrypt;
|
||||
use Appwrite\Auth\Hash\Scryptmodified;
|
||||
use Appwrite\Auth\Hash\Sha;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
@@ -17,87 +10,6 @@ use Utopia\Database\Validator\Roles;
|
||||
|
||||
class Auth
|
||||
{
|
||||
public const SUPPORTED_ALGOS = [
|
||||
'argon2',
|
||||
'bcrypt',
|
||||
'md5',
|
||||
'sha',
|
||||
'phpass',
|
||||
'scrypt',
|
||||
'scryptMod',
|
||||
'plaintext'
|
||||
];
|
||||
|
||||
public const DEFAULT_ALGO = 'argon2';
|
||||
public const DEFAULT_ALGO_OPTIONS = ['type' => 'argon2', 'memoryCost' => 2048, 'timeCost' => 4, 'threads' => 3];
|
||||
|
||||
/**
|
||||
* User Roles.
|
||||
*/
|
||||
public const USER_ROLE_ANY = 'any';
|
||||
public const USER_ROLE_GUESTS = 'guests';
|
||||
public const USER_ROLE_USERS = 'users';
|
||||
public const USER_ROLE_ADMIN = 'admin';
|
||||
public const USER_ROLE_DEVELOPER = 'developer';
|
||||
public const USER_ROLE_OWNER = 'owner';
|
||||
public const USER_ROLE_APPS = 'apps';
|
||||
public const USER_ROLE_SYSTEM = 'system';
|
||||
|
||||
/**
|
||||
* Activity associated with user or the app.
|
||||
*/
|
||||
public const ACTIVITY_TYPE_APP = 'app';
|
||||
public const ACTIVITY_TYPE_USER = 'user';
|
||||
public const ACTIVITY_TYPE_GUEST = 'guest';
|
||||
|
||||
/**
|
||||
* Token Types.
|
||||
*/
|
||||
public const TOKEN_TYPE_LOGIN = 1; // Deprecated
|
||||
public const TOKEN_TYPE_VERIFICATION = 2;
|
||||
public const TOKEN_TYPE_RECOVERY = 3;
|
||||
public const TOKEN_TYPE_INVITE = 4;
|
||||
public const TOKEN_TYPE_MAGIC_URL = 5;
|
||||
public const TOKEN_TYPE_PHONE = 6;
|
||||
public const TOKEN_TYPE_OAUTH2 = 7;
|
||||
public const TOKEN_TYPE_GENERIC = 8;
|
||||
public const TOKEN_TYPE_EMAIL = 9; // OTP
|
||||
|
||||
/**
|
||||
* Session Providers.
|
||||
*/
|
||||
public const SESSION_PROVIDER_EMAIL = 'email';
|
||||
public const SESSION_PROVIDER_ANONYMOUS = 'anonymous';
|
||||
public const SESSION_PROVIDER_MAGIC_URL = 'magic-url';
|
||||
public const SESSION_PROVIDER_PHONE = 'phone';
|
||||
public const SESSION_PROVIDER_OAUTH2 = 'oauth2';
|
||||
public const SESSION_PROVIDER_TOKEN = 'token';
|
||||
public const SESSION_PROVIDER_SERVER = 'server';
|
||||
|
||||
/**
|
||||
* Token Expiration times.
|
||||
*/
|
||||
public const TOKEN_EXPIRATION_LOGIN_LONG = 31536000; /* 1 year */
|
||||
public const TOKEN_EXPIRATION_LOGIN_SHORT = 3600; /* 1 hour */
|
||||
public const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */
|
||||
public const TOKEN_EXPIRATION_CONFIRM = 3600 * 1; /* 1 hour */
|
||||
public const TOKEN_EXPIRATION_OTP = 60 * 15; /* 15 minutes */
|
||||
public const TOKEN_EXPIRATION_GENERIC = 60 * 15; /* 15 minutes */
|
||||
|
||||
/**
|
||||
* Token Lengths.
|
||||
*/
|
||||
public const TOKEN_LENGTH_MAGIC_URL = 64;
|
||||
public const TOKEN_LENGTH_VERIFICATION = 256;
|
||||
public const TOKEN_LENGTH_RECOVERY = 256;
|
||||
public const TOKEN_LENGTH_OAUTH2 = 64;
|
||||
public const TOKEN_LENGTH_SESSION = 256;
|
||||
|
||||
/**
|
||||
* MFA
|
||||
*/
|
||||
public const MFA_RECENT_DURATION = 1800; // 30 mins
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@@ -109,18 +21,18 @@ class Auth
|
||||
public static function getSessionProviderByTokenType(int $type): string
|
||||
{
|
||||
switch ($type) {
|
||||
case Auth::TOKEN_TYPE_VERIFICATION:
|
||||
case Auth::TOKEN_TYPE_RECOVERY:
|
||||
case Auth::TOKEN_TYPE_INVITE:
|
||||
return Auth::SESSION_PROVIDER_EMAIL;
|
||||
case Auth::TOKEN_TYPE_MAGIC_URL:
|
||||
return Auth::SESSION_PROVIDER_MAGIC_URL;
|
||||
case Auth::TOKEN_TYPE_PHONE:
|
||||
return Auth::SESSION_PROVIDER_PHONE;
|
||||
case Auth::TOKEN_TYPE_OAUTH2:
|
||||
return Auth::SESSION_PROVIDER_OAUTH2;
|
||||
case TOKEN_TYPE_VERIFICATION:
|
||||
case TOKEN_TYPE_RECOVERY:
|
||||
case TOKEN_TYPE_INVITE:
|
||||
return SESSION_PROVIDER_EMAIL;
|
||||
case TOKEN_TYPE_MAGIC_URL:
|
||||
return SESSION_PROVIDER_MAGIC_URL;
|
||||
case TOKEN_TYPE_PHONE:
|
||||
return SESSION_PROVIDER_PHONE;
|
||||
case TOKEN_TYPE_OAUTH2:
|
||||
return SESSION_PROVIDER_OAUTH2;
|
||||
default:
|
||||
return Auth::SESSION_PROVIDER_TOKEN;
|
||||
return SESSION_PROVIDER_TOKEN;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,105 +50,6 @@ class Auth
|
||||
return \hash('sha256', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Password Hash.
|
||||
*
|
||||
* One way string hashing for user passwords
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $algo hashing algorithm to use
|
||||
* @param array $options algo-specific options
|
||||
*
|
||||
* @return bool|string|null
|
||||
*/
|
||||
public static function passwordHash(string $string, string $algo, array $options = [])
|
||||
{
|
||||
// Plain text not supported, just an alias. Switch to recommended algo
|
||||
if ($algo === 'plaintext') {
|
||||
$algo = Auth::DEFAULT_ALGO;
|
||||
$options = Auth::DEFAULT_ALGO_OPTIONS;
|
||||
}
|
||||
|
||||
if (!\in_array($algo, Auth::SUPPORTED_ALGOS)) {
|
||||
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
|
||||
}
|
||||
|
||||
switch ($algo) {
|
||||
case 'argon2':
|
||||
$hasher = new Argon2($options);
|
||||
return $hasher->hash($string);
|
||||
case 'bcrypt':
|
||||
$hasher = new Bcrypt($options);
|
||||
return $hasher->hash($string);
|
||||
case 'md5':
|
||||
$hasher = new Md5($options);
|
||||
return $hasher->hash($string);
|
||||
case 'sha':
|
||||
$hasher = new Sha($options);
|
||||
return $hasher->hash($string);
|
||||
case 'phpass':
|
||||
$hasher = new Phpass($options);
|
||||
return $hasher->hash($string);
|
||||
case 'scrypt':
|
||||
$hasher = new Scrypt($options);
|
||||
return $hasher->hash($string);
|
||||
case 'scryptMod':
|
||||
$hasher = new Scryptmodified($options);
|
||||
return $hasher->hash($string);
|
||||
default:
|
||||
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Password verify.
|
||||
*
|
||||
* @param string $plain
|
||||
* @param string $hash
|
||||
* @param string $algo hashing algorithm used to hash
|
||||
* @param array $options algo-specific options
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function passwordVerify(string $plain, string $hash, string $algo, array $options = [])
|
||||
{
|
||||
// Plain text not supported, just an alias. Switch to recommended algo
|
||||
if ($algo === 'plaintext') {
|
||||
$algo = Auth::DEFAULT_ALGO;
|
||||
$options = Auth::DEFAULT_ALGO_OPTIONS;
|
||||
}
|
||||
|
||||
if (!\in_array($algo, Auth::SUPPORTED_ALGOS)) {
|
||||
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
|
||||
}
|
||||
|
||||
switch ($algo) {
|
||||
case 'argon2':
|
||||
$hasher = new Argon2($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
case 'bcrypt':
|
||||
$hasher = new Bcrypt($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
case 'md5':
|
||||
$hasher = new Md5($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
case 'sha':
|
||||
$hasher = new Sha($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
case 'phpass':
|
||||
$hasher = new Phpass($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
case 'scrypt':
|
||||
$hasher = new Scrypt($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
case 'scryptMod':
|
||||
$hasher = new Scryptmodified($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
default:
|
||||
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Token Generator.
|
||||
*
|
||||
@@ -339,9 +152,9 @@ class Auth
|
||||
public static function isPrivilegedUser(array $roles): bool
|
||||
{
|
||||
if (
|
||||
in_array(self::USER_ROLE_OWNER, $roles) ||
|
||||
in_array(self::USER_ROLE_DEVELOPER, $roles) ||
|
||||
in_array(self::USER_ROLE_ADMIN, $roles)
|
||||
in_array(USER_ROLE_OWNER, $roles) ||
|
||||
in_array(USER_ROLE_DEVELOPER, $roles) ||
|
||||
in_array(USER_ROLE_ADMIN, $roles)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -358,7 +171,7 @@ class Auth
|
||||
*/
|
||||
public static function isAppUser(array $roles): bool
|
||||
{
|
||||
if (in_array(self::USER_ROLE_APPS, $roles)) {
|
||||
if (in_array(USER_ROLE_APPS, $roles)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth;
|
||||
|
||||
abstract class Hash
|
||||
{
|
||||
/**
|
||||
* @var array $options Hashing-algo specific options
|
||||
*/
|
||||
protected array $options = [];
|
||||
|
||||
/**
|
||||
* @param array $options Hashing-algo specific options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->setOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set hashing algo options
|
||||
*
|
||||
* @param array $options Hashing-algo specific options
|
||||
*/
|
||||
public function setOptions(array $options): self
|
||||
{
|
||||
$this->options = \array_merge([], $this->getDefaultOptions(), $options);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hashing algo options
|
||||
*
|
||||
* @return array $options Hashing-algo specific options
|
||||
*/
|
||||
public function getOptions(): array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password Input password to hash
|
||||
*
|
||||
* @return string hash
|
||||
*/
|
||||
abstract public function hash(string $password): string;
|
||||
|
||||
/**
|
||||
* @param string $password Input password to validate
|
||||
* @param string $hash Hash to verify password against
|
||||
*
|
||||
* @return boolean true if password matches hash
|
||||
*/
|
||||
abstract public function verify(string $password, string $hash): bool;
|
||||
|
||||
/**
|
||||
* Get default options for specific hashing algo
|
||||
*
|
||||
* @return array options named array
|
||||
*/
|
||||
abstract public function getDefaultOptions(): array;
|
||||
}
|
||||
@@ -104,16 +104,16 @@ class Key
|
||||
$secret = $key;
|
||||
}
|
||||
|
||||
$role = Auth::USER_ROLE_APPS;
|
||||
$role = USER_ROLE_APPS;
|
||||
$roles = Config::getParam('roles', []);
|
||||
$scopes = $roles[Auth::USER_ROLE_APPS]['scopes'] ?? [];
|
||||
$scopes = $roles[USER_ROLE_APPS]['scopes'] ?? [];
|
||||
$expired = false;
|
||||
|
||||
$guestKey = new Key(
|
||||
$project->getId(),
|
||||
$type,
|
||||
Auth::USER_ROLE_GUESTS,
|
||||
$roles[Auth::USER_ROLE_GUESTS]['scopes'] ?? [],
|
||||
USER_ROLE_GUESTS,
|
||||
$roles[USER_ROLE_GUESTS]['scopes'] ?? [],
|
||||
'UNKNOWN'
|
||||
);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Appwrite\Auth\Validator;
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Utopia\Auth\Proofs\Password as ProofsPassword;
|
||||
|
||||
/**
|
||||
* Password.
|
||||
@@ -45,8 +45,10 @@ class PasswordHistory extends Password
|
||||
*/
|
||||
public function isValid($value): bool
|
||||
{
|
||||
$proofForPassword = ProofsPassword::createHash($this->algo, $this->algoOptions);
|
||||
|
||||
foreach ($this->history as $hash) {
|
||||
if (!empty($hash) && Auth::passwordVerify($value, $hash, $this->algo, $this->algoOptions)) {
|
||||
if (!empty($hash) && $proofForPassword->verify($value, $hash)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ class V16 extends Migration
|
||||
* Set default authDuration
|
||||
*/
|
||||
$document->setAttribute('auths', array_merge($document->getAttribute('auths', []), [
|
||||
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG
|
||||
'duration' => TOKEN_EXPIRATION_LOGIN_LONG
|
||||
]));
|
||||
|
||||
/**
|
||||
|
||||
@@ -270,7 +270,7 @@ class V17 extends Migration
|
||||
* Set hashOptions type
|
||||
*/
|
||||
$document->setAttribute('hashOptions', array_merge($document->getAttribute('hashOptions', []), [
|
||||
'type' => $document->getAttribute('hash', Auth::DEFAULT_ALGO)
|
||||
'type' => $document->getAttribute('hash', 'argon2')
|
||||
]));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -632,15 +632,15 @@ class V20 extends Migration
|
||||
}
|
||||
break;
|
||||
case 'sessions':
|
||||
$duration = $this->project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$duration = $this->project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$expire = DateTime::addSeconds(new \DateTime(), $duration);
|
||||
$document->setAttribute('expire', $expire);
|
||||
|
||||
$factors = match ($document->getAttribute('provider')) {
|
||||
Auth::SESSION_PROVIDER_EMAIL => ['password'],
|
||||
Auth::SESSION_PROVIDER_PHONE => ['phone'],
|
||||
Auth::SESSION_PROVIDER_ANONYMOUS => ['anonymous'],
|
||||
Auth::SESSION_PROVIDER_TOKEN => ['token'],
|
||||
SESSION_PROVIDER_EMAIL => ['password'],
|
||||
SESSION_PROVIDER_PHONE => ['phone'],
|
||||
SESSION_PROVIDER_ANONYMOUS => ['anonymous'],
|
||||
SESSION_PROVIDER_TOKEN => ['token'],
|
||||
default => ['email'],
|
||||
};
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ class Audits extends Action
|
||||
|
||||
$userName = $user->getAttribute('name', '');
|
||||
$userEmail = $user->getAttribute('email', '');
|
||||
$userType = $user->getAttribute('type', Auth::ACTIVITY_TYPE_USER);
|
||||
$userType = $user->getAttribute('type', ACTIVITY_TYPE_USER);
|
||||
|
||||
// Create event data
|
||||
$eventData = [
|
||||
|
||||
@@ -716,7 +716,7 @@ class Deletes extends Action
|
||||
private function deleteExpiredSessions(Document $project, callable $getProjectDB): void
|
||||
{
|
||||
$dbForProject = $getProjectDB($project);
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$expired = DateTime::addSeconds(new \DateTime(), -1 * $duration);
|
||||
|
||||
// Delete Sessions
|
||||
|
||||
@@ -105,7 +105,7 @@ class Project extends Model
|
||||
->addRule('authDuration', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Session duration in seconds.',
|
||||
'default' => Auth::TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
'default' => TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
'example' => 60,
|
||||
])
|
||||
->addRule('authLimit', [
|
||||
@@ -359,7 +359,7 @@ class Project extends Model
|
||||
$auth = Config::getParam('auth', []);
|
||||
|
||||
$document->setAttribute('authLimit', $authValues['limit'] ?? 0);
|
||||
$document->setAttribute('authDuration', $authValues['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG);
|
||||
$document->setAttribute('authDuration', $authValues['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG);
|
||||
$document->setAttribute('authSessionsLimit', $authValues['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT);
|
||||
$document->setAttribute('authPasswordHistory', $authValues['passwordHistory'] ?? 0);
|
||||
$document->setAttribute('authPasswordDictionary', $authValues['passwordDictionary'] ?? false);
|
||||
|
||||
@@ -787,7 +787,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(Auth::TOKEN_EXPIRATION_LOGIN_LONG, $response['body']['authDuration']); // 1 Year
|
||||
$this->assertEquals(TOKEN_EXPIRATION_LOGIN_LONG, $response['body']['authDuration']); // 1 Year
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
@@ -931,7 +931,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
'duration' => TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
@@ -944,7 +944,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(Auth::TOKEN_EXPIRATION_LOGIN_LONG, $response['body']['authDuration']); // 1 Year
|
||||
$this->assertEquals(TOKEN_EXPIRATION_LOGIN_LONG, $response['body']['authDuration']); // 1 Year
|
||||
|
||||
return ['projectId' => $projectId];
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Tests\Unit\Auth;
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\Auth\Proofs\Password;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
@@ -37,7 +38,7 @@ class AuthTest extends TestCase
|
||||
// Bcrypt - Version Y
|
||||
$plain = 'secret';
|
||||
$hash = '$2y$08$PDbMtV18J1KOBI9tIYabBuyUwBrtXPGhLxCy9pWP6xkldVOKLrLKy';
|
||||
$generatedHash = Auth::passwordHash($plain, 'bcrypt');
|
||||
$generatedHash = Password::createHash('bcrypt')->hash($plain);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'bcrypt'));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'bcrypt'));
|
||||
$this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'bcrypt'));
|
||||
@@ -45,7 +46,7 @@ class AuthTest extends TestCase
|
||||
// Bcrypt - Version A
|
||||
$plain = 'test123';
|
||||
$hash = '$2a$12$3f2ZaARQ1AmhtQWx2nmQpuXcWfTj1YV2/Hl54e8uKxIzJe3IfwLiu';
|
||||
$generatedHash = Auth::passwordHash($plain, 'bcrypt');
|
||||
$generatedHash = Password::createHash('bcrypt')->hash($plain);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'bcrypt'));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'bcrypt'));
|
||||
$this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'bcrypt'));
|
||||
@@ -53,7 +54,7 @@ class AuthTest extends TestCase
|
||||
// Bcrypt - Cost 5
|
||||
$plain = 'hello-world';
|
||||
$hash = '$2a$05$IjrtSz6SN7UJ6Sh3l.b5jODEvEG2LMJTPAHIaLWRvlWx7if3VMkFO';
|
||||
$generatedHash = Auth::passwordHash($plain, 'bcrypt');
|
||||
$generatedHash = Password::createHash('bcrypt')->hash($plain);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'bcrypt'));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'bcrypt'));
|
||||
$this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'bcrypt'));
|
||||
@@ -61,7 +62,7 @@ class AuthTest extends TestCase
|
||||
// Bcrypt - Cost 15
|
||||
$plain = 'super-secret-password';
|
||||
$hash = '$2a$15$DS0ZzbsFZYumH/E4Qj5oeOHnBcM3nCCsCA2m4Goigat/0iMVQC4Na';
|
||||
$generatedHash = Auth::passwordHash($plain, 'bcrypt');
|
||||
$generatedHash = Password::createHash('bcrypt')->hash($plain);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'bcrypt'));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'bcrypt'));
|
||||
$this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'bcrypt'));
|
||||
@@ -69,7 +70,7 @@ class AuthTest extends TestCase
|
||||
// MD5 - Short
|
||||
$plain = 'appwrite';
|
||||
$hash = '144fa7eaa4904e8ee120651997f70dcc';
|
||||
$generatedHash = Auth::passwordHash($plain, 'md5');
|
||||
$generatedHash = Password::createHash('md5')->hash($plain);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'md5'));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'md5'));
|
||||
$this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'md5'));
|
||||
@@ -77,7 +78,7 @@ class AuthTest extends TestCase
|
||||
// MD5 - Long
|
||||
$plain = 'AppwriteIsAwesomeBackendAsAServiceThatIsAlsoOpenSourced';
|
||||
$hash = '8410e96cf7ac64e0b84c3f8517a82616';
|
||||
$generatedHash = Auth::passwordHash($plain, 'md5');
|
||||
$generatedHash = Password::createHash('md5')->hash($plain);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'md5'));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'md5'));
|
||||
$this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'md5'));
|
||||
@@ -85,7 +86,7 @@ class AuthTest extends TestCase
|
||||
// PHPass
|
||||
$plain = 'pass123';
|
||||
$hash = '$P$BVKPmJBZuLch27D4oiMRTEykGLQ9tX0';
|
||||
$generatedHash = Auth::passwordHash($plain, 'phpass');
|
||||
$generatedHash = Password::createHash('phpass')->hash($plain);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'phpass'));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'phpass'));
|
||||
$this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'phpass'));
|
||||
@@ -93,7 +94,7 @@ class AuthTest extends TestCase
|
||||
// SHA
|
||||
$plain = 'developersAreAwesome!';
|
||||
$hash = '2455118438cb125354b89bb5888346e9bd23355462c40df393fab514bf2220b5a08e4e2d7b85d7327595a450d0ac965cc6661152a46a157c66d681bed20a4735';
|
||||
$generatedHash = Auth::passwordHash($plain, 'sha');
|
||||
$generatedHash = Password::createHash('sha')->hash($plain);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'sha'));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'sha'));
|
||||
$this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'sha'));
|
||||
@@ -101,7 +102,7 @@ class AuthTest extends TestCase
|
||||
// Argon2
|
||||
$plain = 'safe-argon-password';
|
||||
$hash = '$argon2id$v=19$m=2048,t=3,p=4$MWc5NWRmc2QxZzU2$41mp7rSgBZ49YxLbbxIac7aRaxfp5/e1G45ckwnK0g8';
|
||||
$generatedHash = Auth::passwordHash($plain, 'argon2');
|
||||
$generatedHash = Password::createHash('argon2')->hash($plain);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'argon2'));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'argon2'));
|
||||
$this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'argon2'));
|
||||
@@ -109,7 +110,7 @@ class AuthTest extends TestCase
|
||||
// Scrypt
|
||||
$plain = 'some-scrypt-password';
|
||||
$hash = 'b448ad7ba88b653b5b56b8053a06806724932d0751988bc9cd0ef7ff059e8ba8a020e1913b7069a650d3f99a1559aba0221f2c277826919513a054e76e339028';
|
||||
$generatedHash = Auth::passwordHash($plain, 'scrypt', [ 'salt' => 'some-salt', 'length' => 64, 'costCpu' => 16384, 'costMemory' => 12, 'costParallel' => 2]);
|
||||
$generatedHash = Password::createHash('scrypt')->setOptions([ 'salt' => 'some-salt', 'length' => 64, 'costCpu' => 16384, 'costMemory' => 12, 'costParallel' => 2])->hash($plain);
|
||||
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'scrypt', [ 'salt' => 'some-salt', 'length' => 64, 'costCpu' => 16384, 'costMemory' => 12, 'costParallel' => 2]));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'scrypt', [ 'salt' => 'some-salt', 'length' => 64, 'costCpu' => 16384, 'costMemory' => 12, 'costParallel' => 2]));
|
||||
@@ -126,7 +127,7 @@ class AuthTest extends TestCase
|
||||
// Provider #1 (Database)
|
||||
$plain = 'example-password';
|
||||
$hash = '$2a$10$3bIGRWUes86CICsuchGLj.e.BqdCdg2/1Ud9LvBhJr0j7Dze8PBdS';
|
||||
$generatedHash = Auth::passwordHash($plain, 'bcrypt');
|
||||
$generatedHash = Password::createHash('bcrypt')->hash($plain);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'bcrypt'));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'bcrypt'));
|
||||
$this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'bcrypt'));
|
||||
@@ -134,7 +135,7 @@ class AuthTest extends TestCase
|
||||
// Provider #2 (Blog)
|
||||
$plain = 'your-password';
|
||||
$hash = '$P$BkiNDJTpAWXtpaMhEUhUdrv7M0I1g6.';
|
||||
$generatedHash = Auth::passwordHash($plain, 'phpass');
|
||||
$generatedHash = Password::createHash('phpass')->hash($plain);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'phpass'));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'phpass'));
|
||||
$this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'phpass'));
|
||||
@@ -147,7 +148,7 @@ class AuthTest extends TestCase
|
||||
$signerKey = 'XyEKE9RcTDeLEsL/RjwPDBv/RqDl8fb3gpYEOQaPihbxf1ZAtSOHCjuAAa7Q3oHpCYhXSN9tizHgVOwn6krflQ==';
|
||||
|
||||
$options = [ 'salt' => $salt, 'saltSeparator' => $saltSeparator, 'signerKey' => $signerKey ];
|
||||
$generatedHash = Auth::passwordHash($plain, 'scryptMod', $options);
|
||||
$generatedHash = Password::createHash('scryptMod')->hash($plain, $options);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'scryptMod', $options));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'scryptMod', $options));
|
||||
$this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'scryptMod', $options));
|
||||
@@ -159,7 +160,7 @@ class AuthTest extends TestCase
|
||||
|
||||
// Bcrypt - Cost 5
|
||||
$plain = 'whatIsMd8?!?';
|
||||
$generatedHash = Auth::passwordHash($plain, 'md8');
|
||||
$generatedHash = Password::createHash('md8')->hash($plain);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'md8'));
|
||||
}
|
||||
|
||||
@@ -187,14 +188,14 @@ class AuthTest extends TestCase
|
||||
new Document([
|
||||
'$id' => ID::custom('token1'),
|
||||
'secret' => $hash,
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'provider' => SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => 'test@example.com',
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), $expireTime1),
|
||||
]),
|
||||
new Document([
|
||||
'$id' => ID::custom('token2'),
|
||||
'secret' => 'secret2',
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'provider' => SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => 'test@example.com',
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), $expireTime1),
|
||||
]),
|
||||
@@ -206,14 +207,14 @@ class AuthTest extends TestCase
|
||||
new Document([ // Correct secret and type time, wrong expire time
|
||||
'$id' => ID::custom('token1'),
|
||||
'secret' => $hash,
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'provider' => SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => 'test@example.com',
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), $expireTime2),
|
||||
]),
|
||||
new Document([
|
||||
'$id' => ID::custom('token2'),
|
||||
'secret' => 'secret2',
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'provider' => SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => 'test@example.com',
|
||||
'expire' => DateTime::addSeconds(new \DateTime(), $expireTime2),
|
||||
]),
|
||||
@@ -232,13 +233,13 @@ class AuthTest extends TestCase
|
||||
$tokens1 = [
|
||||
new Document([
|
||||
'$id' => ID::custom('token1'),
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
'type' => TOKEN_TYPE_RECOVERY,
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), 60 * 60 * 24)),
|
||||
'secret' => $hash,
|
||||
]),
|
||||
new Document([
|
||||
'$id' => ID::custom('token2'),
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
'type' => TOKEN_TYPE_RECOVERY,
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)),
|
||||
'secret' => 'secret2',
|
||||
]),
|
||||
@@ -247,13 +248,13 @@ class AuthTest extends TestCase
|
||||
$tokens2 = [
|
||||
new Document([ // Correct secret and type time, wrong expire time
|
||||
'$id' => ID::custom('token1'),
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
'type' => TOKEN_TYPE_RECOVERY,
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)),
|
||||
'secret' => $hash,
|
||||
]),
|
||||
new Document([
|
||||
'$id' => ID::custom('token2'),
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
'type' => TOKEN_TYPE_RECOVERY,
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)),
|
||||
'secret' => 'secret2',
|
||||
]),
|
||||
@@ -262,25 +263,25 @@ class AuthTest extends TestCase
|
||||
$tokens3 = [ // Correct secret and expire time, wrong type
|
||||
new Document([
|
||||
'$id' => ID::custom('token1'),
|
||||
'type' => Auth::TOKEN_TYPE_INVITE,
|
||||
'type' => TOKEN_TYPE_INVITE,
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), 60 * 60 * 24)),
|
||||
'secret' => $hash,
|
||||
]),
|
||||
new Document([
|
||||
'$id' => ID::custom('token2'),
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
'type' => TOKEN_TYPE_RECOVERY,
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)),
|
||||
'secret' => 'secret2',
|
||||
]),
|
||||
];
|
||||
|
||||
$this->assertEquals(Auth::tokenVerify($tokens1, Auth::TOKEN_TYPE_RECOVERY, $secret), $tokens1[0]);
|
||||
$this->assertEquals(Auth::tokenVerify($tokens1, TOKEN_TYPE_RECOVERY, $secret), $tokens1[0]);
|
||||
$this->assertEquals(Auth::tokenVerify($tokens1, null, $secret), $tokens1[0]);
|
||||
$this->assertEquals(Auth::tokenVerify($tokens1, Auth::TOKEN_TYPE_RECOVERY, 'false-secret'), false);
|
||||
$this->assertEquals(Auth::tokenVerify($tokens2, Auth::TOKEN_TYPE_RECOVERY, $secret), false);
|
||||
$this->assertEquals(Auth::tokenVerify($tokens2, Auth::TOKEN_TYPE_RECOVERY, 'false-secret'), false);
|
||||
$this->assertEquals(Auth::tokenVerify($tokens3, Auth::TOKEN_TYPE_RECOVERY, $secret), false);
|
||||
$this->assertEquals(Auth::tokenVerify($tokens3, Auth::TOKEN_TYPE_RECOVERY, 'false-secret'), false);
|
||||
$this->assertEquals(Auth::tokenVerify($tokens1, TOKEN_TYPE_RECOVERY, 'false-secret'), false);
|
||||
$this->assertEquals(Auth::tokenVerify($tokens2, TOKEN_TYPE_RECOVERY, $secret), false);
|
||||
$this->assertEquals(Auth::tokenVerify($tokens2, TOKEN_TYPE_RECOVERY, 'false-secret'), false);
|
||||
$this->assertEquals(Auth::tokenVerify($tokens3, TOKEN_TYPE_RECOVERY, $secret), false);
|
||||
$this->assertEquals(Auth::tokenVerify($tokens3, TOKEN_TYPE_RECOVERY, 'false-secret'), false);
|
||||
}
|
||||
|
||||
public function testIsPrivilegedUser(): void
|
||||
@@ -288,16 +289,16 @@ class AuthTest extends TestCase
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([Role::guests()->toString()]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([Role::users()->toString()]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_ADMIN]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_OWNER]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([Auth::USER_ROLE_APPS]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([Auth::USER_ROLE_SYSTEM]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([USER_ROLE_ADMIN]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([USER_ROLE_OWNER]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([USER_ROLE_APPS]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([USER_ROLE_SYSTEM]));
|
||||
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([Auth::USER_ROLE_APPS, Auth::USER_ROLE_APPS]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([Auth::USER_ROLE_APPS, Role::guests()->toString()]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_OWNER, Role::guests()->toString()]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_OWNER, Auth::USER_ROLE_ADMIN, Auth::USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([USER_ROLE_APPS, USER_ROLE_APPS]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([USER_ROLE_APPS, Role::guests()->toString()]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([USER_ROLE_OWNER, Role::guests()->toString()]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser([USER_ROLE_OWNER, USER_ROLE_ADMIN, USER_ROLE_DEVELOPER]));
|
||||
}
|
||||
|
||||
public function testIsAppUser(): void
|
||||
@@ -305,16 +306,16 @@ class AuthTest extends TestCase
|
||||
$this->assertEquals(false, Auth::isAppUser([]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Role::guests()->toString()]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Role::users()->toString()]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_ADMIN]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_OWNER]));
|
||||
$this->assertEquals(true, Auth::isAppUser([Auth::USER_ROLE_APPS]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_SYSTEM]));
|
||||
$this->assertEquals(false, Auth::isAppUser([USER_ROLE_ADMIN]));
|
||||
$this->assertEquals(false, Auth::isAppUser([USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(false, Auth::isAppUser([USER_ROLE_OWNER]));
|
||||
$this->assertEquals(true, Auth::isAppUser([USER_ROLE_APPS]));
|
||||
$this->assertEquals(false, Auth::isAppUser([USER_ROLE_SYSTEM]));
|
||||
|
||||
$this->assertEquals(true, Auth::isAppUser([Auth::USER_ROLE_APPS, Auth::USER_ROLE_APPS]));
|
||||
$this->assertEquals(true, Auth::isAppUser([Auth::USER_ROLE_APPS, Role::guests()->toString()]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_OWNER, Role::guests()->toString()]));
|
||||
$this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_OWNER, Auth::USER_ROLE_ADMIN, Auth::USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(true, Auth::isAppUser([USER_ROLE_APPS, USER_ROLE_APPS]));
|
||||
$this->assertEquals(true, Auth::isAppUser([USER_ROLE_APPS, Role::guests()->toString()]));
|
||||
$this->assertEquals(false, Auth::isAppUser([USER_ROLE_OWNER, Role::guests()->toString()]));
|
||||
$this->assertEquals(false, Auth::isAppUser([USER_ROLE_OWNER, USER_ROLE_ADMIN, USER_ROLE_DEVELOPER]));
|
||||
}
|
||||
|
||||
public function testGuestRoles(): void
|
||||
@@ -394,7 +395,7 @@ class AuthTest extends TestCase
|
||||
|
||||
public function testPrivilegedUserRoles(): void
|
||||
{
|
||||
Authorization::setRole(Auth::USER_ROLE_OWNER);
|
||||
Authorization::setRole(USER_ROLE_OWNER);
|
||||
$user = new Document([
|
||||
'$id' => ID::custom('123'),
|
||||
'emailVerification' => true,
|
||||
@@ -438,7 +439,7 @@ class AuthTest extends TestCase
|
||||
|
||||
public function testAppUserRoles(): void
|
||||
{
|
||||
Authorization::setRole(Auth::USER_ROLE_APPS);
|
||||
Authorization::setRole(USER_ROLE_APPS);
|
||||
$user = new Document([
|
||||
'$id' => ID::custom('123'),
|
||||
'memberships' => [
|
||||
|
||||
@@ -21,7 +21,7 @@ class KeyTest extends TestCase
|
||||
'collections.read',
|
||||
'documents.read',
|
||||
];
|
||||
$roleScopes = Config::getParam('roles', [])[Auth::USER_ROLE_APPS]['scopes'];
|
||||
$roleScopes = Config::getParam('roles', [])[USER_ROLE_APPS]['scopes'];
|
||||
|
||||
$key = static::generateKey($projectId, $usage, $scopes);
|
||||
$project = new Document(['$id' => $projectId,]);
|
||||
@@ -29,7 +29,7 @@ class KeyTest extends TestCase
|
||||
|
||||
$this->assertEquals($projectId, $decoded->getProjectId());
|
||||
$this->assertEquals(API_KEY_DYNAMIC, $decoded->getType());
|
||||
$this->assertEquals(Auth::USER_ROLE_APPS, $decoded->getRole());
|
||||
$this->assertEquals(USER_ROLE_APPS, $decoded->getRole());
|
||||
$this->assertEquals(\array_merge($scopes, $roleScopes), $decoded->getScopes());
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class MessagingChannelsTest extends TestCase
|
||||
'confirm' => true,
|
||||
'roles' => [
|
||||
empty($index % 2)
|
||||
? Auth::USER_ROLE_ADMIN
|
||||
? USER_ROLE_ADMIN
|
||||
: 'member',
|
||||
]
|
||||
]
|
||||
@@ -294,7 +294,7 @@ class MessagingChannelsTest extends TestCase
|
||||
}
|
||||
|
||||
$role = empty($index % 2)
|
||||
? Auth::USER_ROLE_ADMIN
|
||||
? USER_ROLE_ADMIN
|
||||
: 'member';
|
||||
|
||||
$permissions = [
|
||||
|
||||
Reference in New Issue
Block a user