@@ -59,9 +59,9 @@ type APIToken struct {
5959}
6060
6161type APITokenRepo interface {
62- Create (ctx context.Context , name string , description * string , expiresAt * time.Time , organizationID uuid.UUID , projectID * uuid.UUID , policies []* authz.Policy ) (* APIToken , error )
62+ Create (ctx context.Context , name string , description * string , expiresAt * time.Time , organizationID * uuid.UUID , projectID * uuid.UUID , policies []* authz.Policy ) (* APIToken , error )
6363 List (ctx context.Context , orgID * uuid.UUID , filters * APITokenListFilters ) ([]* APIToken , error )
64- Revoke (ctx context.Context , orgID , ID uuid.UUID ) error
64+ Revoke (ctx context.Context , orgID * uuid. UUID , ID uuid.UUID ) error
6565 UpdateExpiration (ctx context.Context , ID uuid.UUID , expiresAt time.Time ) error
6666 UpdateLastUsedAt (ctx context.Context , ID uuid.UUID , lastUsedAt time.Time ) error
6767 FindByID (ctx context.Context , ID uuid.UUID ) (* APIToken , error )
@@ -131,6 +131,7 @@ func NewAPITokenUseCase(apiTokenRepo APITokenRepo, jwtConfig *APITokenJWTConfig,
131131type apiTokenOptions struct {
132132 project * Project
133133 showOnlySystemTokens bool
134+ policies []* authz.Policy
134135}
135136
136137type APITokenCreateOpt func (* apiTokenOptions )
@@ -141,16 +142,34 @@ func APITokenWithProject(project *Project) APITokenCreateOpt {
141142 }
142143}
143144
145+ func APITokenWithPolicies (policies []* authz.Policy ) APITokenCreateOpt {
146+ return func (o * apiTokenOptions ) {
147+ o .policies = policies
148+ }
149+ }
150+
144151// expires in is a string that can be parsed by time.ParseDuration
145- func (uc * APITokenUseCase ) Create (ctx context.Context , name string , description * string , expiresIn * time.Duration , orgID string , opts ... APITokenCreateOpt ) (* APIToken , error ) {
152+ func (uc * APITokenUseCase ) Create (ctx context.Context , name string , description * string , expiresIn * time.Duration , orgID * string , opts ... APITokenCreateOpt ) (* APIToken , error ) {
146153 options := & apiTokenOptions {}
147154 for _ , opt := range opts {
148155 opt (options )
149156 }
150157
151- orgUUID , err := uuid .Parse (orgID )
152- if err != nil {
153- return nil , NewErrInvalidUUID (err )
158+ // Parse organization ID if provided
159+ var orgUUID * uuid.UUID
160+ var org * Organization
161+ if orgID != nil && * orgID != "" {
162+ parsed , err := uuid .Parse (* orgID )
163+ if err != nil {
164+ return nil , NewErrInvalidUUID (err )
165+ }
166+ orgUUID = & parsed
167+
168+ // Retrieve the organization
169+ org , err = uc .orgUseCase .FindByID (ctx , * orgID )
170+ if err != nil {
171+ return nil , fmt .Errorf ("finding organization: %w" , err )
172+ }
154173 }
155174
156175 if name == "" {
@@ -170,22 +189,21 @@ func (uc *APITokenUseCase) Create(ctx context.Context, name string, description
170189 * expiresAt = time .Now ().Add (* expiresIn )
171190 }
172191
173- // Retrieve the organization
174- org , err := uc .orgUseCase .FindByID (ctx , orgID )
175- if err != nil {
176- return nil , fmt .Errorf ("finding organization: %w" , err )
177- }
178-
179192 // If a project is provided, we store it in the token
180193 var projectID * uuid.UUID
181194 if options .project != nil {
182195 projectID = ToPtr (options .project .ID )
183196 }
184197
198+ // Use provided policies if present, otherwise use defaults
199+ policies := options .policies
200+ if policies == nil {
201+ policies = uc .DefaultAuthzPolicies
202+ }
203+
185204 // NOTE: the expiration time is stored just for reference, it's also encoded in the JWT
186205 // We store it since Chainloop will not have access to the JWT to check the expiration once created
187- // Pass default policies to be stored with the token
188- token , err := uc .apiTokenRepo .Create (ctx , name , description , expiresAt , orgUUID , projectID , uc .DefaultAuthzPolicies )
206+ token , err := uc .apiTokenRepo .Create (ctx , name , description , expiresAt , orgUUID , projectID , policies )
189207 if err != nil {
190208 if IsErrAlreadyExists (err ) {
191209 return nil , NewErrAlreadyExistsStr ("name already taken" )
@@ -194,21 +212,26 @@ func (uc *APITokenUseCase) Create(ctx context.Context, name string, description
194212 }
195213
196214 generationOpts := & apitoken.GenerateJWTOptions {
197- OrgID : token .OrganizationID ,
198- OrgName : org .Name ,
199215 KeyID : token .ID ,
200216 KeyName : name ,
201217 ExpiresAt : expiresAt ,
202218 }
203219
220+ // Set org info if available or instance-level token scope
221+ if org != nil {
222+ generationOpts .OrgID = & token .OrganizationID
223+ generationOpts .OrgName = & org .Name
224+ } else {
225+ generationOpts .Scope = ToPtr (authz .ScopeInstanceAdmin )
226+ }
227+
204228 if projectID != nil {
205229 generationOpts .ProjectID = ToPtr (options .project .ID )
206230 generationOpts .ProjectName = ToPtr (options .project .Name )
207231 }
208232
209233 // generate the JWT
210234 token .JWT , err = uc .jwtBuilder .GenerateJWT (generationOpts )
211-
212235 if err != nil {
213236 return nil , fmt .Errorf ("generating jwt: %w" , err )
214237 }
@@ -221,7 +244,7 @@ func (uc *APITokenUseCase) Create(ctx context.Context, name string, description
221244 },
222245 APITokenDescription : description ,
223246 ExpiresAt : expiresAt ,
224- }, & orgUUID )
247+ }, orgUUID )
225248
226249 return token , nil
227250}
@@ -239,19 +262,26 @@ func (uc *APITokenUseCase) RegenerateJWT(ctx context.Context, tokenID uuid.UUID,
239262 return nil , fmt .Errorf ("finding token: %w" , err )
240263 }
241264
242- org , err := uc .orgUseCase .FindByID (ctx , token .OrganizationID .String ())
243- if err != nil {
244- return nil , fmt .Errorf ("finding organization: %w" , err )
245- }
246-
247265 generationOpts := & apitoken.GenerateJWTOptions {
248- OrgID : token .OrganizationID ,
249- OrgName : org .Name ,
250266 KeyID : token .ID ,
251267 KeyName : token .Name ,
252268 ExpiresAt : & expiresAt ,
253269 }
254270
271+ // Check if this is an org-scoped or instance-level token
272+ if token .OrganizationID != uuid .Nil {
273+ // Org-scoped token
274+ org , err := uc .orgUseCase .FindByID (ctx , token .OrganizationID .String ())
275+ if err != nil {
276+ return nil , fmt .Errorf ("finding organization: %w" , err )
277+ }
278+ generationOpts .OrgID = & token .OrganizationID
279+ generationOpts .OrgName = & org .Name
280+ } else {
281+ // Instance-level token
282+ generationOpts .Scope = ToPtr (authz .ScopeInstanceAdmin )
283+ }
284+
255285 // generate the JWT
256286 token .JWT , err = uc .jwtBuilder .GenerateJWT (generationOpts )
257287 if err != nil {
@@ -289,13 +319,15 @@ func WithAPITokenScope(scope APITokenScope) APITokenListOpt {
289319type APITokenScope string
290320
291321const (
292- APITokenScopeProject APITokenScope = "project"
293- APITokenScopeGlobal APITokenScope = "global"
322+ APITokenScopeProject APITokenScope = "project"
323+ APITokenScopeGlobal APITokenScope = "global"
324+ APITokenScopeInstance APITokenScope = "instance"
294325)
295326
296327var availableAPITokenScopes = []APITokenScope {
297328 APITokenScopeProject ,
298329 APITokenScopeGlobal ,
330+ APITokenScopeInstance ,
299331}
300332
301333type APITokenListFilters struct {
@@ -318,18 +350,26 @@ func (uc *APITokenUseCase) List(ctx context.Context, orgID string, opts ...APITo
318350 return nil , NewErrValidationStr (fmt .Sprintf ("invalid scope %q, please chose one of: %v" , filters .FilterByScope , availableAPITokenScopes ))
319351 }
320352
321- orgUUID , err := uuid .Parse (orgID )
322- if err != nil {
323- return nil , NewErrInvalidUUID (err )
353+ var orgUUID * uuid.UUID
354+ if orgID != "" {
355+ parsed , err := uuid .Parse (orgID )
356+ if err != nil {
357+ return nil , NewErrInvalidUUID (err )
358+ }
359+ orgUUID = & parsed
324360 }
325361
326- return uc .apiTokenRepo .List (ctx , & orgUUID , filters )
362+ return uc .apiTokenRepo .List (ctx , orgUUID , filters )
327363}
328364
329365func (uc * APITokenUseCase ) Revoke (ctx context.Context , orgID , id string ) error {
330- orgUUID , err := uuid .Parse (orgID )
331- if err != nil {
332- return NewErrInvalidUUID (err )
366+ var orgUUID * uuid.UUID
367+ if orgID != "" {
368+ parsed , err := uuid .Parse (orgID )
369+ if err != nil {
370+ return NewErrInvalidUUID (err )
371+ }
372+ orgUUID = & parsed
333373 }
334374
335375 tokenUUID , err := uuid .Parse (id )
@@ -352,7 +392,7 @@ func (uc *APITokenUseCase) Revoke(ctx context.Context, orgID, id string) error {
352392 APITokenID : & tokenUUID ,
353393 APITokenName : token .Name ,
354394 },
355- }, & orgUUID )
395+ }, orgUUID )
356396
357397 return nil
358398}
0 commit comments