fix remote HMAC: identify tenant by domain, not shared nodeServer
new.gholaktala.ir is the API host of many versions. Prefer subdomain of the product domain and refuse ambiguous nodeServer ties. 403 names the matched tenant. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -54,10 +54,18 @@ function hostsEqual(left: string, right: string): boolean {
|
||||
return left.replace(/^www\./, '') === right.replace(/^www\./, '');
|
||||
}
|
||||
|
||||
/** new.gholaktala.ir belongs to tenant domain gholaktala.ir — not via shared nodeServer. */
|
||||
function isSubdomainOf(host: string, parent: string): boolean {
|
||||
const child = normalizeTenantHost(host);
|
||||
const root = normalizeTenantHost(parent).replace(/^www\./, '');
|
||||
if (!child || !root || !root.includes('.')) return false;
|
||||
return child === root || child.endsWith('.' + root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same identity as GET /get_user?domain= — primary domain, then aliases,
|
||||
* then app/admin/node URLs so HMAC and get_user cannot disagree.
|
||||
* Origin پنل ادمین (دامنه یا آیپی LAN داخل adminUrl) هم همینجا مچ میشود.
|
||||
* then app/admin URLs. nodeServer is last and only if unique (many versions
|
||||
* share one API host like new.gholaktala.ir).
|
||||
*/
|
||||
export function scoreTenantHost(
|
||||
tenant: TenantHostFields,
|
||||
@@ -75,6 +83,10 @@ export function scoreTenantHost(
|
||||
for (const value of readHostList(tenant.appUrl)) {
|
||||
if (eq(value)) return 80;
|
||||
}
|
||||
if (tenant.domain && isSubdomainOf(host, tenant.domain)) return 75;
|
||||
for (const alias of readHostList(tenant.domains)) {
|
||||
if (isSubdomainOf(host, alias)) return 72;
|
||||
}
|
||||
for (const value of readHostList(tenant.adminUrl)) {
|
||||
const normalized = normalizeTenantHost(value);
|
||||
if (looksLikeHostname(normalized) && hostsEqual(normalized, host)) {
|
||||
@@ -91,11 +103,15 @@ export function findTenantForHost<T extends TenantHostFields>(
|
||||
tenants: T[],
|
||||
host: string,
|
||||
): T | undefined {
|
||||
let best: { row: T; score: number } | undefined;
|
||||
for (const row of tenants) {
|
||||
const score = scoreTenantHost(row, host);
|
||||
if (score <= 0) continue;
|
||||
if (!best || score > best.score) best = { row, score };
|
||||
}
|
||||
return best?.row;
|
||||
const scored = tenants
|
||||
.map((row) => ({ row, score: scoreTenantHost(row, host) }))
|
||||
.filter((item) => item.score > 0)
|
||||
.sort((a, b) => b.score - a.score);
|
||||
if (!scored.length) return undefined;
|
||||
|
||||
const best = scored[0].score;
|
||||
const tied = scored.filter((item) => item.score === best);
|
||||
// nodeServer مشترک یا دو نسخه با امتیاز مساوی — حدس نزن
|
||||
if (tied.length !== 1) return undefined;
|
||||
return tied[0].row;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user