fix(plugins): match remote HMAC tenants with the same host rules as get_user
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,6 +7,10 @@ import {
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { createHmac, timingSafeEqual } from 'crypto';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import {
|
||||
findTenantForHost,
|
||||
normalizeTenantHost,
|
||||
} from './tenant-host.util';
|
||||
|
||||
const MAX_SKEW_SECONDS = 300;
|
||||
|
||||
@@ -88,7 +92,7 @@ export class RemoteSmsAuthGuard implements CanActivate {
|
||||
throw new UnauthorizedException('timestamp منقضی است');
|
||||
}
|
||||
|
||||
const domain = this.normalizeHost(domainRaw);
|
||||
const domain = normalizeTenantHost(domainRaw);
|
||||
if (!domain) {
|
||||
throw new UnauthorizedException('دامنه نامعتبر است');
|
||||
}
|
||||
@@ -138,7 +142,7 @@ export class RemoteSmsAuthGuard implements CanActivate {
|
||||
}
|
||||
|
||||
private async findTenantByDomain(host: string) {
|
||||
const normalizedHost = this.normalizeHost(host);
|
||||
const normalizedHost = normalizeTenantHost(host);
|
||||
if (!normalizedHost) return null;
|
||||
|
||||
const tenants = await this.prisma.tenant.findMany({
|
||||
@@ -156,89 +160,18 @@ export class RemoteSmsAuthGuard implements CanActivate {
|
||||
},
|
||||
});
|
||||
|
||||
let best: { row: TenantHostRow; score: number } | null = null;
|
||||
for (const row of tenants) {
|
||||
const score = this.scoreHostMatch(row, normalizedHost);
|
||||
if (score <= 0) continue;
|
||||
if (!best || score > best.score) {
|
||||
best = { row, score };
|
||||
}
|
||||
}
|
||||
if (!best) return null;
|
||||
const hit = findTenantForHost(tenants as TenantHostRow[], normalizedHost);
|
||||
if (!hit) return null;
|
||||
|
||||
return {
|
||||
id: best.row.id,
|
||||
nameFa: best.row.nameFa,
|
||||
domain: best.row.domain,
|
||||
product: best.row.product,
|
||||
isActive: best.row.isActive,
|
||||
id: hit.id,
|
||||
nameFa: hit.nameFa,
|
||||
domain: hit.domain,
|
||||
product: hit.product,
|
||||
isActive: hit.isActive,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* امتیاز بالاتر = هویت دقیقتر نسخه.
|
||||
* nodeServer ضعیف است چون چند نسخه ممکن است یک API مشترک داشته باشند.
|
||||
*/
|
||||
private scoreHostMatch(tenant: TenantHostRow, host: string): number {
|
||||
const hostNoWww = host.replace(/^www\./, '');
|
||||
const eq = (raw?: string | null) => {
|
||||
const normalized = this.normalizeHost(raw);
|
||||
if (!normalized) return false;
|
||||
return (
|
||||
normalized === host || normalized.replace(/^www\./, '') === hostNoWww
|
||||
);
|
||||
};
|
||||
|
||||
if (eq(tenant.domain)) return 100;
|
||||
|
||||
for (const alias of this.readDomains(tenant.domains)) {
|
||||
if (eq(alias)) return 90;
|
||||
}
|
||||
|
||||
if (eq(tenant.appUrl)) return 80;
|
||||
|
||||
// adminUrl گاهی موبایل است؛ فقط اگر شبیه میزبان باشد
|
||||
if (this.looksLikeHostname(tenant.adminUrl) && eq(tenant.adminUrl)) {
|
||||
return 50;
|
||||
}
|
||||
|
||||
if (eq(tenant.nodeServer)) return 10;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private looksLikeHostname(value?: string | null) {
|
||||
const normalized = this.normalizeHost(value);
|
||||
if (!normalized || !normalized.includes('.')) return false;
|
||||
if (/^\d+(\.\d+)*$/.test(normalized)) return false;
|
||||
if (/^09\d{9}$/.test(normalized)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private readDomains(raw: unknown): string[] {
|
||||
if (!raw) return [];
|
||||
if (Array.isArray(raw)) return raw.map(String);
|
||||
if (typeof raw === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
return Array.isArray(parsed) ? parsed.map(String) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private normalizeHost(value?: string | null) {
|
||||
if (!value) return '';
|
||||
return String(value)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/^https?:\/\//, '')
|
||||
.split('/')[0]
|
||||
.split(':')[0];
|
||||
}
|
||||
|
||||
private equalHex(a: string, b: string) {
|
||||
try {
|
||||
const left = Buffer.from(String(a).toLowerCase(), 'utf8');
|
||||
|
||||
Reference in New Issue
Block a user