Skip to content

Commit

Permalink
Prepare for v0.21.0 release (#309)
Browse files Browse the repository at this point in the history
* Prepare for v0.21.0 release

* add test

* update test
  • Loading branch information
fairclothjm authored Jun 13, 2024
1 parent cb729dd commit 52af34e
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## Unreleased

## v0.21.0

NO CHANGES

## v0.20.3

BUG FIXES:
Expand Down
219 changes: 218 additions & 1 deletion path_login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func setupBackend(t *testing.T, cfg testConfig) (closeableBackend, logical.Stora
return cb, storage
}

func getTestJWT(t *testing.T, privKey string, cl sqjwt.Claims, privateCl interface{}) (string, *ecdsa.PrivateKey) {
func getTestJWT(t *testing.T, privKey string, cl interface{}, privateCl interface{}) (string, *ecdsa.PrivateKey) {
t.Helper()
var key *ecdsa.PrivateKey
block, _ := pem.Decode([]byte(privKey))
Expand Down Expand Up @@ -208,6 +208,223 @@ func getTestOIDC(t *testing.T) string {
return out.AccessToken
}

// TestLoginBoundAudiences tests that the login JWT's aud claim is ignored if
// it is a single string. This is a case that is fixed in later versions of
// the plugin.
// See https://github.com/hashicorp/vault-plugin-auth-jwt/pull/308
func TestLoginBoundAudiences(t *testing.T) {
testCases := []struct {
name string
roleData map[string]interface{}
jwtData map[string]interface{}
expectErr bool
}{
{
name: "jwt with string aud and no bound_audiences",
roleData: map[string]interface{}{
"role_type": "jwt",
"bound_subject": "subject",
"user_claim": "https://vault/user",
"policies": "test",
"period": "3s",
"ttl": "1s",
"num_uses": 12,
"max_ttl": "5s",
},
jwtData: map[string]interface{}{
"sub": "subject",
"iss": "https://team-vault.auth0.com/",
"aud": "https://vault.plugin.auth.jwt.test",
"nbf": sqjwt.NewNumericDate(time.Now().Add(-5 * time.Second)),
},
expectErr: true,
},
{
name: "jwt with array aud and no bound_audiences",
// bound_audiences is unset
roleData: map[string]interface{}{
"role_type": "jwt",
"bound_subject": "subject",
"user_claim": "https://vault/user",
"policies": "test",
"period": "3s",
"ttl": "1s",
"num_uses": 12,
"max_ttl": "5s",
},
jwtData: map[string]interface{}{
"sub": "subject",
"iss": "https://team-vault.auth0.com/",
"aud": []string{"https://vault.plugin.auth.jwt.test"},
"nbf": sqjwt.NewNumericDate(time.Now().Add(-5 * time.Second)),
},
expectErr: true,
},
{
name: "jwt with array aud and role with bound_audiences",
roleData: map[string]interface{}{
"role_type": "jwt",
"bound_audiences": []string{"https://vault.plugin.auth.jwt.test", "another_audience"},
"bound_subject": "subject",
"user_claim": "https://vault/user",
"policies": "test",
"period": "3s",
"ttl": "1s",
"num_uses": 12,
"max_ttl": "5s",
},
// aud is an array
jwtData: map[string]interface{}{
"sub": "subject",
"iss": "https://team-vault.auth0.com/",
"aud": []string{"https://vault.plugin.auth.jwt.test"},
"nbf": sqjwt.NewNumericDate(time.Now().Add(-5 * time.Second)),
},
expectErr: false,
},
{
name: "error jwt with string aud and role with no match of bound_audiences",
roleData: map[string]interface{}{
"role_type": "jwt",
"bound_audiences": []string{"https://vault.plugin.auth.jwt.test", "another_audience"},
"bound_subject": "subject",
"user_claim": "https://vault/user",
"policies": "test",
"period": "3s",
"ttl": "1s",
"num_uses": 12,
"max_ttl": "5s",
},
// aud is not contained in role's bound_audiences
jwtData: map[string]interface{}{
"sub": "subject",
"iss": "https://team-vault.auth0.com/",
"aud": "https://foo.com",
"nbf": sqjwt.NewNumericDate(time.Now().Add(-5 * time.Second)),
},
expectErr: true,
},
{
name: "jwt with string aud and role with bound_audiences",
roleData: map[string]interface{}{
"role_type": "jwt",
"bound_audiences": []string{"https://vault.plugin.auth.jwt.test", "another_audience"},
"bound_subject": "subject",
"user_claim": "https://vault/user",
"policies": "test",
"period": "3s",
"ttl": "1s",
"num_uses": 12,
"max_ttl": "5s",
},
jwtData: map[string]interface{}{
"sub": "subject",
"iss": "https://team-vault.auth0.com/",
"aud": "https://vault.plugin.auth.jwt.test",
"nbf": sqjwt.NewNumericDate(time.Now().Add(-5 * time.Second)),
},
expectErr: false,
},
{
name: "jwt with no aud and role with no bound_audiences",
roleData: map[string]interface{}{
"role_type": "jwt",
"bound_subject": "subject",
"user_claim": "https://vault/user",
"policies": "test",
"period": "3s",
"ttl": "1s",
"num_uses": 12,
"max_ttl": "5s",
},
// aud is not set
jwtData: map[string]interface{}{
"sub": "subject",
"iss": "https://team-vault.auth0.com/",
"nbf": sqjwt.NewNumericDate(time.Now().Add(-5 * time.Second)),
},
expectErr: false,
},
}

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
b, storage := getBackend(t)

configData := map[string]interface{}{
"bound_issuer": "https://team-vault.auth0.com/",
"jwt_validation_pubkeys": ecdsaPubKey,
}

req := &logical.Request{
Operation: logical.UpdateOperation,
Path: configPath,
Storage: storage,
Data: configData,
}

resp, err := b.HandleRequest(context.Background(), req)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%s resp:%#v\n", err, resp)
}

req = &logical.Request{
Operation: logical.CreateOperation,
Path: "role/plugin-test",
Storage: storage,
Data: tt.roleData,
}

resp, err = b.HandleRequest(context.Background(), req)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%s resp:%#v\n", err, resp)
}

privateCl := struct {
User string `json:"https://vault/user"`
Groups []string `json:"https://vault/groups"`
}{
"jeff",
[]string{"foo", "bar"},
}

jwtData, _ := getTestJWT(t, ecdsaPrivKey, tt.jwtData, privateCl)

loginData := map[string]interface{}{
"role": "plugin-test",
"jwt": jwtData,
}

req = &logical.Request{
Operation: logical.UpdateOperation,
Path: "login",
Storage: storage,
Data: loginData,
Connection: &logical.Connection{
RemoteAddr: "127.0.0.1",
},
}

resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("got nil response")
}
if tt.expectErr {
if !resp.IsError() {
t.Fatal("expected error")
}
}

if !tt.expectErr && resp.IsError() {
t.Fatalf("unexpected error: %q", resp.Error().Error())
}
})
}
}

func TestLogin_JWT(t *testing.T) {
testLogin_JWT(t, false)
testLogin_JWT(t, true)
Expand Down

0 comments on commit 52af34e

Please sign in to comment.