Skip to content

Commit 435eab7

Browse files
committed
refactor(auth): add isPlatformAdmin support in authentication and guards
1 parent 6129e7f commit 435eab7

18 files changed

Lines changed: 98 additions & 34 deletions

File tree

apps/api/src/auth/auth-context.decorator.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const AuthContext = createParamDecorator(
1313
organizationId,
1414
authType,
1515
isApiKey,
16+
isPlatformAdmin,
1617
userId,
1718
userEmail,
1819
userRoles,
@@ -30,6 +31,7 @@ export const AuthContext = createParamDecorator(
3031
organizationId,
3132
authType,
3233
isApiKey,
34+
isPlatformAdmin,
3335
userId,
3436
userEmail,
3537
userRoles,

apps/api/src/auth/hybrid-auth.guard.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export class HybridAuthGuard implements CanActivate {
4545
request.organizationId = organizationId;
4646
request.authType = 'api-key';
4747
request.isApiKey = true;
48+
request.isPlatformAdmin = false;
4849
// API keys are organization-scoped and are not tied to a specific user/member.
4950
request.userRoles = null;
5051

@@ -107,6 +108,11 @@ export class HybridAuthGuard implements CanActivate {
107108
id: true,
108109
role: true,
109110
department: true,
111+
user: {
112+
select: {
113+
isPlatformAdmin: true,
114+
},
115+
},
110116
},
111117
});
112118

@@ -124,6 +130,7 @@ export class HybridAuthGuard implements CanActivate {
124130
request.userRoles = userRoles;
125131
request.memberId = member.id;
126132
request.memberDepartment = member.department;
133+
request.isPlatformAdmin = member.user?.isPlatformAdmin ?? false;
127134
request.organizationId = organizationId;
128135
request.authType = 'session';
129136
request.isApiKey = false;

apps/api/src/auth/permission.guard.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ export class PermissionGuard implements CanActivate {
7777
return true;
7878
}
7979

80+
// Platform admins bypass permission checks (full access)
81+
if (request.isPlatformAdmin) {
82+
return true;
83+
}
84+
8085
// Build required permissions map
8186
const permissionBody: Record<string, string[]> = {};
8287
for (const perm of requiredPermissions) {

apps/api/src/auth/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface AuthenticatedRequest extends Request {
66
organizationId: string;
77
authType: 'api-key' | 'session';
88
isApiKey: boolean;
9+
isPlatformAdmin: boolean;
910
userId?: string;
1011
userEmail?: string;
1112
userRoles: string[] | null;
@@ -17,6 +18,7 @@ export interface AuthContext {
1718
organizationId: string;
1819
authType: 'api-key' | 'session';
1920
isApiKey: boolean;
21+
isPlatformAdmin: boolean;
2022
userId?: string; // Only available for session auth
2123
userEmail?: string; // Only available for session auth
2224
userRoles: string[] | null;

apps/api/src/findings/finding-notifier.service.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,10 @@ export class FindingNotifierService {
543543
where: {
544544
organizationId,
545545
deactivated: false,
546-
user: { isPlatformAdmin: false },
546+
OR: [
547+
{ user: { isPlatformAdmin: false } },
548+
{ role: { contains: 'owner' } },
549+
],
547550
},
548551
select: {
549552
user: { select: { id: true, email: true, name: true } },

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ describe('RolesController', () => {
3434
organizationId: 'org_123',
3535
authType: 'session',
3636
isApiKey: false,
37+
isPlatformAdmin: false,
3738
userId: 'usr_123',
3839
userEmail: 'test@example.com',
3940
userRoles: ['owner'],

apps/api/src/tasks/task-notifier.service.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ export class TaskNotifierService {
6464
where: {
6565
organizationId,
6666
deactivated: false,
67-
user: { isPlatformAdmin: false },
67+
OR: [
68+
{ user: { isPlatformAdmin: false } },
69+
{ role: { contains: 'owner' } },
70+
],
6871
},
6972
select: {
7073
id: true,
@@ -233,7 +236,10 @@ export class TaskNotifierService {
233236
where: {
234237
organizationId,
235238
deactivated: false,
236-
user: { isPlatformAdmin: false },
239+
OR: [
240+
{ user: { isPlatformAdmin: false } },
241+
{ role: { contains: 'owner' } },
242+
],
237243
},
238244
select: {
239245
id: true,
@@ -435,7 +441,10 @@ export class TaskNotifierService {
435441
where: {
436442
organizationId,
437443
deactivated: false,
438-
user: { isPlatformAdmin: false },
444+
OR: [
445+
{ user: { isPlatformAdmin: false } },
446+
{ role: { contains: 'owner' } },
447+
],
439448
},
440449
select: {
441450
id: true,
@@ -638,7 +647,10 @@ export class TaskNotifierService {
638647
where: {
639648
organizationId,
640649
deactivated: false,
641-
user: { isPlatformAdmin: false },
650+
OR: [
651+
{ user: { isPlatformAdmin: false } },
652+
{ role: { contains: 'owner' } },
653+
],
642654
},
643655
select: {
644656
id: true,

apps/app/src/app/(app)/[orgId]/components/AppShellWrapper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ function AppShellWrapperContent({
218218
label="Compliance"
219219
/>
220220
</Link>
221-
{isTrustNdaEnabled && (
221+
{isTrustNdaEnabled && canAccessRoute(permissions, 'trust') && (
222222
<Link href={`/${organization.id}/trust`}>
223223
<AppShellRailItem
224224
isActive={isTrustActive}

apps/app/src/app/(app)/[orgId]/people/all/actions/updateMemberRole.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,6 @@ export const updateMemberRole = authActionClient
9595
};
9696
}
9797

98-
// Prevent modifying platform admin members (CX team)
99-
if (targetMember.user.isPlatformAdmin) {
100-
return {
101-
success: false,
102-
error: 'This member is managed by Comp AI and cannot be modified.',
103-
};
104-
}
105-
10698
// Parse the target member's current roles from the string
10799
const currentRoles = parseRolesString(targetMember.role);
108100

apps/app/src/app/(app)/[orgId]/people/all/components/MemberRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ export function MemberRow({
236236
})}
237237
</div>
238238

239-
{!isDeactivated && !isPlatformAdmin && (
239+
{!isDeactivated && (
240240
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
241241
<DropdownMenuTrigger asChild>
242242
<Button variant="ghost" size="sm" className="h-8 w-8 p-0" disabled={!canEdit}>

0 commit comments

Comments
 (0)