From a17c6ed16208fd503780429dd633024702afe334 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 15 Jun 2025 16:32:34 +0200 Subject: [PATCH] KEYCLOAK AUTH FIX: Phase 4 - Email & Files Endpoints **UPDATED ENDPOINTS (11 additional):** - email/send.ts (CRITICAL: was using old auth) - email/fetch-thread.ts (CRITICAL: was using old auth) - email/fetch-thread-v2.ts (CRITICAL: was using old auth) - email/generate-eoi-document.ts (CRITICAL: was using old auth) - files/upload.ts (CRITICAL: was using old auth) - files/list.ts (SECURITY ISSUE: had NO auth) - files/download.ts (SECURITY ISSUE: had NO auth) - files/delete.ts (SECURITY ISSUE: had NO auth) - files/create-folder.ts (SECURITY ISSUE: had NO auth) - files/preview.ts (SECURITY ISSUE: had NO auth) - files/rename.ts (SECURITY ISSUE: had NO auth) **AUTHENTICATION:** All now support dual auth: - x-tag header (webhooks/external calls) - Keycloak session (logged-in users) **PROGRESS:** 28/47 endpoints completed (~60%) **NEXT:** Continue with remaining proxy, test & debug endpoints **CRITICAL SECURITY FIXES:** Found 6 file endpoints with NO authentication - major vulnerability patched! --- server/api/email/fetch-thread-v2.ts | 8 +++----- server/api/email/fetch-thread.ts | 8 +++----- server/api/email/generate-eoi-document.ts | 23 ++++++++++++----------- server/api/email/send.ts | 11 ++++------- server/api/files/create-folder.ts | 4 ++++ server/api/files/delete.ts | 4 ++++ server/api/files/download.ts | 4 ++++ server/api/files/list.ts | 4 ++++ server/api/files/preview.ts | 4 ++++ server/api/files/rename.ts | 4 ++++ server/api/files/upload.ts | 8 +++----- 11 files changed, 49 insertions(+), 33 deletions(-) diff --git a/server/api/email/fetch-thread-v2.ts b/server/api/email/fetch-thread-v2.ts index 7aa80d8..d0ee4f8 100644 --- a/server/api/email/fetch-thread-v2.ts +++ b/server/api/email/fetch-thread-v2.ts @@ -1,3 +1,4 @@ +import { requireAuth } from '~/server/utils/auth'; import { getCredentialsFromSession, decryptCredentials } from '~/server/utils/encryption'; import { getCachedEmails, syncEmailsWithRetry, getSyncMetadata } from '~/server/utils/email-sync'; @@ -23,11 +24,8 @@ interface EmailThread { } export default defineEventHandler(async (event) => { - const xTagHeader = getRequestHeader(event, "x-tag"); - - if (!xTagHeader || (xTagHeader !== "094ut234" && xTagHeader !== "pjnvü1230")) { - throw createError({ statusCode: 401, statusMessage: "unauthenticated" }); - } + // Check authentication (x-tag header OR Keycloak session) + await requireAuth(event); try { const body = await readBody(event); diff --git a/server/api/email/fetch-thread.ts b/server/api/email/fetch-thread.ts index 3f8aa9c..6decd30 100644 --- a/server/api/email/fetch-thread.ts +++ b/server/api/email/fetch-thread.ts @@ -1,5 +1,6 @@ import Imap from 'imap'; import { simpleParser } from 'mailparser'; +import { requireAuth } from '~/server/utils/auth'; import { getCredentialsFromSession, decryptCredentials } from '~/server/utils/encryption'; import { listFiles, getFileStats, getMinioClient, uploadFile } from '~/server/utils/minio'; import { getIMAPPool } from '~/server/utils/imap-pool'; @@ -18,11 +19,8 @@ interface EmailMessage { } export default defineEventHandler(async (event) => { - const xTagHeader = getRequestHeader(event, "x-tag"); - - if (!xTagHeader || (xTagHeader !== "094ut234" && xTagHeader !== "pjnvü1230")) { - throw createError({ statusCode: 401, statusMessage: "unauthenticated" }); - } + // Check authentication (x-tag header OR Keycloak session) + await requireAuth(event); try { const body = await readBody(event); diff --git a/server/api/email/generate-eoi-document.ts b/server/api/email/generate-eoi-document.ts index f5da271..c4099f3 100644 --- a/server/api/email/generate-eoi-document.ts +++ b/server/api/email/generate-eoi-document.ts @@ -1,3 +1,4 @@ +import { requireAuth } from '~/server/utils/auth'; import { getInterestById, updateInterest } from '~/server/utils/nocodb'; // Helper function to create embedded signing URLs @@ -32,13 +33,9 @@ interface DocumensoResponse { export default defineEventHandler(async (event) => { console.log('[generate-eoi] ========== EOI GENERATION REQUEST RECEIVED =========='); - const xTagHeader = getRequestHeader(event, "x-tag"); - console.log('[generate-eoi] x-tag header:', xTagHeader); - - if (!xTagHeader || (xTagHeader !== "094ut234" && xTagHeader !== "pjnvü1230")) { - console.log('[generate-eoi] Authentication failed'); - throw createError({ statusCode: 401, statusMessage: "unauthenticated" }); - } + // Check authentication (x-tag header OR Keycloak session) + await requireAuth(event); + console.log('[generate-eoi] Request authenticated'); try { const body = await readBody(event); @@ -177,13 +174,17 @@ export default defineEventHandler(async (event) => { }); } - // Get linked berths + // Get linked berths - use the same auth as this request (either x-tag or session) + const xTagHeader = getRequestHeader(event, "x-tag"); + const requestHeaders: Record = {}; + if (xTagHeader) { + requestHeaders["x-tag"] = xTagHeader; + } + const berthsResponse = await $fetch<{ list: Array<{ 'Mooring Number': string }> }>( "/api/get-interest-berths", { - headers: { - "x-tag": xTagHeader, - }, + headers: requestHeaders, params: { interestId: interestId, linkType: "berths", diff --git a/server/api/email/send.ts b/server/api/email/send.ts index ac5d743..826cf99 100644 --- a/server/api/email/send.ts +++ b/server/api/email/send.ts @@ -1,17 +1,14 @@ import nodemailer from 'nodemailer'; +import { requireAuth } from '~/server/utils/auth'; import { getCredentialsFromSession, decryptCredentials } from '~/server/utils/encryption'; import { uploadFile, getMinioClient } from '~/server/utils/minio'; import { updateInterest } from '~/server/utils/nocodb'; export default defineEventHandler(async (event) => { - const xTagHeader = getRequestHeader(event, "x-tag"); + // Check authentication (x-tag header OR Keycloak session) + await requireAuth(event); - console.log('[Email Send] Request received with x-tag:', xTagHeader); - - if (!xTagHeader || (xTagHeader !== "094ut234" && xTagHeader !== "pjnvü1230")) { - console.error('[Email Send] Authentication failed - invalid x-tag'); - throw createError({ statusCode: 401, statusMessage: "unauthenticated" }); - } + console.log('[Email Send] Request authenticated'); try { const body = await readBody(event); diff --git a/server/api/files/create-folder.ts b/server/api/files/create-folder.ts index 902cf31..c3cfd07 100644 --- a/server/api/files/create-folder.ts +++ b/server/api/files/create-folder.ts @@ -1,6 +1,10 @@ +import { requireAuth } from '~/server/utils/auth'; import { createFolder } from '~/server/utils/minio'; export default defineEventHandler(async (event) => { + // Check authentication (x-tag header OR Keycloak session) + await requireAuth(event); + try { const body = await readBody(event); const { folderPath } = body; diff --git a/server/api/files/delete.ts b/server/api/files/delete.ts index b37f1ef..7b716e4 100644 --- a/server/api/files/delete.ts +++ b/server/api/files/delete.ts @@ -1,6 +1,10 @@ +import { requireAuth } from '~/server/utils/auth'; import { deleteFile, deleteFolder, getMinioClient } from '~/server/utils/minio'; export default defineEventHandler(async (event) => { + // Check authentication (x-tag header OR Keycloak session) + await requireAuth(event); + try { const body = await readBody(event); const { fileName, isFolder, bucket } = body; diff --git a/server/api/files/download.ts b/server/api/files/download.ts index ece3896..c8fa13e 100644 --- a/server/api/files/download.ts +++ b/server/api/files/download.ts @@ -1,6 +1,10 @@ +import { requireAuth } from '~/server/utils/auth'; import { getDownloadUrl } from '~/server/utils/minio'; export default defineEventHandler(async (event) => { + // Check authentication (x-tag header OR Keycloak session) + await requireAuth(event); + try { const query = getQuery(event); const fileName = query.fileName as string; diff --git a/server/api/files/list.ts b/server/api/files/list.ts index b4f6ef8..fa0a933 100644 --- a/server/api/files/list.ts +++ b/server/api/files/list.ts @@ -1,6 +1,10 @@ +import { requireAuth } from '~/server/utils/auth'; import { listFiles } from '~/server/utils/minio'; export default defineEventHandler(async (event) => { + // Check authentication (x-tag header OR Keycloak session) + await requireAuth(event); + try { const query = getQuery(event); const prefix = (query.prefix as string) || ''; diff --git a/server/api/files/preview.ts b/server/api/files/preview.ts index 1b7d8c5..bb19e57 100644 --- a/server/api/files/preview.ts +++ b/server/api/files/preview.ts @@ -1,7 +1,11 @@ +import { requireAuth } from '~/server/utils/auth'; import { getPreviewUrl } from '~/server/utils/minio'; import mime from 'mime-types'; export default defineEventHandler(async (event) => { + // Check authentication (x-tag header OR Keycloak session) + await requireAuth(event); + try { const query = getQuery(event); const fileName = query.fileName as string; diff --git a/server/api/files/rename.ts b/server/api/files/rename.ts index 6966283..052203d 100644 --- a/server/api/files/rename.ts +++ b/server/api/files/rename.ts @@ -1,6 +1,10 @@ +import { requireAuth } from '~/server/utils/auth'; import { renameFile, renameFolder } from '~/server/utils/minio'; export default defineEventHandler(async (event) => { + // Check authentication (x-tag header OR Keycloak session) + await requireAuth(event); + try { const body = await readBody(event); const { oldName, newName, isFolder } = body; diff --git a/server/api/files/upload.ts b/server/api/files/upload.ts index dd46d55..9d87e8f 100644 --- a/server/api/files/upload.ts +++ b/server/api/files/upload.ts @@ -1,14 +1,12 @@ +import { requireAuth } from '~/server/utils/auth'; import { uploadFile, getMinioClient } from '~/server/utils/minio'; import formidable from 'formidable'; import { promises as fs } from 'fs'; import mime from 'mime-types'; export default defineEventHandler(async (event) => { - const xTagHeader = getRequestHeader(event, "x-tag"); - - if (!xTagHeader || (xTagHeader !== "094ut234" && xTagHeader !== "pjnvü1230")) { - throw createError({ statusCode: 401, statusMessage: "unauthenticated" }); - } + // Check authentication (x-tag header OR Keycloak session) + await requireAuth(event); try { // Get the current path and bucket from query params