Add record access strategy

This commit is contained in:
Francisco Gaona
2026-01-05 07:48:22 +01:00
parent 838a010fb2
commit 16907aadf8
97 changed files with 11350 additions and 208 deletions

View File

@@ -5,6 +5,7 @@ import {
UnauthorizedException,
HttpCode,
HttpStatus,
Req,
} from '@nestjs/common';
import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator';
import { AuthService } from './auth.service';
@@ -40,17 +41,33 @@ class RegisterDto {
export class AuthController {
constructor(private authService: AuthService) {}
private isCentralSubdomain(subdomain: string): boolean {
const centralSubdomains = (process.env.CENTRAL_SUBDOMAINS || 'central,admin').split(',');
return centralSubdomains.includes(subdomain);
}
@HttpCode(HttpStatus.OK)
@Post('login')
async login(@TenantId() tenantId: string, @Body() loginDto: LoginDto) {
if (!tenantId) {
throw new UnauthorizedException('Tenant ID is required');
async login(
@TenantId() tenantId: string,
@Body() loginDto: LoginDto,
@Req() req: any,
) {
const subdomain = req.raw?.subdomain;
// If it's a central subdomain, tenantId is not required
if (!subdomain || !this.isCentralSubdomain(subdomain)) {
if (!tenantId) {
throw new UnauthorizedException('Tenant ID is required');
}
}
const user = await this.authService.validateUser(
tenantId,
loginDto.email,
loginDto.password,
subdomain,
);
if (!user) {
@@ -64,9 +81,15 @@ export class AuthController {
async register(
@TenantId() tenantId: string,
@Body() registerDto: RegisterDto,
@Req() req: any,
) {
if (!tenantId) {
throw new UnauthorizedException('Tenant ID is required');
const subdomain = req.raw?.subdomain;
// If it's a central subdomain, tenantId is not required
if (!subdomain || !this.isCentralSubdomain(subdomain)) {
if (!tenantId) {
throw new UnauthorizedException('Tenant ID is required');
}
}
const user = await this.authService.register(
@@ -75,6 +98,7 @@ export class AuthController {
registerDto.password,
registerDto.firstName,
registerDto.lastName,
subdomain,
);
return user;

View File

@@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { TenantDatabaseService } from '../tenant/tenant-database.service';
import { getCentralPrisma } from '../prisma/central-prisma.service';
import * as bcrypt from 'bcrypt';
@Injectable()
@@ -10,11 +11,24 @@ export class AuthService {
private jwtService: JwtService,
) {}
private isCentralSubdomain(subdomain: string): boolean {
const centralSubdomains = (process.env.CENTRAL_SUBDOMAINS || 'central,admin').split(',');
return centralSubdomains.includes(subdomain);
}
async validateUser(
tenantId: string,
email: string,
password: string,
subdomain?: string,
): Promise<any> {
// Check if this is a central subdomain
if (subdomain && this.isCentralSubdomain(subdomain)) {
return this.validateCentralUser(email, password);
}
// Otherwise, validate as tenant user
const tenantDb = await this.tenantDbService.getTenantKnex(tenantId);
const user = await tenantDb('users')
@@ -43,6 +57,31 @@ export class AuthService {
return null;
}
private async validateCentralUser(
email: string,
password: string,
): Promise<any> {
const centralPrisma = getCentralPrisma();
const user = await centralPrisma.user.findUnique({
where: { email },
});
if (!user) {
return null;
}
if (await bcrypt.compare(password, user.password)) {
const { password: _, ...result } = user;
return {
...result,
isCentralAdmin: true,
};
}
return null;
}
async login(user: any) {
const payload = {
sub: user.id,
@@ -66,7 +105,14 @@ export class AuthService {
password: string,
firstName?: string,
lastName?: string,
subdomain?: string,
) {
// Check if this is a central subdomain
if (subdomain && this.isCentralSubdomain(subdomain)) {
return this.registerCentralUser(email, password, firstName, lastName);
}
// Otherwise, register as tenant user
const tenantDb = await this.tenantDbService.getTenantKnex(tenantId);
const hashedPassword = await bcrypt.hash(password, 10);
@@ -88,4 +134,28 @@ export class AuthService {
const { password: _, ...result } = user;
return result;
}
private async registerCentralUser(
email: string,
password: string,
firstName?: string,
lastName?: string,
) {
const centralPrisma = getCentralPrisma();
const hashedPassword = await bcrypt.hash(password, 10);
const user = await centralPrisma.user.create({
data: {
email,
password: hashedPassword,
firstName: firstName || null,
lastName: lastName || null,
isActive: true,
},
});
const { password: _, ...result } = user;
return result;
}
}