@@ -283,3 +283,118 @@ func TestNewJWTManager_InvalidKeySize(t *testing.T) {
283
283
auth .NewJWTManager (cfg )
284
284
})
285
285
}
286
+
287
+ func TestJWTManager_BlockedNamespaces (t * testing.T ) {
288
+ // Generate a proper Ed25519 seed for testing
289
+ testSeed := make ([]byte , ed25519 .SeedSize )
290
+ _ , err := rand .Read (testSeed )
291
+ require .NoError (t , err )
292
+
293
+ cfg := & config.Config {
294
+ JWTPrivateKey : hex .EncodeToString (testSeed ),
295
+ }
296
+
297
+ ctx := context .Background ()
298
+
299
+ t .Run ("blocked namespace should deny token" , func (t * testing.T ) {
300
+ // Temporarily override blocked namespaces for testing
301
+ originalBlocked := auth .BlockedNamespaces
302
+ auth .BlockedNamespaces = []string {"io.github.spammer" }
303
+ defer func () { auth .BlockedNamespaces = originalBlocked }()
304
+
305
+ jwtManager := auth .NewJWTManager (cfg )
306
+
307
+ claims := auth.JWTClaims {
308
+ AuthMethod : model .AuthMethodGitHubAT ,
309
+ AuthMethodSubject : "spammer" ,
310
+ Permissions : []auth.Permission {
311
+ {
312
+ Action : auth .PermissionActionPublish ,
313
+ ResourcePattern : "io.github.spammer/*" ,
314
+ },
315
+ },
316
+ }
317
+
318
+ tokenResponse , err := jwtManager .GenerateTokenResponse (ctx , claims )
319
+ assert .Error (t , err )
320
+ assert .Contains (t , err .Error (), "your namespace is blocked" )
321
+ assert .Nil (t , tokenResponse )
322
+ })
323
+
324
+ t .Run ("non-blocked namespace should allow token" , func (t * testing.T ) {
325
+ // Temporarily override blocked namespaces for testing
326
+ originalBlocked := auth .BlockedNamespaces
327
+ auth .BlockedNamespaces = []string {"io.github.spammer" }
328
+ defer func () { auth .BlockedNamespaces = originalBlocked }()
329
+
330
+ jwtManager := auth .NewJWTManager (cfg )
331
+
332
+ claims := auth.JWTClaims {
333
+ AuthMethod : model .AuthMethodGitHubAT ,
334
+ AuthMethodSubject : "gooduser" ,
335
+ Permissions : []auth.Permission {
336
+ {
337
+ Action : auth .PermissionActionPublish ,
338
+ ResourcePattern : "io.github.gooduser/*" ,
339
+ },
340
+ },
341
+ }
342
+
343
+ tokenResponse , err := jwtManager .GenerateTokenResponse (ctx , claims )
344
+ require .NoError (t , err )
345
+ assert .NotEmpty (t , tokenResponse .RegistryToken )
346
+ })
347
+
348
+ t .Run ("multiple permissions with one blocked should deny token" , func (t * testing.T ) {
349
+ // Temporarily override blocked namespaces for testing
350
+ originalBlocked := auth .BlockedNamespaces
351
+ auth .BlockedNamespaces = []string {"io.github.badorg" }
352
+ defer func () { auth .BlockedNamespaces = originalBlocked }()
353
+
354
+ jwtManager := auth .NewJWTManager (cfg )
355
+
356
+ claims := auth.JWTClaims {
357
+ AuthMethod : model .AuthMethodGitHubAT ,
358
+ AuthMethodSubject : "user" ,
359
+ Permissions : []auth.Permission {
360
+ {
361
+ Action : auth .PermissionActionPublish ,
362
+ ResourcePattern : "io.github.user/*" , // allowed
363
+ },
364
+ {
365
+ Action : auth .PermissionActionPublish ,
366
+ ResourcePattern : "io.github.badorg/*" , // blocked
367
+ },
368
+ },
369
+ }
370
+
371
+ tokenResponse , err := jwtManager .GenerateTokenResponse (ctx , claims )
372
+ assert .Error (t , err )
373
+ assert .Contains (t , err .Error (), "your namespace is blocked" )
374
+ assert .Nil (t , tokenResponse )
375
+ })
376
+
377
+ t .Run ("global admin permissions should bypass denylist" , func (t * testing.T ) {
378
+ // Temporarily override blocked namespaces for testing
379
+ originalBlocked := auth .BlockedNamespaces
380
+ auth .BlockedNamespaces = []string {"io.github.spammer" }
381
+ defer func () { auth .BlockedNamespaces = originalBlocked }()
382
+
383
+ jwtManager := auth .NewJWTManager (cfg )
384
+
385
+ claims := auth.JWTClaims {
386
+ AuthMethod : model .AuthMethodNone ,
387
+ AuthMethodSubject : "admin" ,
388
+ Permissions : []auth.Permission {
389
+ {
390
+ Action : auth .PermissionActionPublish ,
391
+ ResourcePattern : "*" , // global permission should bypass blocking
392
+ },
393
+ },
394
+ }
395
+
396
+ tokenResponse , err := jwtManager .GenerateTokenResponse (ctx , claims )
397
+ require .NoError (t , err )
398
+ assert .NotEmpty (t , tokenResponse .RegistryToken )
399
+ })
400
+ }
0 commit comments