diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts index 36dfb3c3a9..d25651a6ca 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts @@ -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}`, }); diff --git a/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.ts b/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.ts index 82bed3123a..e8fc233d9c 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.ts @@ -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, diff --git a/packages/twenty-server/src/engine/core-modules/impersonation/services/impersonation.service.ts b/packages/twenty-server/src/engine/core-modules/impersonation/services/impersonation.service.ts index ec54c0aa1f..ad50a024c8 100644 --- a/packages/twenty-server/src/engine/core-modules/impersonation/services/impersonation.service.ts +++ b/packages/twenty-server/src/engine/core-modules/impersonation/services/impersonation.service.ts @@ -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(