@@ -11,21 +11,34 @@ import {
1111} from '@sim/testing'
1212import { afterAll , beforeEach , describe , expect , it , vi } from 'vitest'
1313
14- const { mockIsOrganizationBillingBlocked } = vi . hoisted ( ( ) => ( {
15- mockIsOrganizationBillingBlocked : vi . fn ( ) ,
16- } ) )
14+ const { mockIsOrganizationBillingBlocked, mockCheckOrganizationPersonalKeyRefusal } = vi . hoisted (
15+ ( ) => ( {
16+ mockIsOrganizationBillingBlocked : vi . fn ( ) ,
17+ mockCheckOrganizationPersonalKeyRefusal : vi . fn ( ) ,
18+ } )
19+ )
1720
1821vi . mock ( '@/lib/billing/core/access' , ( ) => ( {
1922 isOrganizationBillingBlocked : mockIsOrganizationBillingBlocked ,
2023} ) )
2124
22- import { validateEnterpriseAuditAccess } from '@/app/api/v1/audit-logs/auth'
25+ vi . mock ( '@/app/api/v1/middleware' , ( ) => ( {
26+ capabilityGovernedUserId : ( rateLimit : { keyType ?: string ; userId ?: string } ) =>
27+ rateLimit . keyType === 'personal' ? ( rateLimit . userId ?? null ) : null ,
28+ checkOrganizationPersonalKeyRefusal : mockCheckOrganizationPersonalKeyRefusal ,
29+ } ) )
30+
31+ import {
32+ validateEnterpriseAuditAccess ,
33+ validateV1EnterpriseAuditAccess ,
34+ } from '@/app/api/v1/audit-logs/auth'
2335
2436describe ( 'enterprise audit access' , ( ) => {
2537 beforeEach ( ( ) => {
2638 vi . clearAllMocks ( )
2739 resetDbChainMock ( )
2840 mockIsOrganizationBillingBlocked . mockResolvedValue ( false )
41+ mockCheckOrganizationPersonalKeyRefusal . mockResolvedValue ( null )
2942 } )
3043
3144 afterAll ( ( ) => {
@@ -112,4 +125,72 @@ describe('enterprise audit access', () => {
112125 } )
113126 } )
114127 } )
128+
129+ describe ( 'v1 API-key access' , ( ) => {
130+ const personalKey = {
131+ allowed : true ,
132+ remaining : 1 ,
133+ limit : 1 ,
134+ resetAt : new Date ( ) ,
135+ userId : 'viewer' ,
136+ keyType : 'personal' as const ,
137+ }
138+
139+ beforeEach ( ( ) => {
140+ setEnvFlags ( { isBillingEnabled : false , isAuditLogsEnabled : true } )
141+ } )
142+
143+ it ( 'refuses a workspace key before resolving its creator as the subject' , async ( ) => {
144+ const result = await validateV1EnterpriseAuditAccess ( {
145+ ...personalKey ,
146+ keyType : 'workspace' ,
147+ workspaceId : 'workspace-a' ,
148+ } )
149+
150+ if ( result . success ) throw new Error ( 'Expected the workspace key to be refused' )
151+ expect ( result . response . status ) . toBe ( 403 )
152+ await expect ( result . response . json ( ) ) . resolves . toEqual ( {
153+ error : 'Audit logs require a personal API key' ,
154+ } )
155+ expect ( dbChainMockFns . where ) . not . toHaveBeenCalled ( )
156+ expect ( mockCheckOrganizationPersonalKeyRefusal ) . not . toHaveBeenCalled ( )
157+ } )
158+
159+ it ( 'authorizes a personal key held by an organization admin' , async ( ) => {
160+ queueTableRows ( schemaMock . member , [ { organizationId : 'org-1' , role : 'admin' } ] )
161+ queueTableRows ( schemaMock . member , [ { userId : 'viewer' } ] )
162+
163+ await expect ( validateV1EnterpriseAuditAccess ( personalKey ) ) . resolves . toEqual ( {
164+ success : true ,
165+ userId : 'viewer' ,
166+ context : { organizationId : 'org-1' , orgMemberIds : [ 'viewer' ] } ,
167+ } )
168+ expect ( mockCheckOrganizationPersonalKeyRefusal ) . toHaveBeenCalledWith ( personalKey )
169+ } )
170+
171+ it ( 'refuses a personal key its permission group withholds' , async ( ) => {
172+ queueTableRows ( schemaMock . member , [ { organizationId : 'org-1' , role : 'admin' } ] )
173+ queueTableRows ( schemaMock . member , [ { userId : 'viewer' } ] )
174+ const refusal = new Response ( null , { status : 403 } )
175+ mockCheckOrganizationPersonalKeyRefusal . mockResolvedValue ( refusal )
176+
177+ const result = await validateV1EnterpriseAuditAccess ( personalKey )
178+
179+ if ( result . success ) throw new Error ( 'Expected the withheld personal key to be refused' )
180+ expect ( result . response ) . toBe ( refusal )
181+ } )
182+
183+ it ( 'answers a non-admin with the role refusal, not the group configuration' , async ( ) => {
184+ queueTableRows ( schemaMock . member , [ { organizationId : 'org-1' , role : 'member' } ] )
185+ mockCheckOrganizationPersonalKeyRefusal . mockResolvedValue ( new Response ( null , { status : 403 } ) )
186+
187+ const result = await validateV1EnterpriseAuditAccess ( personalKey )
188+
189+ if ( result . success ) throw new Error ( 'Expected the non-admin to be refused' )
190+ await expect ( result . response . json ( ) ) . resolves . toEqual ( {
191+ error : 'Organization admin or owner role required' ,
192+ } )
193+ expect ( mockCheckOrganizationPersonalKeyRefusal ) . not . toHaveBeenCalled ( )
194+ } )
195+ } )
115196} )
0 commit comments