Skip to content

Commit f950d95

Browse files
committed
refactor(auth): update resource mapping from "portal" to "trust" in permissions
1 parent 8645156 commit f950d95

File tree

138 files changed

+4336
-718
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

138 files changed

+4336
-718
lines changed

apps/api/src/audit/audit-log.interceptor.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,10 +381,10 @@ describe('AuditLogInterceptor', () => {
381381
});
382382
});
383383

384-
it('should map resource "portal" to entity type "trust"', (done) => {
384+
it('should map resource "trust" to entity type "trust"', (done) => {
385385
jest.spyOn(reflector, 'getAllAndOverride').mockImplementation((key) => {
386386
if (key === PERMISSIONS_KEY) {
387-
return [{ resource: 'portal', actions: ['update'] }];
387+
return [{ resource: 'trust', actions: ['update'] }];
388388
}
389389
if (key === SKIP_AUDIT_LOG_KEY) return false;
390390
return undefined;

apps/api/src/auth/auth.server.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const statement = {
4040
questionnaire: ['create', 'read', 'update', 'delete', 'respond'],
4141
integration: ['create', 'read', 'update', 'delete'],
4242
app: ['read'],
43-
portal: ['read', 'update'],
43+
trust: ['read', 'update'],
4444
} as const;
4545

4646
const ac = createAccessControl(statement);
@@ -60,7 +60,7 @@ const owner = ac.newRole({
6060
questionnaire: ['create', 'read', 'update', 'delete', 'respond'],
6161
integration: ['create', 'read', 'update', 'delete'],
6262
app: ['read'],
63-
portal: ['read', 'update'],
63+
trust: ['read', 'update'],
6464
});
6565

6666
const admin = ac.newRole({
@@ -78,7 +78,7 @@ const admin = ac.newRole({
7878
questionnaire: ['create', 'read', 'update', 'delete', 'respond'],
7979
integration: ['create', 'read', 'update', 'delete'],
8080
app: ['read'],
81-
portal: ['read', 'update'],
81+
trust: ['read', 'update'],
8282
});
8383

8484
const auditor = ac.newRole({
@@ -97,22 +97,22 @@ const auditor = ac.newRole({
9797
questionnaire: ['read'],
9898
integration: ['read'],
9999
app: ['read'],
100-
portal: ['read'],
100+
trust: ['read'],
101101
});
102102

103103
const employee = ac.newRole({
104104
task: ['read', 'complete'],
105105
evidence: ['read', 'upload'],
106106
policy: ['read'],
107107
questionnaire: ['read', 'respond'],
108-
portal: ['read', 'update'],
108+
trust: ['read', 'update'],
109109
});
110110

111111
const contractor = ac.newRole({
112112
task: ['read', 'complete'],
113113
evidence: ['read', 'upload'],
114114
policy: ['read'],
115-
portal: ['read', 'update'],
115+
trust: ['read', 'update'],
116116
});
117117

118118
const allRoles = {

apps/api/src/auth/require-permission.decorator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export type GRCResource =
6868
| 'cloud-security'
6969
| 'training'
7070
| 'app'
71-
| 'portal';
71+
| 'trust';
7272

7373
/**
7474
* Action types available for GRC resources

apps/api/src/people/people.controller.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,6 @@ export class PeopleController {
410410
}
411411

412412
@Get('me/email-preferences')
413-
@RequirePermission('member', 'read')
414413
@ApiOperation({ summary: 'Get current user email notification preferences' })
415414
async getEmailPreferences(
416415
@AuthContext() authContext: AuthContextType,
@@ -430,7 +429,6 @@ export class PeopleController {
430429
}
431430

432431
@Put('me/email-preferences')
433-
@RequirePermission('member', 'read')
434432
@ApiOperation({ summary: 'Update current user email notification preferences' })
435433
async updateEmailPreferences(
436434
@AuthContext() authContext: AuthContextType,

apps/api/src/roles/roles.controller.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
Param,
77
Patch,
88
Post,
9+
Query,
910
UseGuards,
1011
} from '@nestjs/common';
1112
import {
@@ -120,6 +121,46 @@ export class RolesController {
120121
return this.rolesService.listRoles(organizationId);
121122
}
122123

124+
@Get('permissions')
125+
@RequirePermission('app', 'read')
126+
@ApiOperation({
127+
summary: 'Resolve permissions for custom roles',
128+
description:
129+
'Returns the merged permissions for the given custom role names. Used by the frontend to resolve effective permissions for users with custom roles.',
130+
})
131+
@ApiResponse({
132+
status: 200,
133+
description: 'Merged permissions for the requested roles',
134+
schema: {
135+
type: 'object',
136+
properties: {
137+
permissions: {
138+
type: 'object',
139+
additionalProperties: {
140+
type: 'array',
141+
items: { type: 'string' },
142+
},
143+
},
144+
},
145+
},
146+
})
147+
@ApiResponse({ status: 401, description: 'Unauthorized' })
148+
async getPermissionsForRoles(
149+
@OrganizationId() organizationId: string,
150+
@Query('roles') roles: string,
151+
) {
152+
const roleNames = (roles || '')
153+
.split(',')
154+
.map((r) => r.trim())
155+
.filter(Boolean);
156+
const permissions =
157+
await this.rolesService.getPermissionsForRoles(
158+
organizationId,
159+
roleNames,
160+
);
161+
return { permissions };
162+
}
163+
123164
@Get(':roleId')
124165
@RequirePermission('ac', 'read')
125166
@ApiOperation({

apps/api/src/roles/roles.service.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const VALID_RESOURCES: Record<string, string[]> = {
2020
questionnaire: ['create', 'read', 'update', 'delete', 'respond'],
2121
integration: ['create', 'read', 'update', 'delete'],
2222
app: ['read'],
23-
portal: ['read', 'update'],
23+
trust: ['read', 'update'],
2424
};
2525

2626
// Built-in roles that cannot be modified or deleted
@@ -142,20 +142,20 @@ export class RolesService {
142142
questionnaire: ['read'],
143143
integration: ['read'],
144144
app: ['read'],
145-
portal: ['read'],
145+
trust: ['read'],
146146
},
147147
employee: {
148148
task: ['read', 'complete'],
149149
evidence: ['read', 'upload'],
150150
policy: ['read'],
151151
questionnaire: ['read', 'respond'],
152-
portal: ['read', 'update'],
152+
trust: ['read', 'update'],
153153
},
154154
contractor: {
155155
task: ['read', 'complete'],
156156
evidence: ['read', 'upload'],
157157
policy: ['read'],
158-
portal: ['read', 'update'],
158+
trust: ['read', 'update'],
159159
},
160160
};
161161
return builtInPermissions[roleName] || {};
@@ -410,6 +410,46 @@ export class RolesService {
410410
return { success: true, message: `Role '${role.name}' deleted` };
411411
}
412412

413+
/**
414+
* Get merged permissions for a list of custom role names.
415+
* Used by the frontend to resolve effective permissions for custom roles.
416+
*/
417+
async getPermissionsForRoles(
418+
organizationId: string,
419+
roleNames: string[],
420+
): Promise<Record<string, string[]>> {
421+
if (roleNames.length === 0) return {};
422+
423+
const customRoles = await db.organizationRole.findMany({
424+
where: {
425+
organizationId,
426+
name: { in: roleNames },
427+
},
428+
});
429+
430+
const combined: Record<string, string[]> = {};
431+
for (const role of customRoles) {
432+
const perms =
433+
typeof role.permissions === 'string'
434+
? JSON.parse(role.permissions)
435+
: role.permissions;
436+
for (const [resource, actions] of Object.entries(
437+
perms as Record<string, string[]>,
438+
)) {
439+
if (!combined[resource]) {
440+
combined[resource] = [];
441+
}
442+
for (const action of actions) {
443+
if (!combined[resource].includes(action)) {
444+
combined[resource].push(action);
445+
}
446+
}
447+
}
448+
}
449+
450+
return combined;
451+
}
452+
413453
/**
414454
* Get description for built-in roles
415455
*/

apps/api/src/trust-portal/trust-access.controller.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class TrustAccessController {
7979

8080
@Get('admin/requests')
8181
@UseGuards(HybridAuthGuard, PermissionGuard)
82-
@RequirePermission('portal', 'read')
82+
@RequirePermission('trust', 'read')
8383
@ApiSecurity('apikey')
8484
@HttpCode(HttpStatus.OK)
8585
@ApiOperation({
@@ -99,7 +99,7 @@ export class TrustAccessController {
9999

100100
@Get('admin/requests/:id')
101101
@UseGuards(HybridAuthGuard, PermissionGuard)
102-
@RequirePermission('portal', 'read')
102+
@RequirePermission('trust', 'read')
103103
@ApiSecurity('apikey')
104104
@HttpCode(HttpStatus.OK)
105105
@ApiOperation({
@@ -119,7 +119,7 @@ export class TrustAccessController {
119119

120120
@Post('admin/requests/:id/approve')
121121
@UseGuards(HybridAuthGuard, PermissionGuard)
122-
@RequirePermission('portal', 'update')
122+
@RequirePermission('trust', 'update')
123123
@ApiSecurity('apikey')
124124
@HttpCode(HttpStatus.OK)
125125
@ApiOperation({
@@ -154,7 +154,7 @@ export class TrustAccessController {
154154

155155
@Post('admin/requests/:id/deny')
156156
@UseGuards(HybridAuthGuard, PermissionGuard)
157-
@RequirePermission('portal', 'update')
157+
@RequirePermission('trust', 'update')
158158
@ApiSecurity('apikey')
159159
@HttpCode(HttpStatus.OK)
160160
@ApiOperation({
@@ -186,7 +186,7 @@ export class TrustAccessController {
186186

187187
@Get('admin/grants')
188188
@UseGuards(HybridAuthGuard, PermissionGuard)
189-
@RequirePermission('portal', 'read')
189+
@RequirePermission('trust', 'read')
190190
@ApiSecurity('apikey')
191191
@HttpCode(HttpStatus.OK)
192192
@ApiOperation({
@@ -200,7 +200,7 @@ export class TrustAccessController {
200200

201201
@Post('admin/grants/:id/revoke')
202202
@UseGuards(HybridAuthGuard, PermissionGuard)
203-
@RequirePermission('portal', 'update')
203+
@RequirePermission('trust', 'update')
204204
@ApiSecurity('apikey')
205205
@HttpCode(HttpStatus.OK)
206206
@ApiOperation({
@@ -232,7 +232,7 @@ export class TrustAccessController {
232232

233233
@Post('admin/grants/:id/resend-access-email')
234234
@UseGuards(HybridAuthGuard, PermissionGuard)
235-
@RequirePermission('portal', 'update')
235+
@RequirePermission('trust', 'update')
236236
@ApiSecurity('apikey')
237237
@HttpCode(HttpStatus.OK)
238238
@ApiOperation({
@@ -319,7 +319,7 @@ export class TrustAccessController {
319319

320320
@Post('admin/requests/:id/resend-nda')
321321
@UseGuards(HybridAuthGuard, PermissionGuard)
322-
@RequirePermission('portal', 'update')
322+
@RequirePermission('trust', 'update')
323323
@ApiSecurity('apikey')
324324
@HttpCode(HttpStatus.OK)
325325
@ApiOperation({
@@ -339,7 +339,7 @@ export class TrustAccessController {
339339

340340
@Post('admin/requests/:id/preview-nda')
341341
@UseGuards(HybridAuthGuard, PermissionGuard)
342-
@RequirePermission('portal', 'read')
342+
@RequirePermission('trust', 'read')
343343
@ApiSecurity('apikey')
344344
@HttpCode(HttpStatus.OK)
345345
@ApiOperation({

0 commit comments

Comments
 (0)