Files
zot/pkg/api/bearer_test.go
T
Evan d465690630 [feat]: add support for EC/ED25519 public keys for token authentication (#2998)
* feat: rework token auth to allow ED25519/EC public keys

Signed-off-by: evanebb <git@evanus.nl>

* fix: shadow err variable to hopefully avoid data race

Signed-off-by: evanebb <git@evanus.nl>

* fix: apply golangci-lint feedback

Signed-off-by: evanebb <git@evanus.nl>

* fix: simplify public key loading by only supporting certificates, fixes ED25519 certificate handling

Signed-off-by: evanebb <git@evanus.nl>

* test: add golang-jwt based test auth server and test RSA/EC/ED25519 keys

Signed-off-by: evanebb <git@evanus.nl>

* fix: restrict allowed signing algorithms as recommended by library

Signed-off-by: evanebb <git@evanus.nl>

* test: add more bearer authorizer tests

Signed-off-by: evanebb <git@evanus.nl>

* fix: apply more golangci-lint feedback

Signed-off-by: evanebb <git@evanus.nl>

* test: ensure chmod calls run on test failure for authn errors test

Signed-off-by: evanebb <git@evanus.nl>

* fix: verify issued-at in given token if present
Pulls the validation in-line with the old library

Signed-off-by: evanebb <git@evanus.nl>

---------

Signed-off-by: evanebb <git@evanus.nl>
2025-03-06 14:32:13 -08:00

122 lines
2.9 KiB
Go

package api_test
import (
"crypto/rand"
"crypto/rsa"
"testing"
"time"
"github.com/golang-jwt/jwt/v5"
. "github.com/smartystreets/goconvey/convey"
zerr "zotregistry.dev/zot/errors"
"zotregistry.dev/zot/pkg/api"
)
func TestBearerAuthorizer(t *testing.T) {
Convey("Test bearer token authorization", t, func() {
signingMethod := jwt.SigningMethodRS256
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
pubKey := privKey.Public()
authorizer := api.NewBearerAuthorizer("realm", "service", pubKey)
Convey("Empty authorization header given", func() {
err := authorizer.Authorize("", nil)
So(err, ShouldBeError, zerr.ErrNoBearerToken)
})
Convey("Valid token", func() {
access := []api.ResourceAccess{
{
Name: "authorized-repository",
Type: "repository",
Actions: []string{"pull"},
},
}
now := time.Now()
claims := api.ClaimsWithAccess{
Access: access,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(now.Add(time.Minute * 1)),
IssuedAt: jwt.NewNumericDate(now),
Issuer: "Zot",
Audience: []string{"Zot Registry"},
},
}
token, err := jwt.NewWithClaims(signingMethod, claims).SignedString(privKey)
if err != nil {
panic(err)
}
authHeader := "Bearer " + token
Convey("Unauthorized type", func() {
requested := &api.ResourceAction{
Type: "registry",
Name: "catalog",
Action: "*",
}
err := authorizer.Authorize(authHeader, requested)
So(err, ShouldHaveSameTypeAs, &api.AuthChallengeError{})
So(err, ShouldBeError, zerr.ErrInsufficientScope)
})
Convey("Unauthorized name", func() {
requested := &api.ResourceAction{
Type: "repository",
Name: "unauthorized-repository",
Action: "pull",
}
err := authorizer.Authorize(authHeader, requested)
So(err, ShouldHaveSameTypeAs, &api.AuthChallengeError{})
So(err, ShouldBeError, zerr.ErrInsufficientScope)
})
Convey("Unauthorized action", func() {
requested := &api.ResourceAction{
Type: "repository",
Name: "authorized-repository",
Action: "push",
}
err := authorizer.Authorize(authHeader, requested)
So(err, ShouldHaveSameTypeAs, &api.AuthChallengeError{})
So(err, ShouldBeError, zerr.ErrInsufficientScope)
})
Convey("Successful authorization with requested access", func() {
requested := &api.ResourceAction{
Type: "repository",
Name: "authorized-repository",
Action: "pull",
}
err := authorizer.Authorize(authHeader, requested)
So(err, ShouldBeNil)
})
Convey("Successful authorization without requested access", func() {
err := authorizer.Authorize(authHeader, nil)
So(err, ShouldBeNil)
})
})
Convey("Invalid token", func() {
authHeader := "invalid"
err := authorizer.Authorize(authHeader, nil)
So(err, ShouldWrap, zerr.ErrInvalidBearerToken)
})
})
}