Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/domain/definitions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@

import type { Validation } from '^/integrations/validation';

export const TENANT_BY_ORIGIN_PATH = 'domain/tenant/getByOriginConverted';

export const SortOrders = {
POPULAR: 'popular',
RECENT: 'recent'
Expand Down
6 changes: 3 additions & 3 deletions src/integrations/runtime/authenticationMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@

import identityProvider from '^/integrations/authentication';

import { TENANT_BY_ORIGIN_PATH } from '^/domain/definitions';

import AuthenticationMiddleware from './middlewares/AuthenticationMiddleware';

const authProcedures = {
Expand All @@ -13,6 +11,8 @@ const authProcedures = {

const redirectPath = process.env.AUTHENTICATION_CLIENT_PATH || 'undefined';

const whiteList: string[] = [TENANT_BY_ORIGIN_PATH];
const whiteList: string[] = [
'domain/tenant/getByOriginConverted'
];

export default new AuthenticationMiddleware(identityProvider, authProcedures, redirectPath, whiteList);
60 changes: 26 additions & 34 deletions src/integrations/runtime/middlewares/TenantMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,54 @@

import type { Middleware, NextHandler, Request, Response } from 'jitar';
import type { Middleware, NextHandler, Request } from 'jitar';
import { Response } from 'jitar';

import type { Tenant } from '^/domain/tenant';
import getByOrigin from '^/domain/tenant/getByOriginConverted';

const GEY_BY_ORIGIN_FQN = 'domain/tenant/getByOriginConverted';
const TENANT_PARAMETER = '*tenant';

export default class TenantMiddleware implements Middleware
{
readonly #cache = new Map<string, Response>();
readonly #getTenantPath: string;

constructor(tenantPath: string)
{
this.#getTenantPath = tenantPath;
}
readonly #tenants = new Map<string, Tenant>();

async handle(request: Request, next: NextHandler): Promise<Response>
{
return request.fqn === this.#getTenantPath
? this.#getTenant(request, next)
return request.fqn === GEY_BY_ORIGIN_FQN
? this.#getTenant(request)
: this.#handleRequest(request, next);
}

async #getTenant(request: Request, next: NextHandler): Promise<Response>
async #resolveTenant(request: Request): Promise<Tenant>
{
const origin = this.#getOrigin(request);
const cached = this.#cache.get(origin);
const origin = request.getHeader('origin') as string;
const tenant = this.#tenants.get(origin);

Comment on lines +22 to 26
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard and normalize the Origin header; current cast may pass undefined through.

request.getHeader('origin') can be missing or 'null'. The forced cast risks passing undefined into the cache and data layer.

Apply this minimal guard (optional: add normalization):

-        const origin = request.getHeader('origin') as string;
+        const rawOrigin = request.getHeader('origin');
+        if (typeof rawOrigin !== 'string' || rawOrigin.trim() === '' || rawOrigin === 'null') {
+            // Consider mapping to a 400 response upstream or throwing a typed error the global handler maps to 400.
+            throw new Error('Missing or invalid Origin header');
+        }
+        const origin = rawOrigin.trim();

Optional normalization helper (to avoid case/port variance):

// add as a private static method if desired
static #normalizeOrigin(raw: string): string {
  try {
    const u = new URL(raw);
    const isDefault = (u.protocol === 'https:' && (u.port === '' || u.port === '443')) ||
                      (u.protocol === 'http:'  && (u.port === '' || u.port === '80'));
    const host = isDefault ? u.hostname : `${u.hostname}:${u.port}`;
    return `${u.protocol}//${host}`.toLowerCase();
  } catch {
    return raw.toLowerCase();
  }
}
🤖 Prompt for AI Agents
In src/integrations/runtime/middlewares/TenantMiddleware.ts around lines 22-26,
the code force-casts request.getHeader('origin') to string which may yield
undefined or 'null' and pollute the tenant cache; update the method to first
guard against missing/invalid origin (e.g., check for falsy or the literal
'null' and handle by throwing or returning a default tenant), then normalize the
origin before using it as a cache key (add the provided #normalizeOrigin helper
as a private static method or inline equivalent), and finally use the
normalizedOrigin when calling this.#tenants.get(...) so only valid, normalized
origin strings reach the cache and data layer.

if (cached === undefined)
if (tenant === undefined)
{
request.setArgument('origin', origin);
const tenant = await getByOrigin(origin);

const response = await next();
this.#tenants.set(origin, tenant);

if (response.status === 200)
{
this.#cache.set(origin, response);
}

return response;
return tenant;
}

return cached;
return tenant;
}

async #handleRequest(request: Request, next: NextHandler): Promise<Response>
async #getTenant(request: Request): Promise<Response>
{
const origin = this.#getOrigin(request);
const cached = this.#cache.get(origin);

if (cached !== undefined)
{
request.setArgument(TENANT_PARAMETER, cached.result);
}
const tenant = await this.#resolveTenant(request);

return next();
return new Response(200, tenant);
}

#getOrigin(request: Request): string
async #handleRequest(request: Request, next: NextHandler): Promise<Response>
{
return request.getHeader('origin') as string;
const tenant = await this.#resolveTenant(request);

request.setArgument(TENANT_PARAMETER, tenant);

return next();
}
}
4 changes: 0 additions & 4 deletions src/integrations/runtime/tearDownWorker.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@

import database from '^/integrations/database';
import eventBroker from '^/integrations/eventbroker';
import fileStore from '^/integrations/filestore';
import notificationService from '^/integrations/notification';

const disconnections = [];

if (database.connected) disconnections.push(database.disconnect());
if (eventBroker.connected) disconnections.push(eventBroker.disconnect());
if (fileStore.connected) disconnections.push(fileStore.disconnect());
if (notificationService.connected) disconnections.push(notificationService.disconnect());

await Promise.allSettled(disconnections);
6 changes: 1 addition & 5 deletions src/integrations/runtime/tenantMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@

import { TENANT_BY_ORIGIN_PATH } from '^/domain/definitions';

import TenantMiddleware from './middlewares/TenantMiddleware';

const tenantPath = TENANT_BY_ORIGIN_PATH;

export default new TenantMiddleware(tenantPath);
export default new TenantMiddleware();