@@ -5,14 +5,26 @@ import type { WorkflowExecutionDelegatedPrincipal } from '@sim/auth/principal'
55import { beforeEach , describe , expect , it , vi } from 'vitest'
66
77const mocks = vi . hoisted ( ( ) => ( {
8+ findPolicyGrant : vi . fn ( ) ,
89 loadEnrollmentAccess : vi . fn ( ) ,
910} ) )
1011
1112vi . mock ( '@/lib/credential-groups/credentials' , ( ) => ( {
1213 loadCredentialGroupEnrollmentAccessForSubject : mocks . loadEnrollmentAccess ,
1314} ) )
1415
15- import { requireCredentialGroupEnrollmentAccess } from '@/lib/credential-groups/application/authorization'
16+ vi . mock ( '@/lib/resource-policies/authorization' , ( ) => ( {
17+ findResourcePolicyGrant : mocks . findPolicyGrant ,
18+ } ) )
19+
20+ import { requireCredentialGroupCredentialAccess } from '@/lib/credential-groups/application/authorization'
21+
22+ const context = {
23+ workspaceId : 'workspace-1' ,
24+ workspaceOrganizationId : null ,
25+ allowPersonalApiKeys : true ,
26+ credentialGroupId : 'group-1' ,
27+ }
1628
1729function executorPrincipal ( ) : WorkflowExecutionDelegatedPrincipal {
1830 return {
@@ -44,9 +56,10 @@ function executorPrincipal(): WorkflowExecutionDelegatedPrincipal {
4456 }
4557}
4658
47- describe ( 'requireCredentialGroupEnrollmentAccess ' , ( ) => {
59+ describe ( 'requireCredentialGroupCredentialAccess ' , ( ) => {
4860 beforeEach ( ( ) => {
4961 vi . clearAllMocks ( )
62+ mocks . findPolicyGrant . mockResolvedValue ( null )
5063 mocks . loadEnrollmentAccess . mockResolvedValue ( {
5164 enrollmentId : 'enrollment-1' ,
5265 email : 'person@example.com' ,
@@ -56,7 +69,14 @@ describe('requireCredentialGroupEnrollmentAccess', () => {
5669 it ( 'resolves an external workflow actor to their enrollment' , async ( ) => {
5770 const principal = executorPrincipal ( )
5871
59- await expect ( requireCredentialGroupEnrollmentAccess ( principal , 'group-1' ) ) . resolves . toEqual ( {
72+ await expect (
73+ requireCredentialGroupCredentialAccess (
74+ principal ,
75+ context ,
76+ 'credential_groups.credentials.use'
77+ )
78+ ) . resolves . toEqual ( {
79+ scope : 'enrollment' ,
6080 enrollmentId : 'enrollment-1' ,
6181 email : 'person@example.com' ,
6282 } )
@@ -78,10 +98,14 @@ describe('requireCredentialGroupEnrollmentAccess', () => {
7898 }
7999
80100 await expect (
81- requireCredentialGroupEnrollmentAccess ( principal , 'group-1' )
101+ requireCredentialGroupCredentialAccess (
102+ principal ,
103+ context ,
104+ 'credential_groups.credentials.use'
105+ )
82106 ) . rejects . toMatchObject ( {
83107 code : 'forbidden' ,
84- message : 'Credential Group enrollment access required' ,
108+ message : 'Credential Group actor access required' ,
85109 } )
86110 expect ( mocks . loadEnrollmentAccess ) . not . toHaveBeenCalled ( )
87111 } )
@@ -96,8 +120,32 @@ describe('requireCredentialGroupEnrollmentAccess', () => {
96120 }
97121
98122 await expect (
99- requireCredentialGroupEnrollmentAccess ( principal , 'group-1' )
123+ requireCredentialGroupCredentialAccess (
124+ principal ,
125+ context ,
126+ 'credential_groups.credentials.use'
127+ )
100128 ) . rejects . toMatchObject ( { code : 'forbidden' } )
101129 expect ( mocks . loadEnrollmentAccess ) . not . toHaveBeenCalled ( )
102130 } )
131+
132+ it ( 'allows an actorless workflow when an explicit policy grant matches' , async ( ) => {
133+ const principal = executorPrincipal ( )
134+ principal . delegationContext ! . principal = {
135+ kind : 'system' ,
136+ serviceId : 'schedule' ,
137+ workspaceId : 'workspace-1' ,
138+ workflowId : 'workflow-1' ,
139+ }
140+ mocks . findPolicyGrant . mockResolvedValue ( { id : 'grant-1' } )
141+
142+ await expect (
143+ requireCredentialGroupCredentialAccess (
144+ principal ,
145+ context ,
146+ 'credential_groups.credentials.use'
147+ )
148+ ) . resolves . toEqual ( { scope : 'all' , grantId : 'grant-1' } )
149+ expect ( mocks . loadEnrollmentAccess ) . not . toHaveBeenCalled ( )
150+ } )
103151} )
0 commit comments