Implement user tokens subcollection
This commit is contained in:
parent
63538bb0a1
commit
50082c3512
|
@ -1065,9 +1065,9 @@ $collections = [
|
|||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => [],
|
||||
'array' => true,
|
||||
'filters' => ['json'],
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['subQueryTokens'],
|
||||
],
|
||||
[
|
||||
'$id' => 'memberships',
|
||||
|
@ -1128,6 +1128,89 @@ $collections = [
|
|||
],
|
||||
],
|
||||
|
||||
'tokens' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'tokens',
|
||||
'name' => 'Tokens',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'userId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'type',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'secret',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 512, // https://www.tutorialspoint.com/how-long-is-the-sha256-hash-in-mysql (512 for encryption)
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
[
|
||||
'$id' => 'expire',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'userAgent',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'ip',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 45, // https://stackoverflow.com/a/166157/2299554
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
]
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_user',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['userId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'sessions' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'sessions',
|
||||
|
|
|
@ -105,7 +105,7 @@ App::post('/v1/account')
|
|||
'name' => $name,
|
||||
'prefs' => new \stdClass(),
|
||||
'sessions' => [],
|
||||
'tokens' => [],
|
||||
'tokens' => null,
|
||||
'memberships' => [],
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'deleted' => false
|
||||
|
@ -506,7 +506,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
'name' => $name,
|
||||
'prefs' => new \stdClass(),
|
||||
'sessions' => [],
|
||||
'tokens' => [],
|
||||
'tokens' => null,
|
||||
'memberships' => [],
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'deleted' => false
|
||||
|
@ -680,7 +680,7 @@ App::post('/v1/account/sessions/magic-url')
|
|||
'reset' => false,
|
||||
'prefs' => new \stdClass(),
|
||||
'sessions' => [],
|
||||
'tokens' => [],
|
||||
'tokens' => null,
|
||||
'memberships' => [],
|
||||
'search' => implode(' ', [$userId, $email]),
|
||||
'deleted' => false
|
||||
|
@ -706,13 +706,12 @@ App::post('/v1/account/sessions/magic-url')
|
|||
|
||||
Authorization::setRole('user:'.$user->getId());
|
||||
|
||||
$user->setAttribute('tokens', $token, Document::SET_TYPE_APPEND);
|
||||
$token = $dbForProject->createDocument('tokens', $token
|
||||
->setAttribute('$read', ['user:'.$user->getId()])
|
||||
->setAttribute('$write', ['user:'.$user->getId()])
|
||||
);
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed to save user to DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
if(empty($url)) {
|
||||
$url = $request->getProtocol().'://'.$request->getHostname().'/auth/magic-url';
|
||||
|
@ -786,7 +785,7 @@ App::put('/v1/account/sessions/magic-url')
|
|||
/** @var MaxMind\Db\Reader $geodb */
|
||||
/** @var Appwrite\Event\Event $audits */
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
$user = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId));
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
|
@ -825,24 +824,12 @@ App::put('/v1/account/sessions/magic-url')
|
|||
->setAttribute('$write', ['user:' . $user->getId()])
|
||||
);
|
||||
|
||||
$tokens = $user->getAttribute('tokens', []);
|
||||
|
||||
/**
|
||||
* We act like we're updating and validating
|
||||
* the recovery token but actually we don't need it anymore.
|
||||
*/
|
||||
foreach ($tokens as $key => $singleToken) {
|
||||
if ($token === $singleToken->getId()) {
|
||||
unset($tokens[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$user
|
||||
->setAttribute('sessions', $session, Document::SET_TYPE_APPEND)
|
||||
->setAttribute('tokens', $tokens);
|
||||
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$dbForProject->deleteDocument('tokens', $token);
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
|
@ -952,7 +939,7 @@ App::post('/v1/account/sessions/anonymous')
|
|||
'name' => null,
|
||||
'prefs' => new \stdClass(),
|
||||
'sessions' => [],
|
||||
'tokens' => [],
|
||||
'tokens' => null,
|
||||
'memberships' => [],
|
||||
'search' => $userId,
|
||||
'deleted' => false
|
||||
|
@ -1906,9 +1893,12 @@ App::post('/v1/account/recovery')
|
|||
|
||||
Authorization::setRole('user:' . $profile->getId());
|
||||
|
||||
$profile->setAttribute('tokens', $recovery, Document::SET_TYPE_APPEND);
|
||||
$recovery = $dbForProject->createDocument('tokens', $recovery
|
||||
->setAttribute('$read', ['user:'.$profile->getId()])
|
||||
->setAttribute('$write', ['user:'.$profile->getId()])
|
||||
);
|
||||
|
||||
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile);
|
||||
$dbForProject->deleteCachedDocument('users', $profile->getId());
|
||||
|
||||
$url = Template::parseURL($url);
|
||||
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $profile->getId(), 'secret' => $secret, 'expire' => $expire]);
|
||||
|
@ -2003,18 +1993,14 @@ App::put('/v1/account/recovery')
|
|||
->setAttribute('emailVerification', true)
|
||||
);
|
||||
|
||||
$recoveryDocument = $dbForProject->getDocument('tokens', $recovery);
|
||||
|
||||
/**
|
||||
* We act like we're updating and validating
|
||||
* the recovery token but actually we don't need it anymore.
|
||||
*/
|
||||
foreach ($tokens as $key => $token) {
|
||||
if ($recovery === $token->getId()) {
|
||||
$recovery = $token;
|
||||
unset($tokens[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('tokens', $tokens));
|
||||
$dbForProject->deleteDocument('tokens', $recovery);
|
||||
$dbForProject->deleteCachedDocument('users', $profile->getId());
|
||||
|
||||
$audits
|
||||
->setParam('userId', $profile->getId())
|
||||
|
@ -2025,7 +2011,7 @@ App::put('/v1/account/recovery')
|
|||
$usage
|
||||
->setParam('users.update', 1)
|
||||
;
|
||||
$response->dynamic($recovery, Response::MODEL_TOKEN);
|
||||
$response->dynamic($recoveryDocument, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
App::post('/v1/account/verification')
|
||||
|
@ -2089,9 +2075,12 @@ App::post('/v1/account/verification')
|
|||
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
|
||||
$user->setAttribute('tokens', $verification, Document::SET_TYPE_APPEND);
|
||||
$verification = $dbForProject->createDocument('tokens', $verification
|
||||
->setAttribute('$read', ['user:'.$user->getId()])
|
||||
->setAttribute('$write', ['user:'.$user->getId()])
|
||||
);
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
$url = Template::parseURL($url);
|
||||
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $user->getId(), 'secret' => $verificationSecret, 'expire' => $expire]);
|
||||
|
@ -2161,7 +2150,7 @@ App::put('/v1/account/verification')
|
|||
/** @var Appwrite\Event\Event $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
||||
$profile = $dbForProject->getDocument('users', $userId);
|
||||
$profile = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId));
|
||||
|
||||
if ($profile->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
|
@ -2177,19 +2166,15 @@ App::put('/v1/account/verification')
|
|||
Authorization::setRole('user:' . $profile->getId());
|
||||
|
||||
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true));
|
||||
|
||||
$verificationDocument = $dbForProject->getDocument('tokens', $verification);
|
||||
|
||||
/**
|
||||
* We act like we're updating and validating
|
||||
* the verification token but actually we don't need it anymore.
|
||||
*/
|
||||
foreach ($tokens as $key => $token) {
|
||||
if ($token->getId() === $verification) {
|
||||
$verification = $token;
|
||||
unset($tokens[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('tokens', $tokens));
|
||||
$dbForProject->deleteDocument('tokens', $verification);
|
||||
$dbForProject->deleteCachedDocument('users', $profile->getId());
|
||||
|
||||
$audits
|
||||
->setParam('userId', $profile->getId())
|
||||
|
@ -2200,5 +2185,5 @@ App::put('/v1/account/verification')
|
|||
$usage
|
||||
->setParam('users.update', 1)
|
||||
;
|
||||
$response->dynamic($verification, Response::MODEL_TOKEN);
|
||||
$response->dynamic($verificationDocument, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
|
|
@ -339,7 +339,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
'name' => $name,
|
||||
'prefs' => new \stdClass(),
|
||||
'sessions' => [],
|
||||
'tokens' => [],
|
||||
'tokens' => null,
|
||||
'memberships' => [],
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
])));
|
||||
|
|
|
@ -64,7 +64,7 @@ App::post('/v1/users')
|
|||
'name' => $name,
|
||||
'prefs' => new \stdClass(),
|
||||
'sessions' => [],
|
||||
'tokens' => [],
|
||||
'tokens' => null,
|
||||
'memberships' => [],
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'deleted' => false
|
||||
|
@ -739,7 +739,7 @@ App::delete('/v1/users/:userId')
|
|||
->setAttribute("email", null)
|
||||
->setAttribute("password", null)
|
||||
->setAttribute("deleted", true)
|
||||
->setAttribute("tokens", [])
|
||||
->setAttribute("tokens", null)
|
||||
->setAttribute("search", null)
|
||||
;
|
||||
|
||||
|
|
12
app/init.php
12
app/init.php
|
@ -301,6 +301,18 @@ Database::addFilter('subQueryWebhooks',
|
|||
}
|
||||
);
|
||||
|
||||
Database::addFilter('subQueryTokens',
|
||||
function($value) {
|
||||
return null;
|
||||
},
|
||||
function($value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('tokens', [
|
||||
new Query('userId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], $database->getIndexLimit(), 0, []);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('encrypt',
|
||||
function($value) {
|
||||
$key = App::getEnv('_APP_OPENSSL_KEY_V1');
|
||||
|
|
|
@ -232,6 +232,11 @@ class DeletesV1 extends Worker
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Delete tokens
|
||||
$this->deleteByGroup('tokens', [
|
||||
new Query('userId', Query::TYPE_EQUAL, [$userId])
|
||||
], $this->getProjectDB($projectId));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue