Merge pull request #3056 from appwrite/feat-sessions-subquery

Feat: sessions subquery
This commit is contained in:
Torsten Dittmann 2022-05-09 09:08:17 +02:00 committed by GitHub
commit 33578c98b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 69 additions and 55 deletions

View file

@ -1054,9 +1054,9 @@ $collections = [
'size' => 16384,
'signed' => true,
'required' => false,
'default' => [],
'array' => true,
'filters' => ['json'],
'default' => null,
'array' => false,
'filters' => ['subQuerySessions'],
],
[
'$id' => 'tokens',
@ -1478,6 +1478,13 @@ $collections = [
'lengths' => [100, 100],
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
],
[
'$id' => '_key_user',
'type' => Database::INDEX_KEY,
'attributes' => ['userId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
],

View file

@ -104,7 +104,7 @@ App::post('/v1/account')
'reset' => false,
'name' => $name,
'prefs' => new \stdClass(),
'sessions' => [],
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
@ -208,8 +208,7 @@ App::post('/v1/account/sessions')
->setAttribute('$write', ['user:' . $profile->getId()])
);
$profile->setAttribute('sessions', $session, Document::SET_TYPE_APPEND);
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile);
$dbForProject->deleteCachedDocument('users', $profile->getId());
$audits
->setParam('userId', $profile->getId())
@ -458,13 +457,10 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$current = Auth::sessionVerify($sessions, Auth::$secret);
if ($current) { // Delete current session of new one.
foreach ($sessions as $key => $session) {/** @var Document $session */
if ($current === $session['$id']) {
unset($sessions[$key]);
$dbForProject->deleteDocument('sessions', $session->getId());
$dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $sessions));
}
$currentDocument = $dbForProject->getDocument('sessions', $current);
if(!$currentDocument->isEmpty()) {
$dbForProject->deleteDocument('sessions', $currentDocument->getId());
$dbForProject->deleteCachedDocument('users', $user->getId());
}
}
@ -505,7 +501,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'reset' => false,
'name' => $name,
'prefs' => new \stdClass(),
'sessions' => [],
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
@ -553,17 +549,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$user
->setAttribute('status', true)
->setAttribute('sessions', $session, Document::SET_TYPE_APPEND)
;
Authorization::setRole('user:' . $user->getId());
$dbForProject->updateDocument('users', $user->getId(), $user);
$session = $dbForProject->createDocument('sessions', $session
->setAttribute('$read', ['user:' . $user->getId()])
->setAttribute('$write', ['user:' . $user->getId()])
);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$dbForProject->deleteCachedDocument('users', $user->getId());
$audits
->setParam('userId', $user->getId())
@ -679,7 +676,7 @@ App::post('/v1/account/sessions/magic-url')
'registration' => \time(),
'reset' => false,
'prefs' => new \stdClass(),
'sessions' => [],
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email]),
@ -824,6 +821,10 @@ App::put('/v1/account/sessions/magic-url')
->setAttribute('$write', ['user:' . $user->getId()])
);
$dbForProject->deleteCachedDocument('users', $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.
@ -832,8 +833,7 @@ App::put('/v1/account/sessions/magic-url')
$dbForProject->deleteCachedDocument('users', $user->getId());
$user
->setAttribute('emailVerification', true)
->setAttribute('sessions', $session, Document::SET_TYPE_APPEND);
->setAttribute('emailVerification', true);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
@ -942,7 +942,7 @@ App::post('/v1/account/sessions/anonymous')
'reset' => false,
'name' => null,
'prefs' => new \stdClass(),
'sessions' => [],
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => $userId,
@ -978,8 +978,7 @@ App::post('/v1/account/sessions/anonymous')
->setAttribute('$write', ['user:' . $user->getId()])
);
$user = $dbForProject->updateDocument('users', $user->getId(),
$user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND));
$dbForProject->deleteCachedDocument('users', $user->getId());
$audits
->setParam('userId', $user->getId())
@ -1030,16 +1029,17 @@ App::post('/v1/account/jwt')
->label('abuse-key', 'url:{url},userId:{userId}')
->inject('response')
->inject('user')
->action(function ($response, $user) {
->inject('dbForProject')
->action(function ($response, $user, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
$sessions = $user->getAttribute('sessions', []);
$current = new Document();
foreach ($sessions as $session) {
/** @var Utopia\Database\Document $session */
foreach ($sessions as $session) { /** @var Utopia\Database\Document $session */
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$current = $session;
}
@ -1623,8 +1623,8 @@ App::delete('/v1/account/sessions/:sessionId')
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
;
}
$dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $sessions));
$dbForProject->deleteCachedDocument('users', $user->getId());
$events
->setParam('eventData', $response->output($session, Response::MODEL_SESSION))
@ -1718,8 +1718,7 @@ App::patch('/v1/account/sessions/:sessionId')
$dbForProject->updateDocument('sessions', $sessionId, $session);
$user->setAttribute("sessions", $sessions);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$dbForProject->deleteCachedDocument('users', $user->getId());
$audits
->setParam('userId', $user->getId())
@ -1805,7 +1804,7 @@ App::delete('/v1/account/sessions')
}
}
$dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', []));
$dbForProject->deleteCachedDocument('users', $user->getId());
$numOfSessions = count($sessions);

