mirror of
https://github.com/dadosfera/maestro.git
synced 2026-11-14 00:29:08 +00:00
feat(orchest-identity): tenant orchest-api host derivation + allowlist
Derives the tenant orchest-api DNS from customer_name + the
orchest-{module}-{customer_name} namespace convention, with deterministic
slug normalization and an allowlist-regex guard. TODO before prod: confirm
the exact customer_name->namespace mapping against a real token.
Co-Authored-By: WOZCODE <contact@withwoz.com>
This commit is contained in:
@@ -3,6 +3,9 @@ import {
|
||||
moduleGateSeqid,
|
||||
isModuleAllowed,
|
||||
isAdmin,
|
||||
tenantOrchestApiHost,
|
||||
isAllowedOrchestApiHost,
|
||||
namespaceModule,
|
||||
} from './orchest-identity';
|
||||
|
||||
describe('orchest-identity mapping', () => {
|
||||
@@ -31,3 +34,43 @@ describe('orchest-identity mapping', () => {
|
||||
expect(isAdmin([99], [34, 99])).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('orchest-identity tenant routing', () => {
|
||||
it('namespaceModule default and values', () => {
|
||||
expect(namespaceModule(undefined)).toBe('intelli');
|
||||
expect(namespaceModule('process')).toBe('process');
|
||||
expect(namespaceModule('garbage')).toBe('intelli');
|
||||
});
|
||||
|
||||
it('tenantOrchestApiHost builds the namespace pattern', () => {
|
||||
expect(tenantOrchestApiHost('acme', 'intelli')).toBe(
|
||||
'orchest-api.orchest-intelli-acme.svc.cluster.local',
|
||||
);
|
||||
expect(tenantOrchestApiHost('acme', 'process')).toBe(
|
||||
'orchest-api.orchest-process-acme.svc.cluster.local',
|
||||
);
|
||||
});
|
||||
|
||||
it('tenantOrchestApiHost normalizes a non-slug customer_name', () => {
|
||||
expect(tenantOrchestApiHost('Acme Corp', 'intelli')).toBe(
|
||||
'orchest-api.orchest-intelli-acme-corp.svc.cluster.local',
|
||||
);
|
||||
});
|
||||
|
||||
it('isAllowedOrchestApiHost guards against malformed values', () => {
|
||||
expect(
|
||||
isAllowedOrchestApiHost(
|
||||
'orchest-api.orchest-intelli-acme.svc.cluster.local',
|
||||
),
|
||||
).toBe(true);
|
||||
expect(isAllowedOrchestApiHost('evil.example.com')).toBe(false);
|
||||
expect(
|
||||
isAllowedOrchestApiHost('orchest-api.orchest-intelli-.svc.cluster.local'),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isAllowedOrchestApiHost(
|
||||
'orchest-api.orchest-other-acme.svc.cluster.local',
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,3 +32,39 @@ export function isModuleAllowed(perms: number[], gateSeqid: number): boolean {
|
||||
export function isAdmin(perms: number[], adminSeqids: number[]): boolean {
|
||||
return Array.isArray(perms) && perms.some((p) => adminSeqids.includes(p));
|
||||
}
|
||||
|
||||
/** The `{module}` slug in the tenant namespace pattern; default 'intelli'. */
|
||||
export function namespaceModule(
|
||||
raw: string | undefined,
|
||||
): 'intelli' | 'process' {
|
||||
return raw === 'process' ? 'process' : 'intelli';
|
||||
}
|
||||
|
||||
/**
|
||||
* The in-cluster DNS of the calling tenant's orchest-api, from the tenant
|
||||
* namespace convention `orchest-{module}-{customer_name}`.
|
||||
*
|
||||
* `customer_name` is normalized to the namespace slug shape (lowercase,
|
||||
* non-[a-z0-9-] → '-') so a display-name value still yields a valid host; a
|
||||
* value that is already a clean slug is unchanged. NOTE: confirm the exact
|
||||
* prod `customer_name` → namespace mapping against a real token before relying
|
||||
* on this in production (the `dadosferademo2` tenant is a known exception and
|
||||
* is unsupported — it is being removed).
|
||||
*/
|
||||
export function tenantOrchestApiHost(
|
||||
customerName: string,
|
||||
module: string,
|
||||
): string {
|
||||
const slug = String(customerName)
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9-]/g, '-');
|
||||
return `orchest-api.orchest-${module}-${slug}.svc.cluster.local`;
|
||||
}
|
||||
|
||||
const ORCHEST_API_HOST_RE =
|
||||
/^orchest-api\.orchest-(intelli|process)-[a-z0-9-]+\.svc\.cluster\.local$/;
|
||||
|
||||
/** Defense in depth: only call an orchest-api host matching the convention. */
|
||||
export function isAllowedOrchestApiHost(host: string): boolean {
|
||||
return ORCHEST_API_HOST_RE.test(host);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user