@@ -4,10 +4,12 @@ import rbacPlugin, { type RoleBaseAccessController } from "@trigger.dev/rbac";
44import { expect , vi } from "vitest" ;
55import { MAX_API_KEY_TASK_IDENTIFIERS } from "~/consts" ;
66import { createEnvironmentApiKey , revokeEnvironmentApiKey } from "~/models/api-key.server" ;
7+ import { removeTeamMember } from "~/models/removeTeamMember.server" ;
78import type { ApiKeyTelemetry } from "~/services/apiKeyTelemetry.server" ;
89import {
910 createRuntimeEnvironment ,
1011 createTestOrgProjectWithMember ,
12+ createTestUser ,
1113 uniqueId ,
1214} from "./fixtures/environmentVariablesFixtures" ;
1315
@@ -68,6 +70,70 @@ containerTest("standalone fallback creates one explicit full-access key", async
6870 ) . resolves . toBe ( 1 ) ;
6971} ) ;
7072
73+ containerTest ( "cannot create a development key after its member is removed" , async ( { prisma } ) => {
74+ const { organization, project, user, orgMember } = await createTestOrgProjectWithMember ( prisma ) ;
75+ const admin = await createTestUser ( prisma ) ;
76+ await prisma . orgMember . create ( {
77+ data : { userId : admin . id , organizationId : organization . id , role : "ADMIN" } ,
78+ } ) ;
79+ const environment = await createRuntimeEnvironment ( prisma , {
80+ projectId : project . id ,
81+ organizationId : organization . id ,
82+ orgMemberId : orgMember . id ,
83+ type : "DEVELOPMENT" ,
84+ } ) ;
85+ const fallback = rbacPlugin . create ( prisma , { forceFallback : true } ) ;
86+ let policyStarted ! : ( ) => void ;
87+ let resumePolicy ! : ( ) => void ;
88+ const started = new Promise < void > ( ( resolve ) => {
89+ policyStarted = resolve ;
90+ } ) ;
91+ const resume = new Promise < void > ( ( resolve ) => {
92+ resumePolicy = resolve ;
93+ } ) ;
94+ const telemetry = telemetryRecorder ( ) ;
95+ const creation = createEnvironmentApiKey (
96+ {
97+ environmentId : environment . id ,
98+ taskEnvironmentId : environment . id ,
99+ userId : user . id ,
100+ name : "Racing removal" ,
101+ presetId : "FULL_ACCESS" ,
102+ } ,
103+ {
104+ prismaClient : prisma ,
105+ telemetryRecorder : telemetry ,
106+ rbacController : {
107+ async prepareApiKeyPolicy ( input ) {
108+ policyStarted ( ) ;
109+ await resume ;
110+ return fallback . prepareApiKeyPolicy ( input ) ;
111+ } ,
112+ } ,
113+ }
114+ ) ;
115+ const rejected = expect ( creation ) . rejects . toThrow ( "Environment not found" ) ;
116+ await started ;
117+ try {
118+ await removeTeamMember (
119+ { userId : admin . id , slug : organization . slug , memberId : orgMember . id } ,
120+ prisma
121+ ) ;
122+ } finally {
123+ resumePolicy ( ) ;
124+ }
125+ await rejected ;
126+ expect ( telemetry . recordOperation ) . toHaveBeenCalledWith (
127+ "create" ,
128+ "rejected" ,
129+ "membership_removed"
130+ ) ;
131+ expect ( telemetry . recordOperation ) . not . toHaveBeenCalledWith ( "create" , "error" , "database_error" ) ;
132+ await expect (
133+ prisma . apiKey . findFirst ( { where : { runtimeEnvironmentId : environment . id , revokedAt : null } } )
134+ ) . resolves . toBeNull ( ) ;
135+ } ) ;
136+
71137containerTest ( "records successful API key revocation" , async ( { prisma } ) => {
72138 const { user, environment } = await setup ( prisma ) ;
73139 const apiKey = await prisma . apiKey . create ( {
0 commit comments