View file

@ -341,7 +341,7 @@ App::post('/v1/teams/:teamId/memberships')
'reset' => false,
'name' => $name,
'prefs' => new \stdClass(),
'sessions' => [],
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
@ -708,11 +708,10 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->setAttribute('$write', ['user:'.$user->getId()])
);
$user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND);
$dbForProject->deleteCachedDocument('users', $user->getId());
Authorization::setRole('user:'.$userId);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
$dbForProject->deleteCachedDocument('users', $user->getId());

View file

@ -63,7 +63,7 @@ App::post('/v1/users')
'reset' => false,
'name' => $name,
'prefs' => new \stdClass(),
'sessions' => [],
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
@ -632,25 +632,20 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
$sessions = $user->getAttribute('sessions', []);
$session = $dbForProject->getDocument('sessions', $sessionId);
foreach ($sessions as $key => $session) { /** @var Document $session */
if ($sessionId == $session->getId()) {
unset($sessions[$key]);
$dbForProject->deleteDocument('sessions', $session->getId());
$user->setAttribute('sessions', $sessions);
$events
->setParam('eventData', $response->output($user, Response::MODEL_USER))
;
$dbForProject->updateDocument('users', $user->getId(), $user);
}
if($session->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_SESSION_NOT_FOUND);
}
$dbForProject->deleteDocument('sessions', $session->getId());
$dbForProject->deleteCachedDocument('users', $user->getId());
$events
->setParam('eventData', $response->output($user, Response::MODEL_USER))
;
$usage
->setParam('users.update', 1)
->setParam('users.sessions.delete', 1)
@ -693,7 +688,7 @@ App::delete('/v1/users/:userId/sessions')
$dbForProject->deleteDocument('sessions', $session->getId());
}
$dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', []));
$dbForProject->deleteCachedDocument('users', $user->getId());
$events
->setParam('eventData', $response->output($user, Response::MODEL_USER))

View file

@ -301,6 +301,19 @@ Database::addFilter('subQueryWebhooks',
}
);
Database::addFilter('subQuerySessions',
function($value) {
return null;
},
function($value, Document $document, Database $database) {
$sessions = Authorization::skip(fn () => $database->find('sessions', [
new Query('userId', Query::TYPE_EQUAL, [$document->getId()])
], $database->getIndexLimit(), 0, []));
return $sessions;
}
);
Database::addFilter('subQueryTokens',
function($value) {
return null;

View file

@ -208,13 +208,14 @@ class DeletesV1 extends Worker
*/
$userId = $document->getId();
$user = $this->getProjectDB($projectId)->getDocument('users', $userId);
// Delete all sessions of this user from the sessions table and update the sessions field of the user record
$this->deleteByGroup('sessions', [
new Query('userId', Query::TYPE_EQUAL, [$userId])
], $this->getProjectDB($projectId));
$this->getProjectDB($projectId)->deleteCachedDocument('users', $userId);
// Delete Memberships and decrement team membership counts
$this->deleteByGroup('memberships', [
new Query('userId', Query::TYPE_EQUAL, [$userId])

View file

@ -204,7 +204,7 @@ trait TeamsBaseServer
$this->assertEquals(1, $response['body']['total']);
$this->assertIsInt($response['body']['total']);
$this->assertIsInt($response['body']['dateCreated']);
/** Delete User */
$user = $this->client->call(Client::METHOD_DELETE, '/users/' . $userUid, array_merge([