@@ -283,3 +283,103 @@ 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+ jwtManager := auth .NewJWTManager (cfg )
301+ // Add a blocked namespace for testing
302+ jwtManager .BlockedNamespaces = []string {"io.github.spammer" }
303+
304+ claims := auth.JWTClaims {
305+ AuthMethod : model .AuthMethodGitHubAT ,
306+ AuthMethodSubject : "spammer" ,
307+ Permissions : []auth.Permission {
308+ {
309+ Action : auth .PermissionActionPublish ,
310+ ResourcePattern : "io.github.spammer/*" ,
311+ },
312+ },
313+ }
314+
315+ tokenResponse , err := jwtManager .GenerateTokenResponse (ctx , claims )
316+ assert .Error (t , err )
317+ assert .Contains (t , err .Error (), "your namespace is blocked" )
318+ assert .Nil (t , tokenResponse )
319+ })
320+
321+ t .Run ("non-blocked namespace should allow token" , func (t * testing.T ) {
322+ jwtManager := auth .NewJWTManager (cfg )
323+ jwtManager .BlockedNamespaces = []string {"io.github.spammer" }
324+
325+ claims := auth.JWTClaims {
326+ AuthMethod : model .AuthMethodGitHubAT ,
327+ AuthMethodSubject : "gooduser" ,
328+ Permissions : []auth.Permission {
329+ {
330+ Action : auth .PermissionActionPublish ,
331+ ResourcePattern : "io.github.gooduser/*" ,
332+ },
333+ },
334+ }
335+
336+ tokenResponse , err := jwtManager .GenerateTokenResponse (ctx , claims )
337+ require .NoError (t , err )
338+ assert .NotEmpty (t , tokenResponse .RegistryToken )
339+ })
340+
341+ t .Run ("multiple permissions with one blocked should deny token" , func (t * testing.T ) {
342+ jwtManager := auth .NewJWTManager (cfg )
343+ jwtManager .BlockedNamespaces = []string {"io.github.badorg" }
344+
345+ claims := auth.JWTClaims {
346+ AuthMethod : model .AuthMethodGitHubAT ,
347+ AuthMethodSubject : "user" ,
348+ Permissions : []auth.Permission {
349+ {
350+ Action : auth .PermissionActionPublish ,
351+ ResourcePattern : "io.github.user/*" , // allowed
352+ },
353+ {
354+ Action : auth .PermissionActionPublish ,
355+ ResourcePattern : "io.github.badorg/*" , // blocked
356+ },
357+ },
358+ }
359+
360+ tokenResponse , err := jwtManager .GenerateTokenResponse (ctx , claims )
361+ assert .Error (t , err )
362+ assert .Contains (t , err .Error (), "your namespace is blocked" )
363+ assert .Nil (t , tokenResponse )
364+ })
365+
366+ t .Run ("global admin permissions should bypass denylist" , func (t * testing.T ) {
367+ jwtManager := auth .NewJWTManager (cfg )
368+ jwtManager .BlockedNamespaces = []string {"io.github.spammer" }
369+
370+ claims := auth.JWTClaims {
371+ AuthMethod : model .AuthMethodNone ,
372+ AuthMethodSubject : "admin" ,
373+ Permissions : []auth.Permission {
374+ {
375+ Action : auth .PermissionActionPublish ,
376+ ResourcePattern : "*" , // global permission should bypass blocking
377+ },
378+ },
379+ }
380+
381+ tokenResponse , err := jwtManager .GenerateTokenResponse (ctx , claims )
382+ require .NoError (t , err )
383+ assert .NotEmpty (t , tokenResponse .RegistryToken )
384+ })
385+ }
0 commit comments