Fix server level impersonation (#14831)

Tested : 
- server level ok
- workspace level ok
- server level without canImpersonate or without allow impersonation -
fail ok
- workspace level without permission - fail ok
This commit is contained in:
Etienne
2025-10-02 10:46:59 +02:00
committed by GitHub
parent 3f9c315d93
commit 7b16796a20
3 changed files with 66 additions and 35 deletions
@@ -705,16 +705,31 @@ export class AuthResolver {
impersonatorUserWorkspace.user.canImpersonate === true &&
impersonatorUserWorkspace.workspace.allowImpersonation === true;
if (isServerLevelImpersonation && !hasServerLevelImpersonatePermission) {
if (isServerLevelImpersonation) {
if (!hasServerLevelImpersonatePermission) {
await auditService.insertWorkspaceEvent(MONITORING_EVENT, {
eventName: 'server.impersonation.token_exchange_failed',
message: `Server level impersonation not allowed for ${targetUserEmail} by userId ${impersonatorUserWorkspace.user.id}`,
});
throw new AuthException(
'Server level impersonation not allowed on this workspace',
AuthExceptionCode.FORBIDDEN_EXCEPTION,
);
}
await auditService.insertWorkspaceEvent(MONITORING_EVENT, {
eventName: 'server.impersonation.token_exchange_failed',
message: `Server level impersonation not allowed for ${targetUserEmail} by userId ${impersonatorUserWorkspace.user.id}`,
eventName: `server.impersonation.token_exchange_success`,
message: `Impersonation token exchanged for ${targetUserEmail} by userId ${impersonatorUserWorkspace.user.id}`,
});
throw new AuthException(
'Server level impersonation not allowed on this workspace',
AuthExceptionCode.FORBIDDEN_EXCEPTION,
);
return {
workspaceId: workspace.id,
impersonatorUserWorkspaceId: impersonatorUserWorkspace.id,
impersonatedUserWorkspaceId: toImpersonateUserWorkspace.id,
impersonatorUserId: impersonatorUserWorkspace.user.id,
impersonatedUserId: toImpersonateUserWorkspace.user.id,
};
}
const hasWorkspaceLevelImpersonatePermission =
@@ -724,12 +739,9 @@ export class AuthResolver {
workspaceId: workspace.id,
});
if (
!hasWorkspaceLevelImpersonatePermission &&
!hasServerLevelImpersonatePermission
) {
if (!hasWorkspaceLevelImpersonatePermission) {
await auditService.insertWorkspaceEvent(MONITORING_EVENT, {
eventName: `${isServerLevelImpersonation ? 'server' : 'workspace'}.impersonation.token_exchange_failed`,
eventName: 'workspace.impersonation.token_exchange_failed',
message: `Impersonation not allowed for ${targetUserEmail} by userId ${impersonatorUserWorkspace.user.id}`,
});
throw new AuthException(
@@ -739,7 +751,7 @@ export class AuthResolver {
}
await auditService.insertWorkspaceEvent(MONITORING_EVENT, {
eventName: `${isServerLevelImpersonation ? 'server' : 'workspace'}.impersonation.token_exchange_success`,
eventName: 'workspace.impersonation.token_exchange_success',
message: `Impersonation token exchanged for ${targetUserEmail} by userId ${impersonatorUserWorkspace.user.id}`,
});
@@ -241,11 +241,17 @@ export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') {
impersonatorUserWorkspace.user.canImpersonate === true &&
workspace.allowImpersonation === true;
if (isServerLevelImpersonation && !hasServerLevelImpersonatePermission) {
throw new AuthException(
'Server level impersonation not allowed',
AuthExceptionCode.FORBIDDEN_EXCEPTION,
);
if (isServerLevelImpersonation) {
if (!hasServerLevelImpersonatePermission)
throw new AuthException(
'Server level impersonation not allowed',
AuthExceptionCode.FORBIDDEN_EXCEPTION,
);
return {
impersonatorUserWorkspaceId: payload.impersonatorUserWorkspaceId,
impersonatedUserWorkspaceId: payload.impersonatedUserWorkspaceId,
};
}
const hasWorkspaceLevelImpersonatePermission =
@@ -255,10 +261,7 @@ export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') {
workspaceId: impersonatedUserWorkspace.workspace.id,
});
if (
!hasWorkspaceLevelImpersonatePermission &&
!hasServerLevelImpersonatePermission
) {
if (!hasWorkspaceLevelImpersonatePermission) {
throw new AuthException(
'Impersonation not allowed',
AuthExceptionCode.FORBIDDEN_EXCEPTION,
@@ -66,10 +66,17 @@ export class ImpersonationService {
impersonatorUserWorkspace.user.canImpersonate === true &&
impersonatorUserWorkspace.workspace.allowImpersonation === true;
if (isServerLevelImpersonation && !hasServerLevelImpersonatePermission) {
throw new AuthException(
'Impersonation not enabled for the impersonator user or the target workspace',
AuthExceptionCode.FORBIDDEN_EXCEPTION,
if (isServerLevelImpersonation) {
if (!hasServerLevelImpersonatePermission)
throw new AuthException(
'Impersonation not enabled for the impersonator user or the target workspace',
AuthExceptionCode.FORBIDDEN_EXCEPTION,
);
return this.generateImpersonationLoginToken(
impersonatorUserWorkspace,
toImpersonateUserWorkspace,
'server',
);
}
@@ -80,29 +87,38 @@ export class ImpersonationService {
workspaceId: workspaceId,
});
if (
!hasWorkspaceLevelImpersonatePermission &&
!hasServerLevelImpersonatePermission
) {
if (!hasWorkspaceLevelImpersonatePermission) {
throw new AuthException(
'Impersonation not enabled for this workspace',
AuthExceptionCode.FORBIDDEN_EXCEPTION,
);
}
return this.generateImpersonationLoginToken(
impersonatorUserWorkspace,
toImpersonateUserWorkspace,
'workspace',
);
}
async generateImpersonationLoginToken(
impersonatorUserWorkspace: UserWorkspace,
toImpersonateUserWorkspace: UserWorkspace,
impersonationLevel: 'server' | 'workspace',
) {
const auditService = this.auditService.createContext({
workspaceId: impersonatorUserWorkspace.workspace.id,
userId: impersonatorUserWorkspace.user.id,
});
await auditService.insertWorkspaceEvent(MONITORING_EVENT, {
eventName: `${isServerLevelImpersonation ? 'server' : 'workspace'}.impersonation.attempt`,
message: `Impersonation attempt: targetUserId=${toImpersonateUserWorkspace.user.id}, workspaceId=${workspaceId}, impersonatorUserId=${impersonatorUserWorkspace.user.id}`,
eventName: `${impersonationLevel}.impersonation.attempt`,
message: `Impersonation attempt: targetUserId=${toImpersonateUserWorkspace.user.id}, workspaceId=${toImpersonateUserWorkspace.workspace.id}, impersonatorUserId=${impersonatorUserWorkspace.user.id}`,
});
try {
await auditService.insertWorkspaceEvent(MONITORING_EVENT, {
eventName: `${isServerLevelImpersonation ? 'server' : 'workspace'}.impersonation.login_token_attempt`,
eventName: `${impersonationLevel}.impersonation.login_token_attempt`,
message: `Impersonation token generation attempt for user ${toImpersonateUserWorkspace.user.id}`,
});
@@ -116,7 +132,7 @@ export class ImpersonationService {
);
await auditService.insertWorkspaceEvent(MONITORING_EVENT, {
eventName: `${isServerLevelImpersonation ? 'server' : 'workspace'}.impersonation.login_token_generated`,
eventName: `${impersonationLevel}.impersonation.login_token_generated`,
message: `Impersonation token generated successfully for user ${toImpersonateUserWorkspace.user.id}`,
});
@@ -131,7 +147,7 @@ export class ImpersonationService {
};
} catch {
await auditService.insertWorkspaceEvent(MONITORING_EVENT, {
eventName: `${isServerLevelImpersonation ? 'server' : 'workspace'}.impersonation.login_token_failed`,
eventName: `${impersonationLevel}.impersonation.login_token_failed`,
message: `Impersonation token generation failed for targetUserId=${toImpersonateUserWorkspace.user.id}`,
});
throw new AuthException(