Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

identity: enforce key param and key existence on role creation #12208

Merged
merged 9 commits into from Sep 8, 2021
3 changes: 3 additions & 0 deletions changelog/12208.txt
@@ -0,0 +1,3 @@
```release-note:bug
identity: handle creation of role without a key parameter
fairclothjm marked this conversation as resolved.
Show resolved Hide resolved
```
29 changes: 16 additions & 13 deletions vault/identity_store_oidc.go
Expand Up @@ -989,20 +989,23 @@ func (i *IdentityStore) pathOIDCCreateUpdateRole(ctx context.Context, req *logic
role.TokenTTL = time.Duration(d.Get("ttl").(int)) * time.Second
}

// get the key referenced by this role
var key namedKey
entry, err := req.Storage.Get(ctx, namedKeyConfigPath+role.Key)
if err != nil {
return nil, err
}
if entry != nil {
if err := entry.DecodeJSON(&key); err != nil {
// get the key referenced by this role (if it exists)
if role.Key != "" {
var key namedKey
entry, err := req.Storage.Get(ctx, namedKeyConfigPath+role.Key)
if err != nil {
return nil, err
}
}

if role.TokenTTL > key.VerificationTTL {
return logical.ErrorResponse("a role's token ttl cannot be longer than the verification_ttl of the key it references"), nil
if entry != nil {
if err := entry.DecodeJSON(&key); err != nil {
return nil, err
}
} else {
return logical.ErrorResponse("cannot find key %q", role.Key), nil
}
fairclothjm marked this conversation as resolved.
Show resolved Hide resolved
if role.TokenTTL > key.VerificationTTL {
return logical.ErrorResponse("a role's token ttl cannot be longer than the verification_ttl of the key it references"), nil
}
}

if clientID, ok := d.GetOk("client_id"); ok {
Expand All @@ -1019,7 +1022,7 @@ func (i *IdentityStore) pathOIDCCreateUpdateRole(ctx context.Context, req *logic
}

// store role (which was either just created or updated)
entry, err = logical.StorageEntryJSON(roleConfigPath+name, role)
entry, err := logical.StorageEntryJSON(roleConfigPath+name, role)
if err != nil {
return nil, err
}
Expand Down
56 changes: 56 additions & 0 deletions vault/identity_store_oidc_test.go
Expand Up @@ -18,6 +18,61 @@ import (
"gopkg.in/square/go-jose.v2/jwt"
)

// TestOIDC_Path_OIDC_RoleNoKeyParameter
fairclothjm marked this conversation as resolved.
Show resolved Hide resolved
func TestOIDC_Path_OIDC_RoleNoKeyParameter(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
ctx := namespace.RootContext(nil)
storage := &logical.InmemStorage{}

// Create a test role "test-role1" without a key param -- should succeed
resp, err := c.identityStore.HandleRequest(ctx, &logical.Request{
Path: "oidc/role/test-role1",
Operation: logical.CreateOperation,
Storage: storage,
})
expectSuccess(t, resp, err)

// Read "test-role1" and validate
resp, err = c.identityStore.HandleRequest(ctx, &logical.Request{
Path: "oidc/role/test-role1",
Operation: logical.ReadOperation,
Storage: storage,
})
expectSuccess(t, resp, err)
expected := map[string]interface{}{
"ttl": int64(86400),
"template": "",
"key": "",
"client_id": resp.Data["client_id"],
}
if diff := deep.Equal(expected, resp.Data); diff != nil {
t.Fatal(diff)
}
}

// TestOIDC_Path_OIDC_RoleNilKeyEntry
func TestOIDC_Path_OIDC_RoleNilKeyEntry(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
ctx := namespace.RootContext(nil)
storage := &logical.InmemStorage{}

// Create a test role "test-role1" with a non-existent key -- should fail
resp, err := c.identityStore.HandleRequest(ctx, &logical.Request{
Path: "oidc/role/test-role1",
Operation: logical.CreateOperation,
Data: map[string]interface{}{
"key": "test-key",
},
Storage: storage,
})
expectError(t, resp, err)
// validate error message
expectedStrings := map[string]interface{}{
"cannot find key \"test-key\"": true,
}
expectStrings(t, []string{resp.Data["error"].(string)}, expectedStrings)
}

// TestOIDC_Path_OIDCRoleRole tests CRUD operations for roles
fairclothjm marked this conversation as resolved.
Show resolved Hide resolved
func TestOIDC_Path_OIDCRoleRole(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
Expand Down Expand Up @@ -1196,6 +1251,7 @@ func expectError(t *testing.T, resp *logical.Response, err error) {
// expectString fails unless every string in actualStrings is also included in expectedStrings and
// the length of actualStrings and expectedStrings are the same
func expectStrings(t *testing.T, actualStrings []string, expectedStrings map[string]interface{}) {
t.Helper()
if len(actualStrings) != len(expectedStrings) {
t.Fatalf("expectStrings mismatch:\nactual strings:\n%#v\nexpected strings:\n%#v\n", actualStrings, expectedStrings)
}
Expand Down
2 changes: 1 addition & 1 deletion website/content/api-docs/secret/identity/tokens.mdx
Expand Up @@ -238,7 +238,7 @@ Create or update a role. ID tokens are generated against a role and signed again

- `name` `(string)` – Name of the role.

- `key` `(string)` – A configured named key, the key must already exist.
- `key` `(string: <optional>)` – A configured named key, the key must already exist.
fairclothjm marked this conversation as resolved.
Show resolved Hide resolved

- `template` `(string: <optional>)` - The template string to use for generating tokens. This may be in string-ified JSON or base64 format.

Expand Down