Skip to content

Commit 265014e

Browse files
committed
simplifiy
1 parent ad124f8 commit 265014e

File tree

1 file changed

+68
-46
lines changed

1 file changed

+68
-46
lines changed

src/permissions/permission-check/permission-check.controller.ts

Lines changed: 68 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
BadRequestException,
44
Body,
55
Controller,
6+
HttpCode,
67
HttpException,
78
Logger,
89
Post,
@@ -57,8 +58,9 @@ export class PermissionCheckController {
5758
description: 'List of user ids and one target document to evaluate.',
5859
type: PermissionCheckRequestDto,
5960
})
61+
@HttpCode(200)
6062
@ApiResponse({
61-
status: 201,
63+
status: 200,
6264
description: 'Permission result for each user id.',
6365
schema: {
6466
type: 'object',
@@ -84,12 +86,28 @@ export class PermissionCheckController {
8486
})
8587
@ApiUnauthorizedResponse({ description: 'Authentication required.' })
8688
async checkPermissions(@Body() body: PermissionCheckRequestDto) {
89+
this.logPermissionCheckRequest(body);
90+
this.validatePermissionCheckRequest(body);
91+
92+
const action: Action = body.action ?? 'read';
93+
const entityDoc = await this.loadCanonicalEntityDoc(body.entityId);
94+
95+
const results = await Promise.all(
96+
body.userIds.map((userId) => this.evaluatePermissionForUser(userId, action, entityDoc)),
97+
);
98+
99+
return Object.fromEntries(results);
100+
}
101+
102+
private logPermissionCheckRequest(body: PermissionCheckRequestDto) {
87103
this.logger.debug(
88104
`Incoming permission check: userCount=${Array.isArray(body?.userIds) ? body.userIds.length : 0}, ` +
89105
`entityId=${body?.entityId}, action=${body?.action ?? 'read'}, ` +
90106
`body keys=${body ? Object.keys(body) : 'null'}`,
91107
);
108+
}
92109

110+
private validatePermissionCheckRequest(body: PermissionCheckRequestDto) {
93111
if (
94112
!Array.isArray(body?.userIds) ||
95113
body.userIds.length === 0 ||
@@ -112,55 +130,59 @@ export class PermissionCheckController {
112130
) {
113131
throw new BadRequestException('action is invalid');
114132
}
133+
}
115134

116-
const action: Action = body.action ?? 'read';
117-
const entityDoc = await this.loadCanonicalEntityDoc(body.entityId);
135+
private async evaluatePermissionForUser(
136+
userId: string,
137+
action: Action,
138+
entityDoc: unknown,
139+
) {
140+
try {
141+
const user = await this.userIdentityService.resolveUser(userId);
142+
const permitted = await this.permissionService.isAllowedTo(
143+
action,
144+
entityDoc,
145+
user,
146+
'app',
147+
);
118148

119-
const results = await Promise.all(
120-
body.userIds.map(async (userId) => {
121-
try {
122-
const user = await this.userIdentityService.resolveUser(userId);
123-
const permitted = await this.permissionService.isAllowedTo(
124-
action,
125-
entityDoc,
126-
user,
127-
'app',
128-
);
129-
130-
return [userId, { permitted }] as const;
131-
} catch (error) {
132-
// Infrastructure failure: Keycloak unreachable or returned a server error → fail the whole batch
133-
if (
134-
error instanceof AxiosError &&
135-
(!error.response || error.response.status >= 500)
136-
) {
137-
throw new BadGatewayException(
138-
'Upstream identity provider is unavailable',
139-
);
140-
}
141-
142-
// User not found in Keycloak (404) or bad user ID format (400)
143-
if (
144-
(error instanceof AxiosError &&
145-
error.response?.status >= 400 &&
146-
error.response?.status < 500) ||
147-
(error instanceof HttpException &&
148-
error.getStatus() >= 400 &&
149-
error.getStatus() < 500)
150-
) {
151-
return [userId, { permitted: false, error: 'NOT_FOUND' }] as const;
152-
}
153-
154-
this.logger.error(
155-
`Failed to evaluate permissions for user ${userId}`,
156-
error?.stack || error,
157-
);
158-
return [userId, { permitted: false, error: 'ERROR' }] as const;
159-
}
160-
}),
149+
return [userId, { permitted }] as const;
150+
} catch (error) {
151+
return this.handlePermissionEvaluationError(userId, error);
152+
}
153+
}
154+
155+
private handlePermissionEvaluationError(userId: string, error: unknown) {
156+
// Infrastructure failure: Keycloak unreachable or returned a server error -> fail the whole batch
157+
if (
158+
error instanceof AxiosError &&
159+
(!error.response || error.response.status >= 500)
160+
) {
161+
throw new BadGatewayException('Upstream identity provider is unavailable');
162+
}
163+
164+
// User not found in Keycloak (404) or bad user ID format (400)
165+
if (this.isClientError(error)) {
166+
return [userId, { permitted: false, error: 'NOT_FOUND' }] as const;
167+
}
168+
169+
this.logger.error(
170+
`Failed to evaluate permissions for user ${userId}`,
171+
error instanceof Error ? error.stack || error.message : String(error),
161172
);
173+
return [userId, { permitted: false, error: 'ERROR' }] as const;
174+
}
162175

163-
return Object.fromEntries(results);
176+
private isClientError(error: unknown) {
177+
return (
178+
(error instanceof AxiosError &&
179+
error.response?.status !== undefined &&
180+
error.response.status >= 400 &&
181+
error.response.status < 500) ||
182+
(error instanceof HttpException &&
183+
error.getStatus() >= 400 &&
184+
error.getStatus() < 500)
185+
);
164186
}
165187

166188
private async loadCanonicalEntityDoc(entityId: string) {

0 commit comments

Comments
 (0)