@@ -283,3 +283,118 @@ func TestNewJWTManager_InvalidKeySize(t *testing.T) {
283283 auth .NewJWTManager (cfg )
284284 })
285285}
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