mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
fix(api): return the terminated count from terminate sessions (#2539)
This commit is contained in:
@@ -315,7 +315,7 @@ export class AdminUserSecurityService {
|
||||
if (!user) {
|
||||
throw new UnknownUserError();
|
||||
}
|
||||
await AuthSession.terminateAllUserSessions(this.deps.apiContext, userId);
|
||||
const terminatedCount = await AuthSession.terminateAllUserSessions(this.deps.apiContext, userId);
|
||||
await auditService.createAuditLog({
|
||||
adminUserId,
|
||||
targetType: 'user',
|
||||
@@ -324,6 +324,7 @@ export class AdminUserSecurityService {
|
||||
auditLogReason,
|
||||
metadata: new Map(),
|
||||
});
|
||||
return {terminated_count: terminatedCount};
|
||||
}
|
||||
|
||||
async setUserAcls(
|
||||
|
||||
@@ -178,18 +178,19 @@ export async function logoutAuthSessions(
|
||||
});
|
||||
}
|
||||
|
||||
export async function terminateAllUserSessions(ctx: ApiContext, userId: UserID): Promise<void> {
|
||||
export async function terminateAllUserSessions(ctx: ApiContext, userId: UserID): Promise<number> {
|
||||
const {users, gateway} = ctx.services;
|
||||
const authSessions = await users.listAuthSessions(userId);
|
||||
await users.deleteAllPushSubscriptions(userId);
|
||||
await gateway.invalidatePushSubscriptions({userId});
|
||||
if (authSessions.length === 0) return;
|
||||
if (authSessions.length === 0) return 0;
|
||||
const hashes = authSessions.map((s) => s.sessionIdHash);
|
||||
await users.deleteAuthSessions(userId, hashes);
|
||||
await gateway.terminateSession({
|
||||
userId,
|
||||
sessionIdHashes: authSessions.map((s) => Buffer.from(s.sessionIdHash).toString('base64url')),
|
||||
});
|
||||
return authSessions.length;
|
||||
}
|
||||
|
||||
export async function replaceCurrentAuthSession(
|
||||
|
||||
@@ -41,8 +41,6 @@ const STREAMED_RESPONSE_ROUTES = new Map([
|
||||
['POST /admin/system/heap-snapshots', "'Content-Type': 'application/octet-stream'"],
|
||||
]);
|
||||
|
||||
const EMPTY_RESPONSE_ROUTES = new Map([['DELETE /admin/users/{}/sessions', 'terminated_count']]);
|
||||
|
||||
const MERGED_SCHEMA_ROUTES = new Map([
|
||||
['POST /auth/sessions/logout', "Validator('json', LogoutAuthSessionsRequest.merge(SudoVerificationSchema))"],
|
||||
[
|
||||
@@ -513,7 +511,7 @@ for (const file of await walk(DOCS_ROOT)) {
|
||||
const resolvedTarget = resolveRef(spec, target);
|
||||
const responseIsUnion =
|
||||
resolvedTarget != null && ((resolvedTarget.oneOf ?? []).length > 0 || (resolvedTarget.anyOf ?? []).length > 0);
|
||||
if (!STREAMED_RESPONSE_ROUTES.has(key) && !EMPTY_RESPONSE_ROUTES.has(key)) {
|
||||
if (!STREAMED_RESPONSE_ROUTES.has(key)) {
|
||||
const referencedTypes = new Map<string, string>();
|
||||
for (const anchor of referenced) {
|
||||
for (const [field, type] of anchorTypes.get(anchor) ?? []) {
|
||||
@@ -542,12 +540,7 @@ for (const file of await walk(DOCS_ROOT)) {
|
||||
});
|
||||
}
|
||||
}
|
||||
if (
|
||||
responseProperties.size > 0 &&
|
||||
!responseIsUnion &&
|
||||
!STREAMED_RESPONSE_ROUTES.has(key) &&
|
||||
!EMPTY_RESPONSE_ROUTES.has(key)
|
||||
) {
|
||||
if (responseProperties.size > 0 && !responseIsUnion && !STREAMED_RESPONSE_ROUTES.has(key)) {
|
||||
responsesChecked += 1;
|
||||
for (const field of responseProperties) {
|
||||
if (pageFields.has(field) || referencedFields.has(field)) {
|
||||
@@ -707,15 +700,6 @@ for (const [route, anchor] of STREAMED_RESPONSE_ROUTES) {
|
||||
staleExemptions.push(`${route}: no longer streams a file, drop this exemption`);
|
||||
}
|
||||
}
|
||||
const adminUserSecuritySource = await readFile(
|
||||
path.join(REPO_ROOT, 'fluxer_api/src/api/admin/services/AdminUserSecurityService.ts'),
|
||||
'utf8',
|
||||
);
|
||||
for (const [route, marker] of EMPTY_RESPONSE_ROUTES) {
|
||||
if (adminUserSecuritySource.includes(marker)) {
|
||||
staleExemptions.push(`${route}: the handler now emits ${marker}, drop this exemption`);
|
||||
}
|
||||
}
|
||||
for (const [route, anchor] of MERGED_SCHEMA_ROUTES) {
|
||||
const present = authSource.includes(anchor) || guildSource.includes(anchor);
|
||||
if (!present) {
|
||||
@@ -743,9 +727,6 @@ console.log(`success response schemas checked: ${responsesChecked.toString()}`);
|
||||
console.log(
|
||||
`streamed-response exemptions active: ${STREAMED_RESPONSE_ROUTES.size.toString()} (the spec declares JSON, the implementation streams a file)`,
|
||||
);
|
||||
console.log(
|
||||
`empty-response exemptions active: ${EMPTY_RESPONSE_ROUTES.size.toString()} (the spec declares a body, the handler returns none)`,
|
||||
);
|
||||
console.log(`response fields found documented on the page: ${responseFieldsFound.toString()}`);
|
||||
console.log(`request body field types compared: ${typesCompared.toString()}`);
|
||||
console.log(`request body optionality compared: ${optionalityCompared.toString()}`);
|
||||
|
||||
@@ -1838,12 +1838,16 @@ There is no operation that revokes one session. The account's Admin API keys, bo
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200<sup>1</sup> | empty<sup>2</sup> | Active sessions were terminated |
|
||||
| 200<sup>1</sup> | [terminate sessions response](#terminate-sessions-response) | Active sessions were terminated |
|
||||
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_USER`, because the account does not exist |
|
||||
|
||||
<sup>1</sup> An account with no live session is accepted and still records the audit entry
|
||||
<sup>1</sup> An account with no live session is accepted, returns `terminated_count` 0, and still records the audit entry
|
||||
|
||||
<sup>2</sup> The handler emits no body at all, so a client MUST NOT read the `terminated_count` member the spec declares
|
||||
#### Terminate sessions response
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| terminated_count | integer | The number of sessions that were terminated |
|
||||
|
||||
:::caution[A terminated session cannot be restored]
|
||||
The tombstone stays visible through [List user sessions](#list-user-sessions), but the session itself is gone.
|
||||
|
||||
Reference in New Issue
Block a user