diff --git a/server/lib/calculateUserClientsForOrgs.ts b/server/lib/calculateUserClientsForOrgs.ts index 123aefdd..b2ea08a3 100644 --- a/server/lib/calculateUserClientsForOrgs.ts +++ b/server/lib/calculateUserClientsForOrgs.ts @@ -307,7 +307,6 @@ async function cleanupOrphanedClients( await sendTerminateClient( deletedClient.clientId, OlmErrorCodes.TERMINATED_DELETED, - "Deleted", deletedClient.olmId ); } diff --git a/server/private/routers/re-key/reGenerateClientSecret.ts b/server/private/routers/re-key/reGenerateClientSecret.ts index a16a0646..b2f9e151 100644 --- a/server/private/routers/re-key/reGenerateClientSecret.ts +++ b/server/private/routers/re-key/reGenerateClientSecret.ts @@ -25,6 +25,7 @@ import { OpenAPITags, registry } from "@server/openApi"; import { hashPassword } from "@server/auth/password"; import { disconnectClient, sendToClient } from "#private/routers/ws"; import { OlmErrorCodes, sendOlmError } from "@server/routers/olm/error"; +import { sendTerminateClient } from "@server/routers/client/terminate"; const reGenerateSecretParamsSchema = z.strictObject({ clientId: z.string().transform(Number).pipe(z.int().positive()) @@ -118,15 +119,12 @@ export async function reGenerateClientSecret( // Only disconnect if explicitly requested if (disconnect) { - const payload = { - type: `olm/terminate`, - data: { - code: OlmErrorCodes.TERMINATED_REKEYED, - message: "Client secret has been regenerated" - } - }; // Don't await this to prevent blocking the response - sendToClient(existingOlms[0].olmId, payload).catch((error) => { + sendTerminateClient( + clientId, + OlmErrorCodes.TERMINATED_REKEYED, + existingOlms[0].olmId + ).catch((error) => { logger.error( "Failed to send termination message to olm:", error diff --git a/server/routers/client/blockClient.ts b/server/routers/client/blockClient.ts index 3bb878a5..bd760b3d 100644 --- a/server/routers/client/blockClient.ts +++ b/server/routers/client/blockClient.ts @@ -79,7 +79,7 @@ export async function blockClient( // Send terminate signal if there's an associated OLM and it's connected if (client.olmId && client.online) { - await sendTerminateClient(client.clientId, OlmErrorCodes.TERMINATED_BLOCKED, "Blocked", client.olmId); + await sendTerminateClient(client.clientId, OlmErrorCodes.TERMINATED_BLOCKED, client.olmId); } }); diff --git a/server/routers/client/deleteClient.ts b/server/routers/client/deleteClient.ts index db88c365..276bfde9 100644 --- a/server/routers/client/deleteClient.ts +++ b/server/routers/client/deleteClient.ts @@ -92,7 +92,7 @@ export async function deleteClient( await rebuildClientAssociationsFromClient(deletedClient, trx); if (olm) { - await sendTerminateClient(deletedClient.clientId, OlmErrorCodes.TERMINATED_DELETED, "Deleted", olm.olmId); // the olmId needs to be provided because it cant look it up after deletion + await sendTerminateClient(deletedClient.clientId, OlmErrorCodes.TERMINATED_DELETED, olm.olmId); // the olmId needs to be provided because it cant look it up after deletion } }); diff --git a/server/routers/client/terminate.ts b/server/routers/client/terminate.ts index 5c5ca216..db9cfcb0 100644 --- a/server/routers/client/terminate.ts +++ b/server/routers/client/terminate.ts @@ -5,8 +5,7 @@ import { OlmErrorCodes } from "../olm/error"; export async function sendTerminateClient( clientId: number, - code: (typeof OlmErrorCodes)[keyof typeof OlmErrorCodes], - message: string, + error: (typeof OlmErrorCodes)[keyof typeof OlmErrorCodes], olmId?: string | null ) { if (!olmId) { @@ -24,8 +23,8 @@ export async function sendTerminateClient( await sendToClient(olmId, { type: `olm/terminate`, data: { - code, - message + code: error.code, + message: error.message } }); } diff --git a/server/routers/olm/archiveUserOlm.ts b/server/routers/olm/archiveUserOlm.ts index a835dbdc..b1a7bb4d 100644 --- a/server/routers/olm/archiveUserOlm.ts +++ b/server/routers/olm/archiveUserOlm.ts @@ -53,7 +53,7 @@ export async function archiveUserOlm( .where(eq(clients.clientId, client.clientId)); await rebuildClientAssociationsFromClient(client, trx); - await sendTerminateClient(client.clientId, OlmErrorCodes.TERMINATED_ARCHIVED, "Archived", olmId); + await sendTerminateClient(client.clientId, OlmErrorCodes.TERMINATED_ARCHIVED, olmId); } // Archive the OLM (set archived to true) diff --git a/server/routers/olm/deleteUserOlm.ts b/server/routers/olm/deleteUserOlm.ts index 3d9e5c23..2c281489 100644 --- a/server/routers/olm/deleteUserOlm.ts +++ b/server/routers/olm/deleteUserOlm.ts @@ -78,7 +78,6 @@ export async function deleteUserOlm( await sendTerminateClient( deletedClient.clientId, OlmErrorCodes.TERMINATED_DELETED, - "Deleted", olm.olmId ); // the olmId needs to be provided because it cant look it up after deletion } diff --git a/server/routers/olm/error.ts b/server/routers/olm/error.ts index 11d3d4cf..289e7219 100644 --- a/server/routers/olm/error.ts +++ b/server/routers/olm/error.ts @@ -1,35 +1,82 @@ import { sendToClient } from "#dynamic/routers/ws"; // Error codes for registration failures export const OlmErrorCodes = { - OLM_NOT_FOUND: "OLM_NOT_FOUND", - CLIENT_ID_NOT_FOUND: "CLIENT_ID_NOT_FOUND", - CLIENT_NOT_FOUND: "CLIENT_NOT_FOUND", - CLIENT_BLOCKED: "CLIENT_BLOCKED", - CLIENT_PENDING: "CLIENT_PENDING", - ORG_NOT_FOUND: "ORG_NOT_FOUND", - USER_ID_NOT_FOUND: "USER_ID_NOT_FOUND", - INVALID_USER_SESSION: "INVALID_USER_SESSION", - USER_ID_MISMATCH: "USER_ID_MISMATCH", - ACCESS_POLICY_DENIED: "ACCESS_POLICY_DENIED", - TERMINATED_REKEYED: "TERMINATED_REKEYED", - TERMINATED_ORG_DELETED: "TERMINATED_ORG_DELETED", - TERMINATED_INACTIVITY: "TERMINATED_INACTIVITY", - TERMINATED_DELETED: "TERMINATED_DELETED", - TERMINATED_ARCHIVED: "TERMINATED_ARCHIVED", - TERMINATED_BLOCKED: "TERMINATED_BLOCKED" + OLM_NOT_FOUND: { + code: "OLM_NOT_FOUND", + message: "The requested OLM session could not be found." + }, + CLIENT_ID_NOT_FOUND: { + code: "CLIENT_ID_NOT_FOUND", + message: "No client ID was provided in the request." + }, + CLIENT_NOT_FOUND: { + code: "CLIENT_NOT_FOUND", + message: "The specified client does not exist." + }, + CLIENT_BLOCKED: { + code: "CLIENT_BLOCKED", + message: "This client has been blocked and cannot connect." + }, + CLIENT_PENDING: { + code: "CLIENT_PENDING", + message: "This client is pending approval and cannot connect yet." + }, + ORG_NOT_FOUND: { + code: "ORG_NOT_FOUND", + message: "The organization could not be found." + }, + USER_ID_NOT_FOUND: { + code: "USER_ID_NOT_FOUND", + message: "No user ID was provided in the request." + }, + INVALID_USER_SESSION: { + code: "INVALID_USER_SESSION", + message: "Your user session is invalid or has expired." + }, + USER_ID_MISMATCH: { + code: "USER_ID_MISMATCH", + message: "The provided user ID does not match the session." + }, + ACCESS_POLICY_DENIED: { + code: "ACCESS_POLICY_DENIED", + message: "Access denied due to policy restrictions." + }, + TERMINATED_REKEYED: { + code: "TERMINATED_REKEYED", + message: "This session was terminated because encryption keys were regenerated." + }, + TERMINATED_ORG_DELETED: { + code: "TERMINATED_ORG_DELETED", + message: "This session was terminated because the organization was deleted." + }, + TERMINATED_INACTIVITY: { + code: "TERMINATED_INACTIVITY", + message: "This session was terminated due to inactivity." + }, + TERMINATED_DELETED: { + code: "TERMINATED_DELETED", + message: "This session was terminated because it was deleted." + }, + TERMINATED_ARCHIVED: { + code: "TERMINATED_ARCHIVED", + message: "This session was terminated because it was archived." + }, + TERMINATED_BLOCKED: { + code: "TERMINATED_BLOCKED", + message: "This session was terminated because access was blocked." + } } as const; // Helper function to send registration error export async function sendOlmError( - code: string, - errorMessage: string, + error: typeof OlmErrorCodes[keyof typeof OlmErrorCodes], olmId: string ) { sendToClient(olmId, { type: "olm/error", data: { - code, - message: errorMessage + code: error.code, + message: error.message } }); } diff --git a/server/routers/olm/handleOlmPingMessage.ts b/server/routers/olm/handleOlmPingMessage.ts index fc1ebb3f..1fcd85b5 100644 --- a/server/routers/olm/handleOlmPingMessage.ts +++ b/server/routers/olm/handleOlmPingMessage.ts @@ -66,7 +66,6 @@ export const startOlmOfflineChecker = (): void => { await sendTerminateClient( offlineClient.clientId, OlmErrorCodes.TERMINATED_INACTIVITY, - "Client terminated due to inactivity", offlineClient.olmId ); // terminate first // wait a moment to ensure the message is sent diff --git a/server/routers/olm/handleOlmRegisterMessage.ts b/server/routers/olm/handleOlmRegisterMessage.ts index 242fe345..a8c1327d 100644 --- a/server/routers/olm/handleOlmRegisterMessage.ts +++ b/server/routers/olm/handleOlmRegisterMessage.ts @@ -56,7 +56,6 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => { logger.warn("Olm client ID not found"); sendOlmError( OlmErrorCodes.CLIENT_ID_NOT_FOUND, - "Olm client ID not found", olm.olmId ); return; @@ -72,7 +71,6 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => { logger.warn("Client ID not found"); sendOlmError( OlmErrorCodes.CLIENT_NOT_FOUND, - "Client not found in organization", olm.olmId ); return; @@ -84,7 +82,6 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => { ); sendOlmError( OlmErrorCodes.CLIENT_BLOCKED, - "Client is blocked", olm.olmId ); return; @@ -96,7 +93,6 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => { ); sendOlmError( OlmErrorCodes.CLIENT_PENDING, - "Client approval is pending", olm.olmId ); return; @@ -112,7 +108,6 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => { logger.warn("Org not found"); sendOlmError( OlmErrorCodes.ORG_NOT_FOUND, - "Organization not found", olm.olmId ); return; @@ -123,7 +118,6 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => { logger.warn("Olm has no user ID"); sendOlmError( OlmErrorCodes.USER_ID_NOT_FOUND, - "User ID not found for this client", olm.olmId ); return; @@ -135,7 +129,6 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => { logger.warn("Invalid user session for olm register"); sendOlmError( OlmErrorCodes.INVALID_USER_SESSION, - "Invalid or expired user session token", olm.olmId ); return; @@ -144,7 +137,6 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => { logger.warn("User ID mismatch for olm register"); sendOlmError( OlmErrorCodes.USER_ID_MISMATCH, - "User ID does not match the authenticated session", olm.olmId ); return; @@ -166,7 +158,6 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => { ); sendOlmError( OlmErrorCodes.ACCESS_POLICY_DENIED, - `Access policy denied: ${policyCheck.error}`, olm.olmId ); return; diff --git a/server/routers/org/deleteOrg.ts b/server/routers/org/deleteOrg.ts index a4b913e8..48d3102d 100644 --- a/server/routers/org/deleteOrg.ts +++ b/server/routers/org/deleteOrg.ts @@ -22,6 +22,7 @@ import { sendToClient } from "#dynamic/routers/ws"; import { deletePeer } from "../gerbil/peers"; import { OpenAPITags, registry } from "@server/openApi"; import { OlmErrorCodes } from "../olm/error"; +import { sendTerminateClient } from "../client/terminate"; const deleteOrgSchema = z.strictObject({ orgId: z.string() @@ -207,13 +208,11 @@ export async function deleteOrg( } for (const olmId of olmsToTerminate) { - sendToClient(olmId, { - type: "olm/terminate", - data: { - code: OlmErrorCodes.TERMINATED_REKEYED, - message: "Organization has been deleted" - } - }).catch((error) => { + sendTerminateClient( + 0, // clientId not needed since we're passing olmId + OlmErrorCodes.TERMINATED_REKEYED, + olmId + ).catch((error) => { logger.error( "Failed to send termination message to olm:", error