e18e61000d
Add tenant-scoped document lookup with throwIfNotFound() before S3 operations in GetAttachment, DeleteAttachment, and GetAttachmentPresignedUrl services. This prevents users from reading, deleting, or generating presigned URLs for attachments belonging to other tenants. Also adds RequirePermission decorators to the three attachment endpoints and introduces Attachment ability subject with View and Delete actions. GHSA-rc4v-wq22-v6cf Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
57 lines
1.7 KiB
TypeScript
57 lines
1.7 KiB
TypeScript
import { Inject, Injectable } from '@nestjs/common';
|
|
import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
import { Knex } from 'knex';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
|
|
import { S3_CLIENT } from '../S3/S3.module';
|
|
import { DocumentModel } from './models/Document.model';
|
|
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
|
import { DocumentLinkModel } from './models/DocumentLink.model';
|
|
|
|
@Injectable()
|
|
export class DeleteAttachment {
|
|
constructor(
|
|
private readonly uow: UnitOfWork,
|
|
private readonly configService: ConfigService,
|
|
|
|
@Inject(S3_CLIENT)
|
|
private readonly s3Client: S3Client,
|
|
|
|
@Inject(DocumentModel.name)
|
|
private readonly documentModel: TenantModelProxy<typeof DocumentModel>,
|
|
|
|
@Inject(DocumentLinkModel.name)
|
|
private readonly documentLinkModel: TenantModelProxy<
|
|
typeof DocumentLinkModel
|
|
>,
|
|
) {}
|
|
|
|
/**
|
|
* Deletes the give file attachment file key.
|
|
* @param {string} filekey
|
|
*/
|
|
async delete(filekey: string): Promise<void> {
|
|
const foundDocument = await this.documentModel()
|
|
.query()
|
|
.findOne('key', filekey)
|
|
.throwIfNotFound();
|
|
|
|
const params = {
|
|
Bucket: this.configService.get('s3.bucket'),
|
|
Key: filekey,
|
|
};
|
|
await this.s3Client.send(new DeleteObjectCommand(params));
|
|
|
|
await this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
|
// Delete all document links
|
|
await this.documentLinkModel()
|
|
.query(trx)
|
|
.where('documentId', foundDocument.id)
|
|
.delete();
|
|
|
|
// Delete thedocument.
|
|
await this.documentModel().query(trx).findById(foundDocument.id).delete();
|
|
});
|
|
}
|
|
}
|