Skip to content

Commit 220eceb

Browse files
committed
fix: add hook to check global or project permissions for non-project-specific routes gf-583
1 parent 2c82f82 commit 220eceb

File tree

6 files changed

+90
-14
lines changed

6 files changed

+90
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {
2+
ExceptionMessage,
3+
type PermissionKey,
4+
type ProjectPermissionKey,
5+
} from "~/libs/enums/enums.js";
6+
import { checkHasPermission } from "~/libs/helpers/helpers.js";
7+
import { type APIPreHandler } from "~/libs/modules/controller/controller.js";
8+
import { HTTPCode, HTTPError } from "~/libs/modules/http/http.js";
9+
import { type UserAuthResponseDto } from "~/modules/users/users.js";
10+
11+
import { type ValueOf } from "../types/types.js";
12+
13+
const checkGlobalOrProjectPermissions = (
14+
rootPermissions: ValueOf<typeof PermissionKey>[],
15+
projectPermissions: ValueOf<typeof ProjectPermissionKey>[],
16+
): APIPreHandler => {
17+
return (options, done): void => {
18+
const user = options.user as UserAuthResponseDto;
19+
20+
const userPermissions = user.groups.flatMap((group) => group.permissions);
21+
22+
const hasRootPermission = checkHasPermission(
23+
rootPermissions,
24+
userPermissions,
25+
);
26+
27+
if (hasRootPermission) {
28+
done();
29+
30+
return;
31+
}
32+
33+
const userProjectPermissions = user.projectGroups.flatMap(
34+
(group) => group.permissions,
35+
);
36+
const hasProjectPermission = checkHasPermission(
37+
projectPermissions,
38+
userProjectPermissions,
39+
);
40+
41+
if (!hasProjectPermission) {
42+
throw new HTTPError({
43+
message: ExceptionMessage.NO_PERMISSION,
44+
status: HTTPCode.FORBIDDEN,
45+
});
46+
}
47+
48+
done();
49+
};
50+
};
51+
52+
export { checkGlobalOrProjectPermissions };

apps/backend/src/libs/hooks/check-user-permissions.hook.ts

+26-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import {
2+
ExceptionMessage,
23
type PermissionKey,
34
type ProjectPermissionKey,
45
} from "~/libs/enums/enums.js";
5-
import { checkPermissions } from "~/libs/helpers/helpers.js";
6+
import { checkHasPermission } from "~/libs/helpers/helpers.js";
67
import {
78
type APIHandlerOptions,
89
type APIPreHandler,
910
} from "~/libs/modules/controller/controller.js";
11+
import { HTTPCode, HTTPError } from "~/libs/modules/http/http.js";
1012
import { type UserAuthResponseDto } from "~/modules/users/users.js";
1113

1214
import { type ValueOf } from "../types/types.js";
@@ -20,12 +22,29 @@ const checkUserPermissions = (
2022
const user = options.user as UserAuthResponseDto;
2123
const projectId = getProjectId?.(options);
2224

23-
checkPermissions({
24-
projectId: projectId ?? null,
25-
projectsPermissions: projectsPermissions ?? null,
26-
rootPermissions: permissions,
27-
user,
28-
});
25+
const userPermissions = user.groups.flatMap((group) => group.permissions);
26+
const projectPermissions = projectId
27+
? user.projectGroups
28+
.filter((group) => group.projectId === projectId)
29+
.flatMap((projectGroup) => projectGroup.permissions)
30+
: [];
31+
32+
const hasGlobalPermission = checkHasPermission(
33+
permissions,
34+
userPermissions,
35+
);
36+
37+
const hasProjectPermission =
38+
projectId && projectsPermissions
39+
? checkHasPermission(projectsPermissions, projectPermissions)
40+
: false;
41+
42+
if (!hasGlobalPermission && (!projectId || !hasProjectPermission)) {
43+
throw new HTTPError({
44+
message: ExceptionMessage.NO_PERMISSION,
45+
status: HTTPCode.FORBIDDEN,
46+
});
47+
}
2948

3049
done();
3150
};

apps/backend/src/libs/hooks/hooks.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export { checkGlobalOrProjectPermissions } from "./check-globa-project-permission.hook.js";
12
export { checkUserPermissions } from "./check-user-permissions.hook.js";

apps/backend/src/modules/activity-logs/activity-log.controller.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
ProjectPermissionKey,
55
} from "~/libs/enums/enums.js";
66
import { checkHasPermission } from "~/libs/helpers/helpers.js";
7-
import { checkUserPermissions } from "~/libs/hooks/hooks.js";
7+
import { checkGlobalOrProjectPermissions } from "~/libs/hooks/hooks.js";
88
import {
99
type APIHandlerOptions,
1010
type APIHandlerResponse,
@@ -100,7 +100,7 @@ class ActivityLogController extends BaseController {
100100
method: "GET",
101101
path: ActivityLogsApiPath.ROOT,
102102
preHandlers: [
103-
checkUserPermissions(
103+
checkGlobalOrProjectPermissions(
104104
[PermissionKey.VIEW_ALL_PROJECTS, PermissionKey.MANAGE_ALL_PROJECTS],
105105
[
106106
ProjectPermissionKey.VIEW_PROJECT,

apps/backend/src/modules/projects/project.controller.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import {
44
ProjectPermissionKey,
55
} from "~/libs/enums/enums.js";
66
import { checkHasPermission } from "~/libs/helpers/helpers.js";
7-
import { checkUserPermissions } from "~/libs/hooks/hooks.js";
7+
import {
8+
checkGlobalOrProjectPermissions,
9+
checkUserPermissions,
10+
} from "~/libs/hooks/hooks.js";
811
import {
912
type APIHandlerOptions,
1013
type APIHandlerResponse,
@@ -104,13 +107,13 @@ class ProjectController extends BaseController {
104107
method: "GET",
105108
path: ProjectsApiPath.ROOT,
106109
preHandlers: [
107-
checkUserPermissions(
108-
[PermissionKey.VIEW_ALL_PROJECTS, PermissionKey.MANAGE_ALL_PROJECTS],
110+
checkGlobalOrProjectPermissions(
111+
[PermissionKey.VIEW_ALL_PROJECTS, PermissionKey.MANAGE_ALL_PROJECTS], // Root permissions
109112
[
110113
ProjectPermissionKey.VIEW_PROJECT,
111114
ProjectPermissionKey.EDIT_PROJECT,
112115
ProjectPermissionKey.MANAGE_PROJECT,
113-
],
116+
], // Project-level permissions
114117
),
115118
],
116119
});

apps/backend/src/modules/users/user.controller.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
ProjectPermissionKey,
55
} from "~/libs/enums/enums.js";
66
import { checkUserPermissions } from "~/libs/hooks/check-user-permissions.hook.js";
7+
import { checkGlobalOrProjectPermissions } from "~/libs/hooks/hooks.js";
78
import {
89
type APIHandlerOptions,
910
type APIHandlerResponse,
@@ -57,7 +58,7 @@ class UserController extends BaseController {
5758
method: "GET",
5859
path: UsersApiPath.ROOT,
5960
preHandlers: [
60-
checkUserPermissions(
61+
checkGlobalOrProjectPermissions(
6162
[PermissionKey.MANAGE_USER_ACCESS, PermissionKey.MANAGE_ALL_PROJECTS],
6263
[ProjectPermissionKey.MANAGE_PROJECT],
6364
),

0 commit comments

Comments
 (0)