forked from wangziqi/gongxue-base
171 lines
4.4 KiB
JavaScript
171 lines
4.4 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { HttpError } from '../apps/api/src/core/errors.ts';
|
|
import { normalizeTenantHost, selectTenantLocator } from '../apps/api/src/features/tenant/locator.ts';
|
|
import { resolveTenantRoute } from '../apps/api/src/features/tenant/routes.ts';
|
|
|
|
const TENANT_ID = '00000000-0000-4000-8000-000000000001';
|
|
|
|
function tenantRow(overrides = {}) {
|
|
return {
|
|
id: TENANT_ID,
|
|
slug: 'campus-a',
|
|
name: 'Campus A',
|
|
status: 'active',
|
|
mode: 'saas',
|
|
host: 'campus-a.example.com',
|
|
brand_name: 'Campus A',
|
|
short_name: 'Campus A',
|
|
slogan: null,
|
|
logo_url: null,
|
|
favicon_url: null,
|
|
service_wechat: null,
|
|
service_account_name: null,
|
|
theme: {},
|
|
public_assets: {},
|
|
feature_flags: {},
|
|
admin_feature_flags: {},
|
|
public_config: {},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function requestContext(url, headers = {}) {
|
|
return {
|
|
url: new URL(url),
|
|
req: { headers },
|
|
res: {},
|
|
};
|
|
}
|
|
|
|
async function expectHttpError(run, statusCode, code) {
|
|
await assert.rejects(run, error => {
|
|
assert.ok(error instanceof HttpError);
|
|
assert.equal(error.statusCode, statusCode);
|
|
assert.equal(error.code, code);
|
|
return true;
|
|
});
|
|
}
|
|
|
|
assert.equal(normalizeTenantHost('Example.COM.:443'), 'example.com');
|
|
assert.equal(normalizeTenantHost('[::1]:8787'), '::1');
|
|
assert.equal(normalizeTenantHost('::1'), '::1');
|
|
assert.equal(normalizeTenantHost('bad,forwarded.example.com'), '');
|
|
|
|
assert.deepEqual(selectTenantLocator({
|
|
origin: 'https://Campus-A.Example.com:443',
|
|
requestedHost: 'campus-a.example.com.',
|
|
requestHost: 'api.example.com',
|
|
isProduction: true,
|
|
}), {
|
|
ok: true,
|
|
locator: {
|
|
kind: 'host',
|
|
host: 'campus-a.example.com',
|
|
expectedTenantCode: null,
|
|
source: 'browser',
|
|
},
|
|
});
|
|
|
|
assert.deepEqual(selectTenantLocator({
|
|
origin: 'null',
|
|
referer: 'https://campus-a.example.com/pages/student/home',
|
|
requestHost: 'api.example.com',
|
|
isProduction: true,
|
|
}), {
|
|
ok: true,
|
|
locator: {
|
|
kind: 'host',
|
|
host: 'campus-a.example.com',
|
|
expectedTenantCode: null,
|
|
source: 'browser',
|
|
},
|
|
});
|
|
|
|
assert.equal(selectTenantLocator({
|
|
origin: 'https://campus-a.example.com',
|
|
requestedHost: 'campus-b.example.com',
|
|
isProduction: true,
|
|
}).code, 'TENANT_HOST_CONFLICT');
|
|
|
|
assert.deepEqual(selectTenantLocator({
|
|
origin: 'http://localhost:5173',
|
|
requestedHost: 'localhost:5173',
|
|
tenantCode: 'campus-a',
|
|
requestHost: 'localhost:8787',
|
|
isProduction: false,
|
|
}), {
|
|
ok: true,
|
|
locator: { kind: 'tenantCode', tenantCode: 'campus-a', source: 'local-development' },
|
|
});
|
|
|
|
assert.deepEqual(selectTenantLocator({
|
|
tenantCode: 'campus-a',
|
|
requestHost: 'api.example.com',
|
|
isProduction: true,
|
|
}), {
|
|
ok: true,
|
|
locator: { kind: 'tenantCode', tenantCode: 'campus-a', source: 'headless-client' },
|
|
});
|
|
|
|
assert.equal(selectTenantLocator({
|
|
requestedHost: 'campus-a.example.com',
|
|
requestHost: 'api.example.com',
|
|
isProduction: true,
|
|
}).code, 'TENANT_HOST_UNTRUSTED');
|
|
|
|
{
|
|
const calls = [];
|
|
const result = await resolveTenantRoute(
|
|
requestContext('https://api.example.com/api/tenant/resolve?host=campus-a.example.com', {
|
|
host: 'api.example.com',
|
|
origin: 'https://campus-a.example.com',
|
|
'x-forwarded-host': 'attacker.example.com',
|
|
}),
|
|
async (sql, params) => {
|
|
calls.push({ sql, params });
|
|
return tenantRow();
|
|
},
|
|
);
|
|
assert.equal(result.tenant.id, TENANT_ID);
|
|
assert.deepEqual(calls[0].params, ['campus-a.example.com']);
|
|
assert.match(calls[0].sql, /d\.status = 'active' and t\.status = 'active'/);
|
|
}
|
|
|
|
await expectHttpError(
|
|
() => resolveTenantRoute(
|
|
requestContext('https://api.example.com/api/tenant/resolve?host=unknown.example.com', {
|
|
host: 'api.example.com',
|
|
origin: 'https://unknown.example.com',
|
|
}),
|
|
async () => null,
|
|
),
|
|
404,
|
|
'TENANT_DOMAIN_NOT_BOUND',
|
|
);
|
|
|
|
await expectHttpError(
|
|
() => resolveTenantRoute(
|
|
requestContext('https://api.example.com/api/tenant/resolve?host=campus-a.example.com&tenantCode=campus-b', {
|
|
host: 'api.example.com',
|
|
origin: 'https://campus-a.example.com',
|
|
}),
|
|
async () => tenantRow(),
|
|
),
|
|
409,
|
|
'TENANT_LOCATOR_CONFLICT',
|
|
);
|
|
|
|
await expectHttpError(
|
|
() => resolveTenantRoute(
|
|
requestContext('https://api.example.com/api/tenant/resolve?host=campus-a.example.com', {
|
|
host: 'api.example.com',
|
|
origin: 'https://campus-b.example.com',
|
|
}),
|
|
async () => tenantRow(),
|
|
),
|
|
409,
|
|
'TENANT_HOST_CONFLICT',
|
|
);
|
|
|
|
console.log('[PASS] Tenant resolve fail-closed contract');
|