mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-04 18:00:08 +00:00
Add integration test for multiple audience in structured authn
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>
This commit is contained in:
parent
d76d7a1e7a
commit
0feb1d5173
@ -242,7 +242,7 @@ type Issuer struct {
|
|||||||
AudienceMatchPolicy AudienceMatchPolicyType `json:"audienceMatchPolicy,omitempty"`
|
AudienceMatchPolicy AudienceMatchPolicyType `json:"audienceMatchPolicy,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AudienceMatchPolicyType is a set of valid values for Issuer.AudienceMatchPolicy
|
// AudienceMatchPolicyType is a set of valid values for issuer.audienceMatchPolicy
|
||||||
type AudienceMatchPolicyType string
|
type AudienceMatchPolicyType string
|
||||||
|
|
||||||
// Valid types for AudienceMatchPolicyType
|
// Valid types for AudienceMatchPolicyType
|
||||||
|
@ -219,6 +219,7 @@ func validateClaimValidationRules(compiler authenticationcel.Compiler, celMapper
|
|||||||
|
|
||||||
compilationResult, err := compileClaimsCELExpression(compiler, &authenticationcel.ClaimValidationCondition{
|
compilationResult, err := compileClaimsCELExpression(compiler, &authenticationcel.ClaimValidationCondition{
|
||||||
Expression: rule.Expression,
|
Expression: rule.Expression,
|
||||||
|
Message: rule.Message,
|
||||||
}, fldPath.Child("expression"))
|
}, fldPath.Child("expression"))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -578,10 +578,8 @@ func (v *idTokenVerifier) verifyAudience(t *oidc.IDToken) error {
|
|||||||
if v.audiences.Len() == 0 {
|
if v.audiences.Len() == 0 {
|
||||||
return fmt.Errorf("oidc: invalid configuration, audiences cannot be empty")
|
return fmt.Errorf("oidc: invalid configuration, audiences cannot be empty")
|
||||||
}
|
}
|
||||||
for _, aud := range t.Audience {
|
if v.audiences.HasAny(t.Audience...) {
|
||||||
if v.audiences.Has(aud) {
|
return nil
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("oidc: expected audience in %q got %q", sets.List(v.audiences), t.Audience)
|
return fmt.Errorf("oidc: expected audience in %q got %q", sets.List(v.audiences), t.Audience)
|
||||||
|
@ -1554,6 +1554,39 @@ func TestToken(t *testing.T) {
|
|||||||
Name: "jane",
|
Name: "jane",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "multiple-audiences in authentication config, multiple matches",
|
||||||
|
options: Options{
|
||||||
|
JWTAuthenticator: apiserver.JWTAuthenticator{
|
||||||
|
Issuer: apiserver.Issuer{
|
||||||
|
URL: "https://auth.example.com",
|
||||||
|
Audiences: []string{"random-client", "my-client", "other-client"},
|
||||||
|
AudienceMatchPolicy: "MatchAny",
|
||||||
|
},
|
||||||
|
ClaimMappings: apiserver.ClaimMappings{
|
||||||
|
Username: apiserver.PrefixedClaimOrExpression{
|
||||||
|
Claim: "username",
|
||||||
|
Prefix: pointer.String(""),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
now: func() time.Time { return now },
|
||||||
|
},
|
||||||
|
signingKey: loadRSAPrivKey(t, "testdata/rsa_1.pem", jose.RS256),
|
||||||
|
pubKeys: []*jose.JSONWebKey{
|
||||||
|
loadRSAKey(t, "testdata/rsa_1.pem", jose.RS256),
|
||||||
|
},
|
||||||
|
claims: fmt.Sprintf(`{
|
||||||
|
"iss": "https://auth.example.com",
|
||||||
|
"aud": ["not-my-client", "my-client", "other-client"],
|
||||||
|
"azp": "not-my-client",
|
||||||
|
"username": "jane",
|
||||||
|
"exp": %d
|
||||||
|
}`, valid.Unix()),
|
||||||
|
want: &user.DefaultInfo{
|
||||||
|
Name: "jane",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "multiple-audiences in authentication config, no match",
|
name: "multiple-audiences in authentication config, no match",
|
||||||
options: Options{
|
options: Options{
|
||||||
@ -1585,6 +1618,82 @@ func TestToken(t *testing.T) {
|
|||||||
}`, valid.Unix()),
|
}`, valid.Unix()),
|
||||||
wantErr: `oidc: verify token: oidc: expected audience in ["my-client" "random-client"] got ["not-my-client"]`,
|
wantErr: `oidc: verify token: oidc: expected audience in ["my-client" "random-client"] got ["not-my-client"]`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "nuanced audience validation using claim validation rules",
|
||||||
|
options: Options{
|
||||||
|
JWTAuthenticator: apiserver.JWTAuthenticator{
|
||||||
|
Issuer: apiserver.Issuer{
|
||||||
|
URL: "https://auth.example.com",
|
||||||
|
Audiences: []string{"bar", "foo", "baz"},
|
||||||
|
AudienceMatchPolicy: "MatchAny",
|
||||||
|
},
|
||||||
|
ClaimMappings: apiserver.ClaimMappings{
|
||||||
|
Username: apiserver.PrefixedClaimOrExpression{
|
||||||
|
Claim: "username",
|
||||||
|
Prefix: pointer.String(""),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ClaimValidationRules: []apiserver.ClaimValidationRule{
|
||||||
|
{
|
||||||
|
Expression: `sets.equivalent(claims.aud, ["bar", "foo", "baz"])`,
|
||||||
|
Message: "audience must exactly contain [bar, foo, baz]",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
now: func() time.Time { return now },
|
||||||
|
},
|
||||||
|
signingKey: loadRSAPrivKey(t, "testdata/rsa_1.pem", jose.RS256),
|
||||||
|
pubKeys: []*jose.JSONWebKey{
|
||||||
|
loadRSAKey(t, "testdata/rsa_1.pem", jose.RS256),
|
||||||
|
},
|
||||||
|
claims: fmt.Sprintf(`{
|
||||||
|
"iss": "https://auth.example.com",
|
||||||
|
"aud": ["foo", "bar", "baz"],
|
||||||
|
"azp": "not-my-client",
|
||||||
|
"username": "jane",
|
||||||
|
"exp": %d
|
||||||
|
}`, valid.Unix()),
|
||||||
|
want: &user.DefaultInfo{
|
||||||
|
Name: "jane",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "audience validation using claim validation rules fails",
|
||||||
|
options: Options{
|
||||||
|
JWTAuthenticator: apiserver.JWTAuthenticator{
|
||||||
|
Issuer: apiserver.Issuer{
|
||||||
|
URL: "https://auth.example.com",
|
||||||
|
Audiences: []string{"bar", "foo", "baz"},
|
||||||
|
AudienceMatchPolicy: "MatchAny",
|
||||||
|
},
|
||||||
|
ClaimMappings: apiserver.ClaimMappings{
|
||||||
|
Username: apiserver.PrefixedClaimOrExpression{
|
||||||
|
Claim: "username",
|
||||||
|
Prefix: pointer.String(""),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ClaimValidationRules: []apiserver.ClaimValidationRule{
|
||||||
|
{
|
||||||
|
Expression: `sets.equivalent(claims.aud, ["bar", "foo", "baz"])`,
|
||||||
|
Message: "audience must exactly contain [bar, foo, baz]",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
now: func() time.Time { return now },
|
||||||
|
},
|
||||||
|
signingKey: loadRSAPrivKey(t, "testdata/rsa_1.pem", jose.RS256),
|
||||||
|
pubKeys: []*jose.JSONWebKey{
|
||||||
|
loadRSAKey(t, "testdata/rsa_1.pem", jose.RS256),
|
||||||
|
},
|
||||||
|
claims: fmt.Sprintf(`{
|
||||||
|
"iss": "https://auth.example.com",
|
||||||
|
"aud": ["foo", "baz"],
|
||||||
|
"azp": "not-my-client",
|
||||||
|
"username": "jane",
|
||||||
|
"exp": %d
|
||||||
|
}`, valid.Unix()),
|
||||||
|
wantErr: `oidc: error evaluating claim validation expression: validation expression 'sets.equivalent(claims.aud, ["bar", "foo", "baz"])' failed: audience must exactly contain [bar, foo, baz]`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "invalid-issuer",
|
name: "invalid-issuer",
|
||||||
options: Options{
|
options: Options{
|
||||||
|
@ -761,6 +761,58 @@ jwt:
|
|||||||
},
|
},
|
||||||
wantUser: nil,
|
wantUser: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "multiple audiences check with claim validation rule is ok",
|
||||||
|
authConfigFn: func(t *testing.T, issuerURL, caCert string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
apiVersion: apiserver.config.k8s.io/v1alpha1
|
||||||
|
kind: AuthenticationConfiguration
|
||||||
|
jwt:
|
||||||
|
- issuer:
|
||||||
|
url: %s
|
||||||
|
audiences:
|
||||||
|
- baz
|
||||||
|
- foo
|
||||||
|
audienceMatchPolicy: MatchAny
|
||||||
|
certificateAuthority: |
|
||||||
|
%s
|
||||||
|
claimMappings:
|
||||||
|
username:
|
||||||
|
expression: "'k8s-' + claims.sub"
|
||||||
|
uid:
|
||||||
|
expression: "claims.uid"
|
||||||
|
claimValidationRules:
|
||||||
|
- expression: 'sets.equivalent(claims.aud, ["bar", "foo", "baz"])'
|
||||||
|
message: 'aud claim must be exactly match list ["bar", "foo", "baz"]'
|
||||||
|
`, issuerURL, indentCertificateAuthority(caCert))
|
||||||
|
},
|
||||||
|
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
||||||
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
||||||
|
idTokenLifetime := time.Second * 1200
|
||||||
|
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
||||||
|
t,
|
||||||
|
signingPrivateKey,
|
||||||
|
map[string]interface{}{
|
||||||
|
"iss": oidcServer.URL(),
|
||||||
|
"sub": defaultOIDCClaimedUsername,
|
||||||
|
"aud": []string{"foo", "bar", "baz"},
|
||||||
|
"exp": time.Now().Add(idTokenLifetime).Unix(),
|
||||||
|
"uid": "1234",
|
||||||
|
},
|
||||||
|
defaultStubAccessToken,
|
||||||
|
defaultStubRefreshToken,
|
||||||
|
))
|
||||||
|
},
|
||||||
|
configureClient: configureClientFetchingOIDCCredentials,
|
||||||
|
assertErrFn: func(t *testing.T, errorToCheck error) {
|
||||||
|
assert.NoError(t, errorToCheck)
|
||||||
|
},
|
||||||
|
wantUser: &authenticationv1.UserInfo{
|
||||||
|
Username: "k8s-john_doe",
|
||||||
|
Groups: []string{"system:authenticated"},
|
||||||
|
UID: "1234",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
Loading…
Reference in New Issue
Block a user