[TASK-028] security: Add tenant_id validation to uniqueness checks
- warehouses.service.ts: Add code uniqueness check with tenantId - products.service.ts: Add SKU/barcode uniqueness checks with tenantId - accounts.service.ts: Add tenantId to code and parent validation Fixes 5 RLS gaps in backend services for multi-tenant isolation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
7ae745e509
commit
2d2a562274
@ -209,9 +209,10 @@ class AccountsService {
|
||||
userId: string
|
||||
): Promise<Account> {
|
||||
try {
|
||||
// Validate unique code within company
|
||||
// Validate unique code within company and tenant (RLS compliance)
|
||||
const existing = await this.accountRepository.findOne({
|
||||
where: {
|
||||
tenantId,
|
||||
companyId: dto.companyId,
|
||||
code: dto.code,
|
||||
deletedAt: IsNull(),
|
||||
@ -225,11 +226,12 @@ class AccountsService {
|
||||
// Validate account type exists
|
||||
await this.findAccountTypeById(dto.accountTypeId);
|
||||
|
||||
// Validate parent account if specified
|
||||
// Validate parent account if specified (RLS compliance)
|
||||
if (dto.parentId) {
|
||||
const parent = await this.accountRepository.findOne({
|
||||
where: {
|
||||
id: dto.parentId,
|
||||
tenantId,
|
||||
companyId: dto.companyId,
|
||||
deletedAt: IsNull(),
|
||||
},
|
||||
@ -295,6 +297,7 @@ class AccountsService {
|
||||
const parent = await this.accountRepository.findOne({
|
||||
where: {
|
||||
id: dto.parentId,
|
||||
tenantId,
|
||||
companyId: existing.companyId,
|
||||
deletedAt: IsNull(),
|
||||
},
|
||||
|
||||
@ -164,6 +164,24 @@ class ProductsServiceClass {
|
||||
}
|
||||
|
||||
async create(tenantId: string, dto: CreateProductDto, createdBy?: string): Promise<Product> {
|
||||
// Validate unique SKU within tenant (RLS compliance)
|
||||
const existingSku = await this.productRepository.findOne({
|
||||
where: { sku: dto.sku, tenantId, deletedAt: IsNull() },
|
||||
});
|
||||
if (existingSku) {
|
||||
throw new Error(`Product with SKU '${dto.sku}' already exists`);
|
||||
}
|
||||
|
||||
// Validate unique barcode within tenant if provided (RLS compliance)
|
||||
if (dto.barcode) {
|
||||
const existingBarcode = await this.productRepository.findOne({
|
||||
where: { barcode: dto.barcode, tenantId, deletedAt: IsNull() },
|
||||
});
|
||||
if (existingBarcode) {
|
||||
throw new Error(`Product with barcode '${dto.barcode}' already exists`);
|
||||
}
|
||||
}
|
||||
|
||||
const product = this.productRepository.create({
|
||||
...dto,
|
||||
tenantId,
|
||||
@ -175,6 +193,27 @@ class ProductsServiceClass {
|
||||
async update(id: string, tenantId: string, dto: UpdateProductDto, updatedBy?: string): Promise<Product | null> {
|
||||
const product = await this.findOne(id, tenantId);
|
||||
if (!product) return null;
|
||||
|
||||
// Validate unique SKU within tenant if changing (RLS compliance)
|
||||
if (dto.sku && dto.sku !== product.sku) {
|
||||
const existingSku = await this.productRepository.findOne({
|
||||
where: { sku: dto.sku, tenantId, deletedAt: IsNull() },
|
||||
});
|
||||
if (existingSku) {
|
||||
throw new Error(`Product with SKU '${dto.sku}' already exists`);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate unique barcode within tenant if changing (RLS compliance)
|
||||
if (dto.barcode && dto.barcode !== product.barcode) {
|
||||
const existingBarcode = await this.productRepository.findOne({
|
||||
where: { barcode: dto.barcode, tenantId, deletedAt: IsNull() },
|
||||
});
|
||||
if (existingBarcode) {
|
||||
throw new Error(`Product with barcode '${dto.barcode}' already exists`);
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(product, { ...dto, updatedBy });
|
||||
return this.productRepository.save(product);
|
||||
}
|
||||
|
||||
@ -136,6 +136,14 @@ class WarehousesServiceClass {
|
||||
}
|
||||
|
||||
async create(tenantId: string, dto: CreateWarehouseDto, _createdBy?: string): Promise<Warehouse> {
|
||||
// Validate unique code within tenant (RLS compliance)
|
||||
const existingCode = await this.warehouseRepository.findOne({
|
||||
where: { code: dto.code, tenantId, deletedAt: IsNull() },
|
||||
});
|
||||
if (existingCode) {
|
||||
throw new Error(`Warehouse with code '${dto.code}' already exists`);
|
||||
}
|
||||
|
||||
// If this is set as default, unset other defaults
|
||||
if (dto.isDefault) {
|
||||
await this.warehouseRepository.update(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user