Trigger delete of targets associated with sessions when sessions are deleted
This commit is contained in:
parent
465bc81071
commit
874e483fb5
1 changed files with 222 additions and 34 deletions
|
@ -2591,8 +2591,9 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
|
->inject('queueForDeletes')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->action(function (?string $sessionId, ?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Document $project) {
|
->action(function (?string $sessionId, ?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes, Document $project) {
|
||||||
|
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
$sessionId = ($sessionId === 'current')
|
$sessionId = ($sessionId === 'current')
|
||||||
|
@ -2601,43 +2602,48 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||||
|
|
||||||
$sessions = $user->getAttribute('sessions', []);
|
$sessions = $user->getAttribute('sessions', []);
|
||||||
|
|
||||||
foreach ($sessions as $key => $session) {/** @var Document $session */
|
foreach ($sessions as $key => $session) {
|
||||||
if ($sessionId == $session->getId()) {
|
/** @var Document $session */
|
||||||
$dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $session) {
|
if ($sessionId !== $session->getId()) {
|
||||||
return $dbForProject->deleteDocument('sessions', $session->getId());
|
continue;
|
||||||
});
|
}
|
||||||
|
|
||||||
unset($sessions[$key]);
|
$dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $session) {
|
||||||
|
return $dbForProject->deleteDocument('sessions', $session->getId());
|
||||||
|
});
|
||||||
|
|
||||||
$session->setAttribute('current', false);
|
unset($sessions[$key]);
|
||||||
|
|
||||||
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
|
$session->setAttribute('current', false);
|
||||||
$session
|
|
||||||
->setAttribute('current', true)
|
|
||||||
->setAttribute('countryName', $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')))
|
|
||||||
;
|
|
||||||
|
|
||||||
if (!Config::getParam('domainVerification')) {
|
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
|
||||||
$response
|
$session
|
||||||
->addHeader('X-Fallback-Cookies', \json_encode([]))
|
->setAttribute('current', true)
|
||||||
;
|
->setAttribute('countryName', $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')));
|
||||||
}
|
|
||||||
|
|
||||||
$response
|
if (!Config::getParam('domainVerification')) {
|
||||||
->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
$response->addHeader('X-Fallback-Cookies', \json_encode([]));
|
||||||
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
$response
|
||||||
|
->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
||||||
$queueForEvents
|
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'));
|
||||||
->setParam('userId', $user->getId())
|
|
||||||
->setParam('sessionId', $session->getId())
|
|
||||||
->setPayload($response->output($session, Response::MODEL_SESSION))
|
|
||||||
;
|
|
||||||
return $response->noContent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||||
|
|
||||||
|
$queueForEvents
|
||||||
|
->setParam('userId', $user->getId())
|
||||||
|
->setParam('sessionId', $session->getId())
|
||||||
|
->setPayload($response->output($session, Response::MODEL_SESSION));
|
||||||
|
|
||||||
|
$queueForDeletes
|
||||||
|
->setType(DELETE_TYPE_SESSION_TARGETS)
|
||||||
|
->setDocument($session)
|
||||||
|
->trigger();
|
||||||
|
|
||||||
|
$response->noContent();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
|
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
|
||||||
|
@ -2739,7 +2745,8 @@ App::delete('/v1/account/sessions')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents) {
|
->inject('queueForDeletes')
|
||||||
|
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes) {
|
||||||
|
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
$sessions = $user->getAttribute('sessions', []);
|
$sessions = $user->getAttribute('sessions', []);
|
||||||
|
@ -2753,8 +2760,7 @@ App::delete('/v1/account/sessions')
|
||||||
|
|
||||||
$session
|
$session
|
||||||
->setAttribute('current', false)
|
->setAttribute('current', false)
|
||||||
->setAttribute('countryName', $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')))
|
->setAttribute('countryName', $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')));
|
||||||
;
|
|
||||||
|
|
||||||
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) {
|
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) {
|
||||||
$session->setAttribute('current', true);
|
$session->setAttribute('current', true);
|
||||||
|
@ -2765,7 +2771,13 @@ App::delete('/v1/account/sessions')
|
||||||
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'));
|
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'));
|
||||||
|
|
||||||
// Use current session for events.
|
// Use current session for events.
|
||||||
$queueForEvents->setPayload($response->output($session, Response::MODEL_SESSION));
|
$queueForEvents
|
||||||
|
->setPayload($response->output($session, Response::MODEL_SESSION));
|
||||||
|
|
||||||
|
$queueForDeletes
|
||||||
|
->setType(DELETE_TYPE_SESSION_TARGETS)
|
||||||
|
->setDocument($session)
|
||||||
|
->trigger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3906,3 +3918,179 @@ App::delete('/v1/account')
|
||||||
|
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App::post('/v1/account/targets/push')
|
||||||
|
->desc('Create a push target')
|
||||||
|
->groups(['api', 'account'])
|
||||||
|
->label('scope', 'targets.write')
|
||||||
|
->label('audits.event', 'target.create')
|
||||||
|
->label('audits.resource', 'target/response.$id')
|
||||||
|
->label('event', 'users.[userId].targets.[targetId].create')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
|
||||||
|
->label('sdk.namespace', 'account')
|
||||||
|
->label('sdk.method', 'createPushTarget')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_TARGET)
|
||||||
|
->param('targetId', '', new CustomId(), 'Target ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||||
|
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
|
||||||
|
->param('providerId', '', new UID(), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true)
|
||||||
|
->inject('queueForEvents')
|
||||||
|
->inject('user')
|
||||||
|
->inject('session')
|
||||||
|
->inject('request')
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForProject')
|
||||||
|
->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) {
|
||||||
|
$targetId = $targetId == 'unique()' ? ID::unique() : $targetId;
|
||||||
|
|
||||||
|
$provider = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId));
|
||||||
|
|
||||||
|
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
|
||||||
|
|
||||||
|
if (!$target->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
$detector = new Detector($request->getUserAgent());
|
||||||
|
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
|
||||||
|
|
||||||
|
$device = $detector->getDevice();
|
||||||
|
|
||||||
|
$sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret);
|
||||||
|
$session = $dbForProject->getDocument('sessions', $sessionId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$target = $dbForProject->createDocument('targets', new Document([
|
||||||
|
'$id' => $targetId,
|
||||||
|
'$permissions' => [
|
||||||
|
Permission::read(Role::user($user->getId())),
|
||||||
|
Permission::update(Role::user($user->getId())),
|
||||||
|
Permission::delete(Role::user($user->getId())),
|
||||||
|
],
|
||||||
|
'providerId' => !empty($providerId) ? $providerId : null,
|
||||||
|
'providerInternalId' => !empty($providerId) ? $provider->getInternalId() : null,
|
||||||
|
'providerType' => MESSAGE_TYPE_PUSH,
|
||||||
|
'userId' => $user->getId(),
|
||||||
|
'userInternalId' => $user->getInternalId(),
|
||||||
|
'sessionId' => $session->getId(),
|
||||||
|
'sessionInternalId' => $session->getInternalId(),
|
||||||
|
'identifier' => $identifier,
|
||||||
|
'name' => "{$device['deviceBrand']} {$device['deviceModel']}"
|
||||||
|
]));
|
||||||
|
} catch (Duplicate) {
|
||||||
|
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||||
|
|
||||||
|
$queueForEvents
|
||||||
|
->setParam('userId', $user->getId())
|
||||||
|
->setParam('targetId', $target->getId());
|
||||||
|
|
||||||
|
$response
|
||||||
|
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||||
|
->dynamic($target, Response::MODEL_TARGET);
|
||||||
|
});
|
||||||
|
|
||||||
|
App::put('/v1/account/targets/:targetId/push')
|
||||||
|
->desc('Update a push target')
|
||||||
|
->groups(['api', 'account'])
|
||||||
|
->label('scope', 'targets.write')
|
||||||
|
->label('audits.event', 'target.update')
|
||||||
|
->label('audits.resource', 'target/response.$id')
|
||||||
|
->label('event', 'users.[userId].targets.[targetId].update')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
|
||||||
|
->label('sdk.namespace', 'account')
|
||||||
|
->label('sdk.method', 'updatePushTarget')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_TARGET)
|
||||||
|
->param('targetId', '', new UID(), 'Target ID.')
|
||||||
|
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
|
||||||
|
->inject('queueForEvents')
|
||||||
|
->inject('user')
|
||||||
|
->inject('request')
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForProject')
|
||||||
|
->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) {
|
||||||
|
|
||||||
|
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
|
||||||
|
|
||||||
|
if ($target->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->getId() !== $target->getAttribute('userId')) {
|
||||||
|
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($identifier) {
|
||||||
|
$target->setAttribute('identifier', $identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
$detector = new Detector($request->getUserAgent());
|
||||||
|
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
|
||||||
|
|
||||||
|
$device = $detector->getDevice();
|
||||||
|
|
||||||
|
$target->setAttribute('name', "{$device['deviceBrand']} {$device['deviceModel']}");
|
||||||
|
|
||||||
|
$target = $dbForProject->updateDocument('targets', $target->getId(), $target);
|
||||||
|
|
||||||
|
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||||
|
|
||||||
|
$queueForEvents
|
||||||
|
->setParam('userId', $user->getId())
|
||||||
|
->setParam('targetId', $target->getId());
|
||||||
|
|
||||||
|
$response
|
||||||
|
->dynamic($target, Response::MODEL_TARGET);
|
||||||
|
});
|
||||||
|
|
||||||
|
App::delete('/v1/account/targets/:targetId/push')
|
||||||
|
->desc('Delete a push target')
|
||||||
|
->groups(['api', 'account'])
|
||||||
|
->label('scope', 'targets.write')
|
||||||
|
->label('audits.event', 'target.delete')
|
||||||
|
->label('audits.resource', 'target/response.$id')
|
||||||
|
->label('event', 'users.[userId].targets.[targetId].delete')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
|
||||||
|
->label('sdk.namespace', 'account')
|
||||||
|
->label('sdk.method', 'deletePushTarget')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_TARGET)
|
||||||
|
->param('targetId', '', new UID(), 'Target ID.')
|
||||||
|
->inject('queueForEvents')
|
||||||
|
->inject('queueForDeletes')
|
||||||
|
->inject('user')
|
||||||
|
->inject('request')
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForProject')
|
||||||
|
->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject) {
|
||||||
|
$target = Authorization::skip(fn() => $dbForProject->getDocument('targets', $targetId));
|
||||||
|
|
||||||
|
if ($target->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->getInternalId() !== $target->getAttribute('userInternalId')) {
|
||||||
|
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbForProject->deleteDocument('targets', $target->getId());
|
||||||
|
|
||||||
|
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||||
|
|
||||||
|
$queueForDeletes
|
||||||
|
->setType(DELETE_TYPE_TARGET)
|
||||||
|
->setDocument($target);
|
||||||
|
|
||||||
|
$queueForEvents
|
||||||
|
->setParam('userId', $user->getId())
|
||||||
|
->setParam('targetId', $target->getId())
|
||||||
|
->setPayload($response->output($target, Response::MODEL_TARGET));
|
||||||
|
|
||||||
|
$response->noContent();
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue