Merge pull request #7540 from appwrite/feat-session-target-delete
Feat session target delete
This commit is contained in:
commit
bcf6d5dbe2
5 changed files with 271 additions and 170 deletions
|
@ -2117,6 +2117,28 @@ $commonCollections = [
|
||||||
'array' => false,
|
'array' => false,
|
||||||
'filters' => [],
|
'filters' => [],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('sessionId'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => Database::LENGTH_KEY,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('sessionInternalId'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => Database::LENGTH_KEY,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'$id' => ID::custom('providerType'),
|
'$id' => ID::custom('providerType'),
|
||||||
'type' => Database::VAR_STRING,
|
'type' => Database::VAR_STRING,
|
||||||
|
|
|
@ -1967,76 +1967,6 @@ App::post('/v1/account/jwt')
|
||||||
])]), Response::MODEL_JWT);
|
])]), Response::MODEL_JWT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/account/targets/push')
|
|
||||||
->desc('Create Account\'s push target')
|
|
||||||
->groups(['api', 'account'])
|
|
||||||
->label('scope', 'account')
|
|
||||||
->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('request')
|
|
||||||
->inject('response')
|
|
||||||
->inject('dbForProject')
|
|
||||||
->action(function (string $targetId, string $providerId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) {
|
|
||||||
$targetId = $targetId == 'unique()' ? ID::unique() : $targetId;
|
|
||||||
|
|
||||||
$provider = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId));
|
|
||||||
|
|
||||||
if ($user->isEmpty()) {
|
|
||||||
throw new Exception(Exception::USER_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
$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();
|
|
||||||
|
|
||||||
try {
|
|
||||||
$target = $dbForProject->createDocument('targets', new Document([
|
|
||||||
'$id' => $targetId,
|
|
||||||
'$permissions' => [
|
|
||||||
Permission::read(Role::user($user->getId())),
|
|
||||||
Permission::update(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(),
|
|
||||||
'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::get('/v1/account')
|
App::get('/v1/account')
|
||||||
->desc('Get account')
|
->desc('Get account')
|
||||||
->groups(['api', 'account'])
|
->groups(['api', 'account'])
|
||||||
|
@ -2591,8 +2521,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,8 +2532,12 @@ 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 */
|
||||||
|
if ($sessionId !== $session->getId()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $session) {
|
$dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $session) {
|
||||||
return $dbForProject->deleteDocument('sessions', $session->getId());
|
return $dbForProject->deleteDocument('sessions', $session->getId());
|
||||||
});
|
});
|
||||||
|
@ -2614,19 +2549,15 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||||
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
|
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
|
||||||
$session
|
$session
|
||||||
->setAttribute('current', true)
|
->setAttribute('current', true)
|
||||||
->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 (!Config::getParam('domainVerification')) {
|
if (!Config::getParam('domainVerification')) {
|
||||||
$response
|
$response->addHeader('X-Fallback-Cookies', \json_encode([]));
|
||||||
->addHeader('X-Fallback-Cookies', \json_encode([]))
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$response
|
$response
|
||||||
->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
||||||
->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'));
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||||
|
@ -2634,10 +2565,15 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||||
$queueForEvents
|
$queueForEvents
|
||||||
->setParam('userId', $user->getId())
|
->setParam('userId', $user->getId())
|
||||||
->setParam('sessionId', $session->getId())
|
->setParam('sessionId', $session->getId())
|
||||||
->setPayload($response->output($session, Response::MODEL_SESSION))
|
->setPayload($response->output($session, Response::MODEL_SESSION));
|
||||||
;
|
|
||||||
return $response->noContent();
|
$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 +2675,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 +2690,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 +2701,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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3814,63 +3756,6 @@ App::put('/v1/account/mfa/challenge')
|
||||||
$response->dynamic($session, Response::MODEL_SESSION);
|
$response->dynamic($session, Response::MODEL_SESSION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/account/targets/:targetId/push')
|
|
||||||
->desc('Update Account\'s push target')
|
|
||||||
->groups(['api', 'account'])
|
|
||||||
->label('scope', 'account')
|
|
||||||
->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) {
|
|
||||||
if ($user->isEmpty()) {
|
|
||||||
throw new Exception(Exception::USER_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
$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')
|
App::delete('/v1/account')
|
||||||
->desc('Delete account')
|
->desc('Delete account')
|
||||||
->groups(['api', 'account'])
|
->groups(['api', 'account'])
|
||||||
|
@ -3906,3 +3791,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();
|
||||||
|
});
|
||||||
|
|
|
@ -177,6 +177,7 @@ const DELETE_TYPE_SCHEDULES = 'schedules';
|
||||||
const DELETE_TYPE_TOPIC = 'topic';
|
const DELETE_TYPE_TOPIC = 'topic';
|
||||||
const DELETE_TYPE_TARGET = 'target';
|
const DELETE_TYPE_TARGET = 'target';
|
||||||
const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets';
|
const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets';
|
||||||
|
const DELETE_TYPE_SESSION_TARGETS = 'session_targets';
|
||||||
// Mail Types
|
// Mail Types
|
||||||
const MAIL_TYPE_VERIFICATION = 'verification';
|
const MAIL_TYPE_VERIFICATION = 'verification';
|
||||||
const MAIL_TYPE_MAGIC_SESSION = 'magicSession';
|
const MAIL_TYPE_MAGIC_SESSION = 'magicSession';
|
||||||
|
|
14
composer.lock
generated
14
composer.lock
generated
|
@ -1543,16 +1543,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/database",
|
"name": "utopia-php/database",
|
||||||
"version": "0.48.1",
|
"version": "0.48.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/database.git",
|
"url": "https://github.com/utopia-php/database.git",
|
||||||
"reference": "52abe057180a76fe354a516300344b33f268f6ea"
|
"reference": "0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/52abe057180a76fe354a516300344b33f268f6ea",
|
"url": "https://api.github.com/repos/utopia-php/database/zipball/0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4",
|
||||||
"reference": "52abe057180a76fe354a516300344b33f268f6ea",
|
"reference": "0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -1593,9 +1593,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/database/issues",
|
"issues": "https://github.com/utopia-php/database/issues",
|
||||||
"source": "https://github.com/utopia-php/database/tree/0.48.1"
|
"source": "https://github.com/utopia-php/database/tree/0.48.2"
|
||||||
},
|
},
|
||||||
"time": "2024-02-02T04:54:13+00:00"
|
"time": "2024-02-02T14:10:14+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/domains",
|
"name": "utopia-php/domains",
|
||||||
|
@ -5523,5 +5523,5 @@
|
||||||
"platform-overrides": {
|
"platform-overrides": {
|
||||||
"php": "8.2"
|
"php": "8.2"
|
||||||
},
|
},
|
||||||
"plugin-api-version": "2.3.0"
|
"plugin-api-version": "2.6.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,6 +164,9 @@ class Deletes extends Action
|
||||||
case DELETE_TYPE_EXPIRED_TARGETS:
|
case DELETE_TYPE_EXPIRED_TARGETS:
|
||||||
$this->deleteExpiredTargets($project, $getProjectDB);
|
$this->deleteExpiredTargets($project, $getProjectDB);
|
||||||
break;
|
break;
|
||||||
|
case DELETE_TYPE_SESSION_TARGETS:
|
||||||
|
$this->deleteSessionTargets($project, $getProjectDB, $document);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new \Exception('No delete operation for type: ' . \strval($type));
|
throw new \Exception('No delete operation for type: ' . \strval($type));
|
||||||
}
|
}
|
||||||
|
@ -249,7 +252,7 @@ class Deletes extends Action
|
||||||
* @param Document $target
|
* @param Document $target
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private function deleteTargetSubscribers(Document $project, callable $getProjectDB, Document $target)
|
private function deleteTargetSubscribers(Document $project, callable $getProjectDB, Document $target): void
|
||||||
{
|
{
|
||||||
/** @var Database */
|
/** @var Database */
|
||||||
$dbForProject = $getProjectDB($project);
|
$dbForProject = $getProjectDB($project);
|
||||||
|
@ -279,7 +282,7 @@ class Deletes extends Action
|
||||||
* @return void
|
* @return void
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private function deleteExpiredTargets(Document $project, callable $getProjectDB)
|
private function deleteExpiredTargets(Document $project, callable $getProjectDB): void
|
||||||
{
|
{
|
||||||
$this->deleteByGroup(
|
$this->deleteByGroup(
|
||||||
'targets',
|
'targets',
|
||||||
|
@ -293,6 +296,20 @@ class Deletes extends Action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function deleteSessionTargets(Document $project, callable $getProjectDB, Document $session): void
|
||||||
|
{
|
||||||
|
$this->deleteByGroup(
|
||||||
|
'targets',
|
||||||
|
[
|
||||||
|
Query::equal('sessionInternalId', [$session->getInternalId()])
|
||||||
|
],
|
||||||
|
$getProjectDB($project),
|
||||||
|
function (Document $target) use ($getProjectDB, $project) {
|
||||||
|
$this->deleteTargetSubscribers($project, $getProjectDB, $target);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Document $project
|
* @param Document $project
|
||||||
* @param callable $getProjectDB
|
* @param callable $getProjectDB
|
||||||
|
|
Loading…
Reference in a new issue