fix!: unauthorized face creation (#28561)
* fix: unauthorized face creation * review changes --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>workflow-webhook-step
parent
408e1180ca
commit
9287fa08c6
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Kysely, sql } from 'kysely';
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
// Delete unauthorized cross-owner asset faces
|
||||||
|
await sql`
|
||||||
|
DELETE FROM asset_face
|
||||||
|
USING person, asset
|
||||||
|
WHERE asset_face."personId" = person.id
|
||||||
|
AND asset_face."assetId" = asset.id
|
||||||
|
AND person."ownerId" != asset."ownerId"
|
||||||
|
`.execute(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(): Promise<void> {
|
||||||
|
// Not implemented: the deleted rows were unauthorized cross-owner entries
|
||||||
|
}
|
||||||
|
|
@ -452,6 +452,30 @@ describe(PersonService.name, () => {
|
||||||
expect(mocks.person.update).not.toHaveBeenCalled();
|
expect(mocks.person.update).not.toHaveBeenCalled();
|
||||||
expect(mocks.job.queueAll).not.toHaveBeenCalled();
|
expect(mocks.job.queueAll).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should reject creating a face on an asset the user does not own', async () => {
|
||||||
|
const auth = AuthFactory.create();
|
||||||
|
const asset = AssetFactory.create();
|
||||||
|
const person = PersonFactory.create({ faceAssetId: null });
|
||||||
|
|
||||||
|
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set());
|
||||||
|
mocks.access.person.checkOwnerAccess.mockResolvedValue(new Set([person.id]));
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
sut.createFace(auth, {
|
||||||
|
assetId: asset.id,
|
||||||
|
personId: person.id,
|
||||||
|
imageHeight: 500,
|
||||||
|
imageWidth: 400,
|
||||||
|
x: 10,
|
||||||
|
y: 20,
|
||||||
|
width: 100,
|
||||||
|
height: 110,
|
||||||
|
}),
|
||||||
|
).rejects.toBeInstanceOf(BadRequestException);
|
||||||
|
|
||||||
|
expect(mocks.person.createAssetFace).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createNewFeaturePhoto', () => {
|
describe('createNewFeaturePhoto', () => {
|
||||||
|
|
|
||||||
|
|
@ -625,7 +625,7 @@ export class PersonService extends BaseService {
|
||||||
// TODO return a asset face response
|
// TODO return a asset face response
|
||||||
async createFace(auth: AuthDto, dto: AssetFaceCreateDto): Promise<void> {
|
async createFace(auth: AuthDto, dto: AssetFaceCreateDto): Promise<void> {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.requireAccess({ auth, permission: Permission.AssetRead, ids: [dto.assetId] }),
|
this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: [dto.assetId] }),
|
||||||
this.requireAccess({ auth, permission: Permission.PersonRead, ids: [dto.personId] }),
|
this.requireAccess({ auth, permission: Permission.PersonRead, ids: [dto.personId] }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue