because I got bored of customising my CV for every job
1import type { BaseEntity } from "@cv/system";
2import { Injectable, type Type } from "@nestjs/common";
3import {
4 CannotCreateError,
5 CannotDeleteError,
6 CannotUpdateError,
7 CannotViewError,
8} from "../errors/authorization.error";
9import type { User } from "../user/user.entity";
10import { PolicyRegistry } from "./policy-registry.service";
11
12@Injectable()
13export class AuthorizationService {
14 constructor(private readonly policyRegistry: PolicyRegistry) {}
15
16 async canView<TResource extends BaseEntity>(
17 userContext: User,
18 resource: TResource,
19 resourceType?: Type<TResource>,
20 ): Promise<void> {
21 const type = resourceType ?? (resource.constructor as Type<TResource>);
22 const policy = this.policyRegistry.getPolicy<TResource>(type);
23 const allowed = await policy.view(userContext, resource);
24 if (!allowed) {
25 throw new CannotViewError(type.name);
26 }
27 }
28
29 async canCreate<TResource extends BaseEntity>(
30 userContext: User,
31 resourceType: Type<TResource>,
32 resource?: Partial<TResource>,
33 ): Promise<void> {
34 const policy = this.policyRegistry.getPolicy<TResource>(resourceType);
35 const allowed = await policy.create(userContext, resource);
36 if (!allowed) {
37 throw new CannotCreateError(resourceType.name);
38 }
39 }
40
41 async canUpdate<TResource extends BaseEntity>(
42 userContext: User,
43 resource: TResource,
44 resourceType?: Type<TResource>,
45 ): Promise<void> {
46 const type = resourceType ?? (resource.constructor as Type<TResource>);
47 const policy = this.policyRegistry.getPolicy<TResource>(type);
48 const allowed = await policy.update(userContext, resource);
49 if (!allowed) {
50 throw new CannotUpdateError(type.name);
51 }
52 }
53
54 async canDelete<TResource extends BaseEntity>(
55 userContext: User,
56 resource: TResource,
57 resourceType?: Type<TResource>,
58 ): Promise<void> {
59 const type = resourceType ?? (resource.constructor as Type<TResource>);
60 const policy = this.policyRegistry.getPolicy<TResource>(type);
61 const allowed = await policy.delete(userContext, resource);
62 if (!allowed) {
63 throw new CannotDeleteError(type.name);
64 }
65 }
66
67 isSameUser(userContext: User, userId: string): boolean {
68 return userContext.id === userId;
69 }
70}