Skip to content

Commit

Permalink
Support the audience as list and add more test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
kovayur committed May 15, 2024
1 parent 44ede5a commit 46ccc7b
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 10 deletions.
1 change: 0 additions & 1 deletion internal/dinosaur/pkg/environments/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ func (b IntegrationEnvLoader) Defaults() map[string]string {
"quota-type": "quota-management-list",
"enable-deletion-of-expired-central": "true",
"dataplane-cluster-scaling-type": "auto", // need to set this to 'auto' for integration environment as some tests rely on this
"fleetshard-authz-config-file": "config/fleetshard-authz-development.yaml",
}
}

Expand Down
11 changes: 8 additions & 3 deletions pkg/auth/acs_claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ func (c *ACSClaims) VerifyIssuer(cmp string, req bool) bool {
return jwt.MapClaims(*c).VerifyIssuer(cmp, req)
}

// VerifyAudience wraps jwt.VerifyAudience
func (c *ACSClaims) VerifyAudience(cmp string, req bool) bool {
return jwt.MapClaims(*c).VerifyAudience(cmp, req)
}

// GetUsername ...
func (c *ACSClaims) GetUsername() (string, error) {
if idx, val := arrays.FindFirst(func(x interface{}) bool { return x != nil },
Expand Down Expand Up @@ -80,9 +85,9 @@ func (c *ACSClaims) GetSubject() (string, error) {
}

// GetAudience returns the audience claim of the token. It identifies the token consumer.
func (c *ACSClaims) GetAudience() (string, error) {
if sub, ok := (*c)[audienceClaim].(string); ok {
return sub, nil
func (c *ACSClaims) GetAudience() (interface{}, error) {
if aud, ok := (*c)[audienceClaim]; ok {
return aud, nil
}

return "", fmt.Errorf("can't find %q attribute in claims", audienceClaim)
Expand Down
42 changes: 37 additions & 5 deletions pkg/auth/fleetshard_authz_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,47 @@ func fleetShardAuthorizationMiddleware(iamConfig *iam.IAMConfig, fleetShardAuthZ
}

func checkAllowedOrgIDs(allowedOrgIDs []string) mux.MiddlewareFunc {
return checkClaim(alternateTenantIDClaim, (*ACSClaims).GetOrgID, allowedOrgIDs)
return checkClaim(tenantIDClaim, (*ACSClaims).GetOrgID, allowedOrgIDs)
}

func checkSubject(subjects []string) mux.MiddlewareFunc {
return checkClaim(tenantSubClaim, (*ACSClaims).GetSubject, subjects)
func checkSubject(allowedSubjects []string) mux.MiddlewareFunc {
return checkClaim(tenantSubClaim, (*ACSClaims).GetSubject, allowedSubjects)
}

func checkAudience(audiences []string) mux.MiddlewareFunc {
return checkClaim(audienceClaim, (*ACSClaims).GetAudience, audiences)
func checkAudience(allowedAudiences []string) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
ctx := request.Context()
claims, err := GetClaimsFromContext(ctx)
if err != nil {
// Deliberately return 404 here so that it will appear as the endpoint doesn't exist if requests are
// not authorised. Otherwise, we would leak information about existing cluster IDs, since the path
// of the request is /agent-clusters/<id>.
shared.HandleError(request, writer, errors.NotFound(""))
return
}

audienceAccepted := false
for _, audience := range allowedAudiences {
if claims.VerifyAudience(audience, true) {
audienceAccepted = true
break
}
}

if !audienceAccepted {
audience, _ := claims.GetAudience()
glog.Infof("none of the audiences (%q) in the access token is not in the list of allowed values [%s]",
audience, strings.Join(allowedAudiences, ","))

shared.HandleError(request, writer, errors.NotFound(""))
return
}

next.ServeHTTP(writer, request)

})
}
}

type claimsGetter func(*ACSClaims) (string, error)
Expand Down
36 changes: 35 additions & 1 deletion pkg/auth/fleetshard_authz_middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func TestUseFleetShardAuthorizationMiddleware_NoTokenSet(t *testing.T) {

func TestUseFleetShardAuthorizationMiddleware_DataPlaneOIDCIssuers(t *testing.T) {
const validIssuer = "http://localhost"
validAudience := []string{"acs-fleet-manager-private-api"}

tests := map[string]struct {
token *jwt.Token
Expand All @@ -136,6 +137,7 @@ func TestUseFleetShardAuthorizationMiddleware_DataPlaneOIDCIssuers(t *testing.T)
Claims: jwt.MapClaims{
"iss": validIssuer,
"sub": "fleetshard-sync",
"aud": validAudience,
},
},
expectedStatusCode: http.StatusOK,
Expand All @@ -145,6 +147,7 @@ func TestUseFleetShardAuthorizationMiddleware_DataPlaneOIDCIssuers(t *testing.T)
Claims: jwt.MapClaims{
"iss": validIssuer,
"sub": "third-party-service",
"aud": "acs-fleet-manager-private-api",
},
},
expectedStatusCode: http.StatusNotFound,
Expand All @@ -168,10 +171,40 @@ func TestUseFleetShardAuthorizationMiddleware_DataPlaneOIDCIssuers(t *testing.T)
token: &jwt.Token{
Claims: jwt.MapClaims{
"iss": validIssuer,
"aud": validAudience,
},
},
expectedStatusCode: http.StatusNotFound,
},
"should fail when audience is not set": {
token: &jwt.Token{
Claims: jwt.MapClaims{
"iss": validIssuer,
"sub": "fleetshard-sync",
},
},
expectedStatusCode: http.StatusNotFound,
},
"should fail when audience is not allowed": {
token: &jwt.Token{
Claims: jwt.MapClaims{
"iss": validIssuer,
"sub": "fleetshard-sync",
"aud": []string{"https://kubernetes.default.svc"},
},
},
expectedStatusCode: http.StatusNotFound,
},
"should succeed when at least one audience is allowed": {
token: &jwt.Token{
Claims: jwt.MapClaims{
"iss": validIssuer,
"sub": "fleetshard-sync",
"aud": []string{"other-api", "acs-fleet-manager-private-api"},
},
},
expectedStatusCode: http.StatusOK,
},
}

for name, tt := range tests {
Expand All @@ -192,7 +225,8 @@ func TestUseFleetShardAuthorizationMiddleware_DataPlaneOIDCIssuers(t *testing.T)
DataPlaneOIDCIssuers: &iam.OIDCIssuers{URIs: []string{validIssuer}},
},
&FleetShardAuthZConfig{
AllowedSubjects: []string{"fleetshard-sync"},
AllowedSubjects: []string{"fleetshard-sync"},
AllowedAudiences: validAudience,
})

req := httptest.NewRequest("GET", "http://example.com/agent-clusters/1234", nil)
Expand Down

0 comments on commit 46ccc7b

Please sign in to comment.