Skip to content

Commit dc000d2

Browse files
authored
Merge pull request iKa-h#54 from No-bodyq/feat/resource-owner-guard
Implement resource ownership checks for chat messages, escrows, orders, and payment methods; add app_role enum and role column to AppUser model
2 parents 346b1ab + 4e483bc commit dc000d2

13 files changed

Lines changed: 935 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- CreateEnum
2+
CREATE TYPE "app_role" AS ENUM ('user', 'admin', 'support');
3+
4+
-- AlterTable
5+
ALTER TABLE "app_user" ADD COLUMN "role" "app_role" NOT NULL DEFAULT 'user';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "escrowonchain" ADD COLUMN "evidence_url" TEXT;

prisma/schema.prisma

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ model AppUser {
1313
publicKey String @unique @map("public_key")
1414
alias String? @unique @map("alias")
1515
username String? @map("username")
16+
role app_role @default(user) @map("role")
1617
kycStatus kyc_status @default(pending) @map("kyc_status")
1718
kycUpdatedAt DateTime? @map("kyc_updated_at") @db.Timestamptz(6)
1819
totalVolume Decimal @default(0) @map("total_volume") @db.Decimal(20, 7)
@@ -141,6 +142,12 @@ model payment_provider {
141142
@@index([type], map: "idx_payment_provider_type")
142143
}
143144

145+
enum app_role {
146+
user
147+
admin
148+
support
149+
}
150+
144151
enum kyc_status {
145152
pending
146153
approved
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Reflector } from '@nestjs/core';
2+
import { ResourceOwner, RESOURCE_OWNER_KEY } from './resource-owner.decorator';
3+
import {
4+
ResourceOwnerMetadata,
5+
ResourceType,
6+
} from '../interfaces/resource-owner.interface';
7+
8+
function getHandlerMetadata(target: object): ResourceOwnerMetadata | undefined {
9+
return Reflect.getMetadata(RESOURCE_OWNER_KEY, target) as
10+
| ResourceOwnerMetadata
11+
| undefined;
12+
}
13+
14+
describe('ResourceOwner decorator', () => {
15+
it('sets RESOURCE_OWNER_KEY metadata with the given type and default paramKey', () => {
16+
class TestController {
17+
@ResourceOwner(ResourceType.ORDER)
18+
handler() {}
19+
}
20+
21+
// eslint-disable-next-line @typescript-eslint/unbound-method
22+
const metadata = getHandlerMetadata(TestController.prototype.handler);
23+
24+
expect(metadata).toEqual({ type: ResourceType.ORDER, paramKey: 'id' });
25+
});
26+
27+
it('sets a custom paramKey when provided', () => {
28+
class TestController {
29+
@ResourceOwner(ResourceType.PAYMENT_METHOD, 'paymentId')
30+
handler() {}
31+
}
32+
33+
// eslint-disable-next-line @typescript-eslint/unbound-method
34+
const metadata = getHandlerMetadata(TestController.prototype.handler);
35+
36+
expect(metadata).toEqual({
37+
type: ResourceType.PAYMENT_METHOD,
38+
paramKey: 'paymentId',
39+
});
40+
});
41+
42+
it('is readable through Reflector.get, exactly as the guard reads it', () => {
43+
class TestController {
44+
@ResourceOwner(ResourceType.ESCROW, 'escrowId')
45+
handler() {}
46+
}
47+
48+
const reflector = new Reflector();
49+
const metadata = reflector.get<ResourceOwnerMetadata | undefined>(
50+
RESOURCE_OWNER_KEY,
51+
// eslint-disable-next-line @typescript-eslint/unbound-method
52+
TestController.prototype.handler,
53+
);
54+
55+
expect(metadata).toEqual({
56+
type: ResourceType.ESCROW,
57+
paramKey: 'escrowId',
58+
});
59+
});
60+
61+
it('leaves undecorated handlers without metadata', () => {
62+
class TestController {
63+
handler() {}
64+
}
65+
66+
// eslint-disable-next-line @typescript-eslint/unbound-method
67+
const metadata = getHandlerMetadata(TestController.prototype.handler);
68+
69+
expect(metadata).toBeUndefined();
70+
});
71+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { SetMetadata } from '@nestjs/common';
2+
import {
3+
ResourceOwnerMetadata,
4+
ResourceType,
5+
} from '../interfaces/resource-owner.interface';
6+
7+
export const RESOURCE_OWNER_KEY = 'resource_owner';
8+
9+
export const ResourceOwner = (type: ResourceType, paramKey = 'id') =>
10+
SetMetadata<string, ResourceOwnerMetadata>(RESOURCE_OWNER_KEY, {
11+
type,
12+
paramKey,
13+
});

0 commit comments

Comments
 (0)