-
Notifications
You must be signed in to change notification settings - Fork 12.1k
Expand file tree
/
Copy pathpermissions.guard.ts
More file actions
95 lines (82 loc) · 4.05 KB
/
permissions.guard.ts
File metadata and controls
95 lines (82 loc) · 4.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import { X_CAL_CLIENT_ID } from "@calcom/platform-constants";
import { hasPermissions } from "@calcom/platform-utils";
import type { PlatformOAuthClient } from "@calcom/prisma/client";
import { CanActivate, ExecutionContext, ForbiddenException, Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Reflector } from "@nestjs/core";
import { getToken } from "next-auth/jwt";
import { isApiKey } from "@/lib/api-key";
import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
import { OAuthClientsOutputService } from "@/modules/oauth-clients/services/oauth-clients/oauth-clients-output.service";
import { TokensRepository } from "@/modules/tokens/tokens.repository";
import { TokensService } from "@/modules/tokens/tokens.service";
@Injectable()
export class PermissionsGuard implements CanActivate {
constructor(
private reflector: Reflector,
private tokensRepository: TokensRepository,
private tokensService: TokensService,
private readonly config: ConfigService,
private readonly oAuthClientRepository: OAuthClientRepository,
private readonly oAuthClientsOutputService: OAuthClientsOutputService
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const requiredPermissions = this.reflector.get(Permissions, context.getHandler());
if (!requiredPermissions?.length || !Object.keys(requiredPermissions)?.length) {
return true;
}
const request = context.switchToHttp().getRequest();
const bearerToken = request.get("Authorization")?.replace("Bearer ", "");
const nextAuthSecret = this.config.get("next.authSecret", { infer: true });
const nextAuthToken = await getToken({ req: request, secret: nextAuthSecret });
const oAuthClientId = request.params?.clientId || request.get(X_CAL_CLIENT_ID);
const apiKey = bearerToken && isApiKey(bearerToken, this.config.get("api.apiKeyPrefix") ?? "cal_");
const decodedThirdPartyToken = bearerToken ? this.getDecodedThirdPartyAccessToken(bearerToken) : null;
if (nextAuthToken || apiKey || decodedThirdPartyToken) {
return true;
}
if (!bearerToken && !oAuthClientId) {
throw new ForbiddenException(
"PermissionsGuard - no authentication provided. Provide either authorization bearer token containing managed user access token or oAuth client id in 'x-cal-client-id' header."
);
}
const oAuthClient = bearerToken
? await this.getOAuthClientByAccessToken(bearerToken)
: await this.getOAuthClientById(oAuthClientId);
const hasRequiredPermissions = hasPermissions(oAuthClient.permissions, [...requiredPermissions]);
if (!hasRequiredPermissions) {
throw new ForbiddenException(
`PermissionsGuard - oAuth client with id=${
oAuthClient.id
} does not have the required permissions=${requiredPermissions
.map((permission) => this.oAuthClientsOutputService.transformOAuthClientPermission(permission))
.join(
", "
)}. Go to platform dashboard settings and add the required permissions to the oAuth client.`
);
}
return true;
}
async getOAuthClientByAccessToken(
accessToken: string
): Promise<Pick<PlatformOAuthClient, "id" | "permissions">> {
const oAuthClient = await this.tokensRepository.getAccessTokenClient(accessToken);
if (!oAuthClient) {
throw new ForbiddenException(
`PermissionsGuard - no oAuth client found for access token=${accessToken}`
);
}
return oAuthClient;
}
async getOAuthClientById(id: string): Promise<Pick<PlatformOAuthClient, "id" | "permissions">> {
const oAuthClient = await this.oAuthClientRepository.getOAuthClient(id);
if (!oAuthClient) {
throw new ForbiddenException(`PermissionsGuard - no oAuth client found for client id=${id}`);
}
return oAuthClient;
}
getDecodedThirdPartyAccessToken(bearerToken: string) {
return this.tokensService.getDecodedThirdPartyAccessToken(bearerToken);
}
}