fix: harden tenant auth and quality gates

This commit is contained in:
hectorzhao
2026-08-28 11:44:16 +08:00
parent c3bf8af3e6
commit 2744690f9f
51 changed files with 1750 additions and 466 deletions
+22 -2
View File
@@ -1,4 +1,4 @@
import { Injectable, NestMiddleware, UnauthorizedException } from '@nestjs/common';
import { ForbiddenException, Injectable, NestMiddleware, UnauthorizedException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { AuthSessionRecord, SessionPortal, SessionService } from './session.service';
@@ -8,6 +8,7 @@ export type SessionRequest = {
url?: string;
sessionUserId?: string;
sessionToken?: string;
sessionTenantId?: string;
authSession?: AuthSessionRecord;
};
@@ -38,7 +39,7 @@ export class SessionValidationMiddleware implements NestMiddleware {
const user = await this.prisma.user.findUnique({
where: { id: result.record.userId },
select: { id: true, status: true, deletedAt: true, sessionVersion: true },
select: { id: true, tenantId: true, status: true, deletedAt: true, sessionVersion: true },
});
if (!user || user.status !== 'active' || user.deletedAt || user.sessionVersion !== result.record.sessionVersion) {
await this.sessions.remove(token);
@@ -51,6 +52,25 @@ export class SessionValidationMiddleware implements NestMiddleware {
request.sessionUserId = user.id;
request.sessionToken = token;
request.authSession = result.record;
if (portal === 'client') {
if (!user.tenantId) {
throw new ForbiddenException({ code: 'CLIENT_TENANT_REQUIRED', message: '当前客户端账号未关联企业' });
}
const suppliedTenantId = request.header('x-tenant-id')?.trim();
if (suppliedTenantId && suppliedTenantId !== user.tenantId) {
await this.prisma.operationLog.create({
data: {
tenantId: user.tenantId,
userId: user.id,
action: 'security.client_tenant_mismatch',
resource: 'auth_session',
detail: { suppliedTenantId },
},
});
throw new ForbiddenException({ code: 'CLIENT_TENANT_MISMATCH', message: '请求企业与登录企业不一致' });
}
request.sessionTenantId = user.tenantId;
}
const isSessionRecoveryRoute = /\/auth\/(?:session(?:\/unlock)?|logout)(?:\?|$)/.test(path);
if (result.status === 'locked' && !isSessionRecoveryRoute) {
if (result.newlyLocked) {