| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| package approle |
| |
| import ( |
| "context" |
| "encoding/json" |
| "fmt" |
| "reflect" |
| "strings" |
| "testing" |
| "time" |
| |
| "github.com/go-test/deep" |
| "github.com/hashicorp/go-sockaddr" |
| "github.com/hashicorp/vault/sdk/helper/policyutil" |
| "github.com/hashicorp/vault/sdk/helper/testhelpers/schema" |
| "github.com/hashicorp/vault/sdk/helper/tokenutil" |
| "github.com/hashicorp/vault/sdk/logical" |
| "github.com/mitchellh/mapstructure" |
| ) |
| |
| func (b *backend) requestNoErr(t *testing.T, req *logical.Request) *logical.Response { |
| t.Helper() |
| resp, err := b.HandleRequest(context.Background(), req) |
| if err != nil || (resp != nil && resp.IsError()) { |
| t.Fatalf("err:%v resp:%#v", err, resp) |
| } |
| schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route(req.Path), req.Operation), resp, true) |
| return resp |
| } |
| |
| func TestAppRole_LocalSecretIDsRead(t *testing.T) { |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "local_secret_ids": true, |
| "bind_secret_id": true, |
| } |
| |
| b.requestNoErr(t, &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/testrole", |
| Storage: storage, |
| Data: roleData, |
| }) |
| |
| resp := b.requestNoErr(t, &logical.Request{ |
| Operation: logical.ReadOperation, |
| Storage: storage, |
| Path: "role/testrole/local-secret-ids", |
| }) |
| |
| if !resp.Data["local_secret_ids"].(bool) { |
| t.Fatalf("expected local_secret_ids to be returned") |
| } |
| } |
| |
| func TestAppRole_LocalNonLocalSecretIDs(t *testing.T) { |
| b, storage := createBackendWithStorage(t) |
| |
| // Create a role with local_secret_ids set |
| resp := b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole1", |
| Operation: logical.CreateOperation, |
| Storage: storage, |
| Data: map[string]interface{}{ |
| "policies": []string{"default", "role1policy"}, |
| "bind_secret_id": true, |
| "local_secret_ids": true, |
| }, |
| }) |
| |
| // Create another role without setting local_secret_ids |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole2", |
| Operation: logical.CreateOperation, |
| Storage: storage, |
| Data: map[string]interface{}{ |
| "policies": []string{"default", "role1policy"}, |
| "bind_secret_id": true, |
| }, |
| }) |
| |
| count := 10 |
| // Create secret IDs on testrole1 |
| for i := 0; i < count; i++ { |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole1/secret-id", |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| }) |
| } |
| |
| // Check the number of secret IDs generated |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole1/secret-id", |
| Operation: logical.ListOperation, |
| Storage: storage, |
| }) |
| |
| if len(resp.Data["keys"].([]string)) != count { |
| t.Fatalf("failed to list secret IDs") |
| } |
| |
| // Create secret IDs on testrole1 |
| for i := 0; i < count; i++ { |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole2/secret-id", |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| }) |
| } |
| |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole2/secret-id", |
| Operation: logical.ListOperation, |
| Storage: storage, |
| }) |
| |
| if len(resp.Data["keys"].([]string)) != count { |
| t.Fatalf("failed to list secret IDs") |
| } |
| } |
| |
| func TestAppRole_UpgradeSecretIDPrefix(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| |
| b, storage := createBackendWithStorage(t) |
| |
| // Create a role entry directly in storage without SecretIDPrefix |
| err = b.setRoleEntry(context.Background(), storage, "testrole", &roleStorageEntry{ |
| RoleID: "testroleid", |
| HMACKey: "testhmackey", |
| Policies: []string{"default"}, |
| BindSecretID: true, |
| BoundCIDRListOld: "127.0.0.1/18,192.178.1.2/24", |
| }, "") |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| // Reading the role entry should upgrade it to contain SecretIDPrefix |
| role, err := b.roleEntry(context.Background(), storage, "testrole") |
| if err != nil { |
| t.Fatal(err) |
| } |
| if role.SecretIDPrefix == "" { |
| t.Fatalf("expected SecretIDPrefix to be set") |
| } |
| |
| // Ensure that the API response contains local_secret_ids |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole", |
| Operation: logical.ReadOperation, |
| Storage: storage, |
| }) |
| |
| _, ok := resp.Data["local_secret_ids"] |
| if !ok { |
| t.Fatalf("expected local_secret_ids to be present in the response") |
| } |
| } |
| |
| func TestAppRole_LocalSecretIDImmutability(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "policies": []string{"default"}, |
| "bind_secret_id": true, |
| "bound_cidr_list": []string{"127.0.0.1/18", "192.178.1.2/24"}, |
| "local_secret_ids": true, |
| } |
| |
| // Create a role with local_secret_ids set |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole", |
| Operation: logical.CreateOperation, |
| Storage: storage, |
| Data: roleData, |
| }) |
| |
| // Attempt to modify local_secret_ids should fail |
| resp, err = b.HandleRequest(context.Background(), &logical.Request{ |
| Path: "role/testrole", |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Data: roleData, |
| }) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if resp == nil || !resp.IsError() { |
| t.Fatalf("expected an error since local_secret_ids can't be overwritten") |
| } |
| } |
| |
| func TestAppRole_UpgradeBoundCIDRList(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "policies": []string{"default"}, |
| "bind_secret_id": true, |
| "bound_cidr_list": []string{"127.0.0.1/18", "192.178.1.2/24"}, |
| } |
| |
| // Create a role with bound_cidr_list set |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole", |
| Operation: logical.CreateOperation, |
| Storage: storage, |
| Data: roleData, |
| }) |
| |
| // Read the role and check that the bound_cidr_list is set properly |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole", |
| Operation: logical.ReadOperation, |
| Storage: storage, |
| }) |
| |
| expected := []string{"127.0.0.1/18", "192.178.1.2/24"} |
| actual := resp.Data["secret_id_bound_cidrs"].([]string) |
| |
| if !reflect.DeepEqual(expected, actual) { |
| t.Fatalf("bad: secret_id_bound_cidrs; expected: %#v\nactual: %#v\n", expected, actual) |
| } |
| |
| // Modify the storage entry of the role to hold the old style string typed bound_cidr_list |
| role := &roleStorageEntry{ |
| RoleID: "testroleid", |
| HMACKey: "testhmackey", |
| Policies: []string{"default"}, |
| BindSecretID: true, |
| BoundCIDRListOld: "127.0.0.1/18,192.178.1.2/24", |
| SecretIDPrefix: secretIDPrefix, |
| } |
| err = b.setRoleEntry(context.Background(), storage, "testrole", role, "") |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| // Read the role. The upgrade code should have migrated the old type to the new type |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole", |
| Operation: logical.ReadOperation, |
| Storage: storage, |
| }) |
| |
| if !reflect.DeepEqual(expected, actual) { |
| t.Fatalf("bad: bound_cidr_list; expected: %#v\nactual: %#v\n", expected, actual) |
| } |
| |
| // Create a secret-id by supplying a subset of the role's CIDR blocks with the new type |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole/secret-id", |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Data: map[string]interface{}{ |
| "cidr_list": []string{"127.0.0.1/24"}, |
| }, |
| }) |
| |
| if resp.Data["secret_id"].(string) == "" { |
| t.Fatalf("failed to generate secret-id") |
| } |
| |
| // Check that the backwards compatibility for the string type is not broken |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrole/secret-id", |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Data: map[string]interface{}{ |
| "cidr_list": "127.0.0.1/24", |
| }, |
| }) |
| |
| if resp.Data["secret_id"].(string) == "" { |
| t.Fatalf("failed to generate secret-id") |
| } |
| } |
| |
| func TestAppRole_RoleNameLowerCasing(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| var roleID, secretID string |
| |
| b, storage := createBackendWithStorage(t) |
| |
| // Save a role with out LowerCaseRoleName set |
| role := &roleStorageEntry{ |
| RoleID: "testroleid", |
| HMACKey: "testhmackey", |
| Policies: []string{"default"}, |
| BindSecretID: true, |
| SecretIDPrefix: secretIDPrefix, |
| } |
| err = b.setRoleEntry(context.Background(), storage, "testRoleName", role, "") |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| secretIDReq := &logical.Request{ |
| Path: "role/testRoleName/secret-id", |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| } |
| resp = b.requestNoErr(t, secretIDReq) |
| |
| secretID = resp.Data["secret_id"].(string) |
| roleID = "testroleid" |
| |
| // Regular login flow. This should succeed. |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "login", |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Data: map[string]interface{}{ |
| "role_id": roleID, |
| "secret_id": secretID, |
| }, |
| }) |
| |
| // Lower case the role name when generating the secret id |
| secretIDReq.Path = "role/testrolename/secret-id" |
| resp = b.requestNoErr(t, secretIDReq) |
| |
| secretID = resp.Data["secret_id"].(string) |
| |
| // Login should fail |
| resp, err = b.HandleRequest(context.Background(), &logical.Request{ |
| Path: "login", |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Data: map[string]interface{}{ |
| "role_id": roleID, |
| "secret_id": secretID, |
| }, |
| }) |
| if err != nil && err != logical.ErrInvalidCredentials { |
| t.Fatal(err) |
| } |
| if resp == nil || !resp.IsError() { |
| t.Fatalf("expected an error") |
| } |
| |
| // Delete the role and create it again. This time don't directly persist |
| // it, but route the request to the creation handler so that it sets the |
| // LowerCaseRoleName to true. |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testRoleName", |
| Operation: logical.DeleteOperation, |
| Storage: storage, |
| }) |
| |
| roleReq := &logical.Request{ |
| Path: "role/testRoleName", |
| Operation: logical.CreateOperation, |
| Storage: storage, |
| Data: map[string]interface{}{ |
| "bind_secret_id": true, |
| }, |
| } |
| resp = b.requestNoErr(t, roleReq) |
| |
| // Create secret id with lower cased role name |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrolename/secret-id", |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| }) |
| |
| secretID = resp.Data["secret_id"].(string) |
| |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrolename/role-id", |
| Operation: logical.ReadOperation, |
| Storage: storage, |
| }) |
| |
| roleID = resp.Data["role_id"].(string) |
| |
| // Login should pass |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "login", |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Data: map[string]interface{}{ |
| "role_id": roleID, |
| "secret_id": secretID, |
| }, |
| }) |
| |
| // Lookup of secret ID should work in case-insensitive manner |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrolename/secret-id/lookup", |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Data: map[string]interface{}{ |
| "secret_id": secretID, |
| }, |
| }) |
| |
| if resp == nil { |
| t.Fatalf("failed to lookup secret IDs") |
| } |
| |
| // Listing of secret IDs should work in case-insensitive manner |
| resp = b.requestNoErr(t, &logical.Request{ |
| Path: "role/testrolename/secret-id", |
| Operation: logical.ListOperation, |
| Storage: storage, |
| }) |
| |
| if len(resp.Data["keys"].([]string)) != 1 { |
| t.Fatalf("failed to list secret IDs") |
| } |
| } |
| |
| func TestAppRole_RoleReadSetIndex(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| |
| b, storage := createBackendWithStorage(t) |
| |
| roleReq := &logical.Request{ |
| Path: "role/testrole", |
| Operation: logical.CreateOperation, |
| Storage: storage, |
| Data: map[string]interface{}{ |
| "bind_secret_id": true, |
| }, |
| } |
| |
| // Create a role |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleIDReq := &logical.Request{ |
| Path: "role/testrole/role-id", |
| Operation: logical.ReadOperation, |
| Storage: storage, |
| } |
| |
| // Get the role ID |
| resp = b.requestNoErr(t, roleIDReq) |
| |
| roleID := resp.Data["role_id"].(string) |
| |
| // Delete the role ID index |
| err = b.roleIDEntryDelete(context.Background(), storage, roleID) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| // Read the role again. This should add the index and return a warning |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| // Check if the warning is being returned |
| if !strings.Contains(resp.Warnings[0], "Role identifier was missing an index back to role name.") { |
| t.Fatalf("bad: expected a warning in the response") |
| } |
| |
| roleIDIndex, err := b.roleIDEntry(context.Background(), storage, roleID) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| // Check if the index has been successfully created |
| if roleIDIndex == nil || roleIDIndex.Name != "testrole" { |
| t.Fatalf("bad: expected role to have an index") |
| } |
| |
| roleReq.Operation = logical.UpdateOperation |
| roleReq.Data = map[string]interface{}{ |
| "bind_secret_id": true, |
| "policies": "default", |
| } |
| |
| // Check if updating and reading of roles work and that there are no lock |
| // contentions dangling due to previous operation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| } |
| |
| func TestAppRole_CIDRSubset(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "role_id": "role-id-123", |
| "policies": "a,b", |
| "bound_cidr_list": "127.0.0.1/24", |
| } |
| |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/testrole1", |
| Storage: storage, |
| Data: roleData, |
| } |
| |
| resp = b.requestNoErr(t, roleReq) |
| |
| secretIDData := map[string]interface{}{ |
| "cidr_list": "127.0.0.1/16", |
| } |
| secretIDReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/testrole1/secret-id", |
| Data: secretIDData, |
| } |
| |
| resp, err = b.HandleRequest(context.Background(), secretIDReq) |
| if resp != nil { |
| t.Fatalf("resp:%#v", resp) |
| } |
| if err == nil { |
| t.Fatal("expected an error") |
| } |
| |
| roleData["bound_cidr_list"] = "192.168.27.29/16,172.245.30.40/24,10.20.30.40/30" |
| roleReq.Operation = logical.UpdateOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| secretIDData["cidr_list"] = "192.168.27.29/20,172.245.30.40/25,10.20.30.40/32" |
| resp = b.requestNoErr(t, secretIDReq) |
| } |
| |
| func TestAppRole_TokenBoundCIDRSubset32Mask(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "role_id": "role-id-123", |
| "policies": "a,b", |
| "token_bound_cidrs": "127.0.0.1/32", |
| } |
| |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/testrole1", |
| Storage: storage, |
| Data: roleData, |
| } |
| |
| resp = b.requestNoErr(t, roleReq) |
| |
| secretIDData := map[string]interface{}{ |
| "token_bound_cidrs": "127.0.0.1/32", |
| } |
| secretIDReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/testrole1/secret-id", |
| Data: secretIDData, |
| } |
| |
| resp = b.requestNoErr(t, secretIDReq) |
| |
| secretIDData = map[string]interface{}{ |
| "token_bound_cidrs": "127.0.0.1/24", |
| } |
| secretIDReq = &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/testrole1/secret-id", |
| Data: secretIDData, |
| } |
| |
| resp, err = b.HandleRequest(context.Background(), secretIDReq) |
| if resp != nil { |
| t.Fatalf("resp:%#v", resp) |
| } |
| |
| if err == nil { |
| t.Fatal("expected an error") |
| } |
| } |
| |
| func TestAppRole_RoleConstraints(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "role_id": "role-id-123", |
| "policies": "a,b", |
| } |
| |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/testrole1", |
| Storage: storage, |
| Data: roleData, |
| } |
| |
| // Set bind_secret_id, which is enabled by default |
| resp = b.requestNoErr(t, roleReq) |
| |
| // Set bound_cidr_list alone by explicitly disabling bind_secret_id |
| roleReq.Operation = logical.UpdateOperation |
| roleData["bind_secret_id"] = false |
| roleData["bound_cidr_list"] = "0.0.0.0/0" |
| resp = b.requestNoErr(t, roleReq) |
| |
| // Remove both constraints |
| roleReq.Operation = logical.UpdateOperation |
| roleData["bound_cidr_list"] = "" |
| roleData["bind_secret_id"] = false |
| resp, err = b.HandleRequest(context.Background(), roleReq) |
| if resp != nil && resp.IsError() { |
| t.Fatalf("err:%v, resp:%#v", err, resp) |
| } |
| if err == nil { |
| t.Fatalf("expected an error") |
| } |
| } |
| |
| func TestAppRole_RoleIDUpdate(t *testing.T) { |
| var resp *logical.Response |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "role_id": "role-id-123", |
| "policies": "a,b", |
| "secret_id_num_uses": 10, |
| "secret_id_ttl": 300, |
| "token_ttl": 400, |
| "token_max_ttl": 500, |
| } |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/testrole1", |
| Storage: storage, |
| Data: roleData, |
| } |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleIDUpdateReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Path: "role/testrole1/role-id", |
| Storage: storage, |
| Data: map[string]interface{}{ |
| "role_id": "customroleid", |
| }, |
| } |
| resp = b.requestNoErr(t, roleIDUpdateReq) |
| |
| secretIDReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/testrole1/secret-id", |
| } |
| resp = b.requestNoErr(t, secretIDReq) |
| |
| secretID := resp.Data["secret_id"].(string) |
| |
| loginData := map[string]interface{}{ |
| "role_id": "customroleid", |
| "secret_id": secretID, |
| } |
| loginReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Path: "login", |
| Storage: storage, |
| Data: loginData, |
| Connection: &logical.Connection{ |
| RemoteAddr: "127.0.0.1", |
| }, |
| } |
| resp = b.requestNoErr(t, loginReq) |
| |
| if resp.Auth == nil { |
| t.Fatalf("expected a non-nil auth object in the response") |
| } |
| } |
| |
| func TestAppRole_RoleIDUniqueness(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "role_id": "role-id-123", |
| "policies": "a,b", |
| "secret_id_num_uses": 10, |
| "secret_id_ttl": 300, |
| "token_ttl": 400, |
| "token_max_ttl": 500, |
| } |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/testrole1", |
| Storage: storage, |
| Data: roleData, |
| } |
| |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Path = "role/testrole2" |
| resp, err = b.HandleRequest(context.Background(), roleReq) |
| if err == nil && !(resp != nil && resp.IsError()) { |
| t.Fatalf("expected an error: got resp:%#v", resp) |
| } |
| |
| roleData["role_id"] = "role-id-456" |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.UpdateOperation |
| roleData["role_id"] = "role-id-123" |
| resp, err = b.HandleRequest(context.Background(), roleReq) |
| if err == nil && !(resp != nil && resp.IsError()) { |
| t.Fatalf("expected an error: got resp:%#v", resp) |
| } |
| |
| roleReq.Path = "role/testrole1" |
| roleData["role_id"] = "role-id-456" |
| resp, err = b.HandleRequest(context.Background(), roleReq) |
| if err == nil && !(resp != nil && resp.IsError()) { |
| t.Fatalf("expected an error: got resp:%#v", resp) |
| } |
| |
| roleIDData := map[string]interface{}{ |
| "role_id": "role-id-456", |
| } |
| roleIDReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Path: "role/testrole1/role-id", |
| Storage: storage, |
| Data: roleIDData, |
| } |
| resp, err = b.HandleRequest(context.Background(), roleIDReq) |
| if err == nil && !(resp != nil && resp.IsError()) { |
| t.Fatalf("expected an error: got resp:%#v", resp) |
| } |
| |
| roleIDData["role_id"] = "role-id-123" |
| roleIDReq.Path = "role/testrole2/role-id" |
| resp, err = b.HandleRequest(context.Background(), roleIDReq) |
| if err == nil && !(resp != nil && resp.IsError()) { |
| t.Fatalf("expected an error: got resp:%#v", resp) |
| } |
| |
| roleIDData["role_id"] = "role-id-2000" |
| resp = b.requestNoErr(t, roleIDReq) |
| |
| roleIDData["role_id"] = "role-id-1000" |
| roleIDReq.Path = "role/testrole1/role-id" |
| resp = b.requestNoErr(t, roleIDReq) |
| } |
| |
| func TestAppRole_RoleDeleteSecretID(t *testing.T) { |
| var resp *logical.Response |
| b, storage := createBackendWithStorage(t) |
| |
| createRole(t, b, storage, "role1", "a,b") |
| secretIDReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id", |
| } |
| // Create 3 secrets on the role |
| resp = b.requestNoErr(t, secretIDReq) |
| resp = b.requestNoErr(t, secretIDReq) |
| resp = b.requestNoErr(t, secretIDReq) |
| |
| listReq := &logical.Request{ |
| Operation: logical.ListOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id", |
| } |
| resp = b.requestNoErr(t, listReq) |
| |
| secretIDAccessors := resp.Data["keys"].([]string) |
| if len(secretIDAccessors) != 3 { |
| t.Fatalf("bad: len of secretIDAccessors: expected:3 actual:%d", len(secretIDAccessors)) |
| } |
| |
| roleReq := &logical.Request{ |
| Operation: logical.DeleteOperation, |
| Storage: storage, |
| Path: "role/role1", |
| } |
| resp = b.requestNoErr(t, roleReq) |
| |
| resp, err := b.HandleRequest(context.Background(), listReq) |
| if err != nil || resp == nil || (resp != nil && !resp.IsError()) { |
| t.Fatalf("expected an error. err:%v resp:%#v", err, resp) |
| } |
| } |
| |
| func TestAppRole_RoleSecretIDReadDelete(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| b, storage := createBackendWithStorage(t) |
| |
| createRole(t, b, storage, "role1", "a,b") |
| secretIDCreateReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id", |
| } |
| resp = b.requestNoErr(t, secretIDCreateReq) |
| |
| secretID := resp.Data["secret_id"].(string) |
| if secretID == "" { |
| t.Fatal("expected non empty secret ID") |
| } |
| |
| secretIDReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id/lookup", |
| Data: map[string]interface{}{ |
| "secret_id": secretID, |
| }, |
| } |
| resp = b.requestNoErr(t, secretIDReq) |
| |
| if resp.Data == nil { |
| t.Fatal(err) |
| } |
| |
| deleteSecretIDReq := &logical.Request{ |
| Operation: logical.DeleteOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id/destroy", |
| Data: map[string]interface{}{ |
| "secret_id": secretID, |
| }, |
| } |
| resp = b.requestNoErr(t, deleteSecretIDReq) |
| resp, err = b.HandleRequest(context.Background(), secretIDReq) |
| if resp != nil && resp.IsError() { |
| t.Fatalf("error response:%#v", resp) |
| } |
| if err != nil { |
| t.Fatal(err) |
| } |
| } |
| |
| func TestAppRole_RoleSecretIDAccessorReadDelete(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| b, storage := createBackendWithStorage(t) |
| |
| createRole(t, b, storage, "role1", "a,b") |
| secretIDReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id", |
| } |
| resp = b.requestNoErr(t, secretIDReq) |
| |
| listReq := &logical.Request{ |
| Operation: logical.ListOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id", |
| } |
| resp = b.requestNoErr(t, listReq) |
| |
| hmacSecretID := resp.Data["keys"].([]string)[0] |
| |
| hmacReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id-accessor/lookup", |
| Data: map[string]interface{}{ |
| "secret_id_accessor": hmacSecretID, |
| }, |
| } |
| resp = b.requestNoErr(t, hmacReq) |
| |
| if resp.Data == nil { |
| t.Fatal(err) |
| } |
| |
| hmacReq.Path = "role/role1/secret-id-accessor/destroy" |
| resp = b.requestNoErr(t, hmacReq) |
| |
| hmacReq.Operation = logical.ReadOperation |
| resp, err = b.HandleRequest(context.Background(), hmacReq) |
| if resp != nil && resp.IsError() { |
| t.Fatalf("err:%v resp:%#v", err, resp) |
| } |
| if err == nil { |
| t.Fatalf("expected an error") |
| } |
| } |
| |
| func TestAppRoleSecretIDLookup(t *testing.T) { |
| b, storage := createBackendWithStorage(t) |
| createRole(t, b, storage, "role1", "a,b") |
| |
| req := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id-accessor/lookup", |
| Data: map[string]interface{}{ |
| "secret_id_accessor": "invalid", |
| }, |
| } |
| resp, err := b.HandleRequest(context.Background(), req) |
| if err != nil { |
| t.Fatalf("unexpected error: %v", err) |
| } |
| expected := &logical.Response{ |
| Data: map[string]interface{}{ |
| "http_content_type": "application/json", |
| "http_raw_body": `{"request_id":"","lease_id":"","renewable":false,"lease_duration":0,"data":{"error":"failed to find accessor entry for secret_id_accessor: \"invalid\""},"wrap_info":null,"warnings":null,"auth":null}`, |
| "http_status_code": 404, |
| }, |
| } |
| if !reflect.DeepEqual(resp, expected) { |
| t.Fatalf("resp:%#v expected:%#v", resp, expected) |
| } |
| } |
| |
| func TestAppRoleRoleListSecretID(t *testing.T) { |
| var resp *logical.Response |
| b, storage := createBackendWithStorage(t) |
| |
| createRole(t, b, storage, "role1", "a,b") |
| |
| secretIDReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id", |
| } |
| // Create 5 'secret_id's |
| resp = b.requestNoErr(t, secretIDReq) |
| resp = b.requestNoErr(t, secretIDReq) |
| resp = b.requestNoErr(t, secretIDReq) |
| resp = b.requestNoErr(t, secretIDReq) |
| resp = b.requestNoErr(t, secretIDReq) |
| |
| listReq := &logical.Request{ |
| Operation: logical.ListOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id/", |
| } |
| resp = b.requestNoErr(t, listReq) |
| |
| secrets := resp.Data["keys"].([]string) |
| if len(secrets) != 5 { |
| t.Fatalf("bad: len of secrets: expected:5 actual:%d", len(secrets)) |
| } |
| } |
| |
| func TestAppRole_RoleList(t *testing.T) { |
| var resp *logical.Response |
| b, storage := createBackendWithStorage(t) |
| |
| createRole(t, b, storage, "role1", "a,b") |
| createRole(t, b, storage, "role2", "c,d") |
| createRole(t, b, storage, "role3", "e,f") |
| createRole(t, b, storage, "role4", "g,h") |
| createRole(t, b, storage, "role5", "i,j") |
| |
| listReq := &logical.Request{ |
| Operation: logical.ListOperation, |
| Path: "role", |
| Storage: storage, |
| } |
| resp = b.requestNoErr(t, listReq) |
| |
| actual := resp.Data["keys"].([]string) |
| expected := []string{"role1", "role2", "role3", "role4", "role5"} |
| if !policyutil.EquivalentPolicies(actual, expected) { |
| t.Fatalf("bad: listed roles: expected:%s\nactual:%s", expected, actual) |
| } |
| } |
| |
| func TestAppRole_RoleSecretIDWithoutFields(t *testing.T) { |
| var resp *logical.Response |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "policies": "p,q,r,s", |
| "secret_id_num_uses": 10, |
| "secret_id_ttl": 300, |
| "token_ttl": 400, |
| "token_max_ttl": 500, |
| } |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/role1", |
| Storage: storage, |
| Data: roleData, |
| } |
| |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleSecretIDReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Path: "role/role1/secret-id", |
| Storage: storage, |
| } |
| resp = b.requestNoErr(t, roleSecretIDReq) |
| |
| if resp.Data["secret_id"].(string) == "" { |
| t.Fatalf("failed to generate secret_id") |
| } |
| if resp.Data["secret_id_ttl"].(int64) != int64(roleData["secret_id_ttl"].(int)) { |
| t.Fatalf("secret_id_ttl has not defaulted to the role's secret id ttl") |
| } |
| if resp.Data["secret_id_num_uses"].(int) != roleData["secret_id_num_uses"].(int) { |
| t.Fatalf("secret_id_num_uses has not defaulted to the role's secret id num_uses") |
| } |
| |
| roleSecretIDReq.Path = "role/role1/custom-secret-id" |
| roleCustomSecretIDData := map[string]interface{}{ |
| "secret_id": "abcd123", |
| } |
| roleSecretIDReq.Data = roleCustomSecretIDData |
| resp = b.requestNoErr(t, roleSecretIDReq) |
| |
| if resp.Data["secret_id"] != "abcd123" { |
| t.Fatalf("failed to set specific secret_id to role") |
| } |
| if resp.Data["secret_id_ttl"].(int64) != int64(roleData["secret_id_ttl"].(int)) { |
| t.Fatalf("secret_id_ttl has not defaulted to the role's secret id ttl") |
| } |
| if resp.Data["secret_id_num_uses"].(int) != roleData["secret_id_num_uses"].(int) { |
| t.Fatalf("secret_id_num_uses has not defaulted to the role's secret id num_uses") |
| } |
| } |
| |
| func TestAppRole_RoleSecretIDWithValidFields(t *testing.T) { |
| type testCase struct { |
| name string |
| payload map[string]interface{} |
| } |
| |
| var resp *logical.Response |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "policies": "p,q,r,s", |
| "secret_id_num_uses": 0, |
| "secret_id_ttl": 0, |
| "token_ttl": 400, |
| "token_max_ttl": 500, |
| } |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/role1", |
| Storage: storage, |
| Data: roleData, |
| } |
| |
| resp = b.requestNoErr(t, roleReq) |
| |
| testCases := []testCase{ |
| { |
| name: "finite num_uses ttl", |
| payload: map[string]interface{}{"secret_id": "finite", "ttl": 5, "num_uses": 5}, |
| }, |
| { |
| name: "infinite num_uses and ttl", |
| payload: map[string]interface{}{"secret_id": "infinite", "ttl": 0, "num_uses": 0}, |
| }, |
| { |
| name: "finite num_uses and infinite ttl", |
| payload: map[string]interface{}{"secret_id": "mixed1", "ttl": 0, "num_uses": 5}, |
| }, |
| { |
| name: "infinite num_uses and finite ttl", |
| payload: map[string]interface{}{"secret_id": "mixed2", "ttl": 5, "num_uses": 0}, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| t.Run(tc.name, func(t *testing.T) { |
| roleSecretIDReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Path: "role/role1/secret-id", |
| Storage: storage, |
| } |
| roleCustomSecretIDData := tc.payload |
| roleSecretIDReq.Data = roleCustomSecretIDData |
| |
| resp = b.requestNoErr(t, roleSecretIDReq) |
| |
| if resp.Data["secret_id"].(string) == "" { |
| t.Fatalf("failed to generate secret_id") |
| } |
| if resp.Data["secret_id_ttl"].(int64) != int64(tc.payload["ttl"].(int)) { |
| t.Fatalf("secret_id_ttl has not been set by the 'ttl' field") |
| } |
| if resp.Data["secret_id_num_uses"].(int) != tc.payload["num_uses"].(int) { |
| t.Fatalf("secret_id_num_uses has not been set by the 'num_uses' field") |
| } |
| |
| roleSecretIDReq.Path = "role/role1/custom-secret-id" |
| roleSecretIDReq.Data = roleCustomSecretIDData |
| resp = b.requestNoErr(t, roleSecretIDReq) |
| |
| if resp.Data["secret_id"] != tc.payload["secret_id"] { |
| t.Fatalf("failed to set specific secret_id to role") |
| } |
| if resp.Data["secret_id_ttl"].(int64) != int64(tc.payload["ttl"].(int)) { |
| t.Fatalf("secret_id_ttl has not been set by the 'ttl' field") |
| } |
| if resp.Data["secret_id_num_uses"].(int) != tc.payload["num_uses"].(int) { |
| t.Fatalf("secret_id_num_uses has not been set by the 'num_uses' field") |
| } |
| }) |
| } |
| } |
| |
| func TestAppRole_ErrorsRoleSecretIDWithInvalidFields(t *testing.T) { |
| type testCase struct { |
| name string |
| payload map[string]interface{} |
| expected string |
| } |
| |
| type roleTestCase struct { |
| name string |
| options map[string]interface{} |
| cases []testCase |
| } |
| |
| infiniteTestCases := []testCase{ |
| { |
| name: "infinite ttl", |
| payload: map[string]interface{}{"secret_id": "abcd123", "num_uses": 1, "ttl": 0}, |
| expected: "ttl cannot be longer than the role's secret_id_ttl", |
| }, |
| { |
| name: "infinite num_uses", |
| payload: map[string]interface{}{"secret_id": "abcd123", "num_uses": 0, "ttl": 1}, |
| expected: "num_uses cannot be higher than the role's secret_id_num_uses", |
| }, |
| } |
| |
| negativeTestCases := []testCase{ |
| { |
| name: "negative num_uses", |
| payload: map[string]interface{}{"secret_id": "abcd123", "num_uses": -1, "ttl": 0}, |
| expected: "num_uses cannot be negative", |
| }, |
| } |
| |
| roleTestCases := []roleTestCase{ |
| { |
| name: "infinite role secret id ttl", |
| options: map[string]interface{}{ |
| "secret_id_num_uses": 1, |
| "secret_id_ttl": 0, |
| }, |
| cases: []testCase{ |
| { |
| name: "higher num_uses", |
| payload: map[string]interface{}{"secret_id": "abcd123", "ttl": 0, "num_uses": 2}, |
| expected: "num_uses cannot be higher than the role's secret_id_num_uses", |
| }, |
| }, |
| }, |
| { |
| name: "infinite role num_uses", |
| options: map[string]interface{}{ |
| "secret_id_num_uses": 0, |
| "secret_id_ttl": 1, |
| }, |
| cases: []testCase{ |
| { |
| name: "longer ttl", |
| payload: map[string]interface{}{"secret_id": "abcd123", "ttl": 2, "num_uses": 0}, |
| expected: "ttl cannot be longer than the role's secret_id_ttl", |
| }, |
| }, |
| }, |
| { |
| name: "finite role ttl and num_uses", |
| options: map[string]interface{}{ |
| "secret_id_num_uses": 2, |
| "secret_id_ttl": 2, |
| }, |
| cases: infiniteTestCases, |
| }, |
| { |
| name: "mixed role ttl and num_uses", |
| options: map[string]interface{}{ |
| "secret_id_num_uses": 400, |
| "secret_id_ttl": 500, |
| }, |
| cases: negativeTestCases, |
| }, |
| } |
| |
| var resp *logical.Response |
| var err error |
| b, storage := createBackendWithStorage(t) |
| |
| for i, rc := range roleTestCases { |
| roleData := map[string]interface{}{ |
| "policies": "p,q,r,s", |
| "token_ttl": 400, |
| "token_max_ttl": 500, |
| } |
| roleData["secret_id_num_uses"] = rc.options["secret_id_num_uses"] |
| roleData["secret_id_ttl"] = rc.options["secret_id_ttl"] |
| |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: fmt.Sprintf("role/role%d", i), |
| Storage: storage, |
| Data: roleData, |
| } |
| |
| resp = b.requestNoErr(t, roleReq) |
| |
| for _, tc := range rc.cases { |
| t.Run(fmt.Sprintf("%s/%s", rc.name, tc.name), func(t *testing.T) { |
| roleSecretIDReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Path: fmt.Sprintf("role/role%d/secret-id", i), |
| Storage: storage, |
| } |
| roleSecretIDReq.Data = tc.payload |
| resp, err = b.HandleRequest(context.Background(), roleSecretIDReq) |
| if err != nil || (resp != nil && !resp.IsError()) { |
| t.Fatalf("err:%v resp:%#v", err, resp) |
| } |
| if resp.Data["error"].(string) != tc.expected { |
| t.Fatalf("expected: %q, got: %q", tc.expected, resp.Data["error"].(string)) |
| } |
| |
| roleSecretIDReq.Path = fmt.Sprintf("role/role%d/custom-secret-id", i) |
| resp, err = b.HandleRequest(context.Background(), roleSecretIDReq) |
| if err != nil || (resp != nil && !resp.IsError()) { |
| t.Fatalf("err:%v resp:%#v", err, resp) |
| } |
| if resp.Data["error"].(string) != tc.expected { |
| t.Fatalf("expected: %q, got: %q", tc.expected, resp.Data["error"].(string)) |
| } |
| }) |
| } |
| } |
| } |
| |
| func TestAppRole_RoleCRUD(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "policies": "p,q,r,s", |
| "secret_id_num_uses": 10, |
| "secret_id_ttl": 300, |
| "token_ttl": 400, |
| "token_max_ttl": 500, |
| "token_num_uses": 600, |
| "secret_id_bound_cidrs": "127.0.0.1/32,127.0.0.1/16", |
| } |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/role1", |
| Storage: storage, |
| Data: roleData, |
| } |
| |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| expected := map[string]interface{}{ |
| "bind_secret_id": true, |
| "policies": []string{"p", "q", "r", "s"}, |
| "secret_id_num_uses": 10, |
| "secret_id_ttl": 300, |
| "token_ttl": 400, |
| "token_max_ttl": 500, |
| "token_num_uses": 600, |
| "secret_id_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"}, |
| "token_bound_cidrs": []string{}, |
| "token_type": "default", |
| } |
| |
| var expectedStruct roleStorageEntry |
| err = mapstructure.Decode(expected, &expectedStruct) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| var actualStruct roleStorageEntry |
| err = mapstructure.Decode(resp.Data, &actualStruct) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| expectedStruct.RoleID = actualStruct.RoleID |
| if diff := deep.Equal(expectedStruct, actualStruct); diff != nil { |
| t.Fatal(diff) |
| } |
| |
| roleData = map[string]interface{}{ |
| "role_id": "test_role_id", |
| "policies": "a,b,c,d", |
| "secret_id_num_uses": 100, |
| "secret_id_ttl": 3000, |
| "token_ttl": 4000, |
| "token_max_ttl": 5000, |
| } |
| roleReq.Data = roleData |
| roleReq.Operation = logical.UpdateOperation |
| |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| expected = map[string]interface{}{ |
| "policies": []string{"a", "b", "c", "d"}, |
| "secret_id_num_uses": 100, |
| "secret_id_ttl": 3000, |
| "token_ttl": 4000, |
| "token_max_ttl": 5000, |
| } |
| err = mapstructure.Decode(expected, &expectedStruct) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| err = mapstructure.Decode(resp.Data, &actualStruct) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if !reflect.DeepEqual(expectedStruct, actualStruct) { |
| t.Fatalf("bad:\nexpected:%#v\nactual:%#v\n", expectedStruct, actualStruct) |
| } |
| |
| // RU for role_id field |
| roleReq.Path = "role/role1/role-id" |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["role_id"].(string) != "test_role_id" { |
| t.Fatalf("bad: role_id: expected:test_role_id actual:%s\n", resp.Data["role_id"].(string)) |
| } |
| |
| roleReq.Data = map[string]interface{}{"role_id": "custom_role_id"} |
| roleReq.Operation = logical.UpdateOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["role_id"].(string) != "custom_role_id" { |
| t.Fatalf("bad: role_id: expected:custom_role_id actual:%s\n", resp.Data["role_id"].(string)) |
| } |
| |
| // RUD for bind_secret_id field |
| roleReq.Path = "role/role1/bind-secret-id" |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Data = map[string]interface{}{"bind_secret_id": false} |
| roleReq.Operation = logical.UpdateOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["bind_secret_id"].(bool) { |
| t.Fatalf("bad: bind_secret_id: expected:false actual:%t\n", resp.Data["bind_secret_id"].(bool)) |
| } |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if !resp.Data["bind_secret_id"].(bool) { |
| t.Fatalf("expected the default value of 'true' to be set") |
| } |
| |
| // RUD for policies field |
| roleReq.Path = "role/role1/policies" |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Data = map[string]interface{}{"policies": "a1,b1,c1,d1"} |
| roleReq.Operation = logical.UpdateOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if !reflect.DeepEqual(resp.Data["policies"].([]string), []string{"a1", "b1", "c1", "d1"}) { |
| t.Fatalf("bad: policies: actual:%s\n", resp.Data["policies"].([]string)) |
| } |
| if !reflect.DeepEqual(resp.Data["token_policies"].([]string), []string{"a1", "b1", "c1", "d1"}) { |
| t.Fatalf("bad: policies: actual:%s\n", resp.Data["policies"].([]string)) |
| } |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| expectedPolicies := []string{} |
| actualPolicies := resp.Data["token_policies"].([]string) |
| if !policyutil.EquivalentPolicies(expectedPolicies, actualPolicies) { |
| t.Fatalf("bad: token_policies: expected:%s actual:%s", expectedPolicies, actualPolicies) |
| } |
| |
| // RUD for secret-id-num-uses field |
| roleReq.Path = "role/role1/secret-id-num-uses" |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Data = map[string]interface{}{"secret_id_num_uses": 200} |
| roleReq.Operation = logical.UpdateOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["secret_id_num_uses"].(int) != 200 { |
| t.Fatalf("bad: secret_id_num_uses: expected:200 actual:%d\n", resp.Data["secret_id_num_uses"].(int)) |
| } |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["secret_id_num_uses"].(int) != 0 { |
| t.Fatalf("expected value to be reset") |
| } |
| |
| // RUD for secret_id_ttl field |
| roleReq.Path = "role/role1/secret-id-ttl" |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Data = map[string]interface{}{"secret_id_ttl": 3001} |
| roleReq.Operation = logical.UpdateOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["secret_id_ttl"].(time.Duration) != 3001 { |
| t.Fatalf("bad: secret_id_ttl: expected:3001 actual:%d\n", resp.Data["secret_id_ttl"].(time.Duration)) |
| } |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["secret_id_ttl"].(time.Duration) != 0 { |
| t.Fatalf("expected value to be reset") |
| } |
| |
| // RUD for secret-id-num-uses field |
| roleReq.Path = "role/role1/token-num-uses" |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["token_num_uses"].(int) != 600 { |
| t.Fatalf("bad: token_num_uses: expected:600 actual:%d\n", resp.Data["token_num_uses"].(int)) |
| } |
| |
| roleReq.Data = map[string]interface{}{"token_num_uses": 60} |
| roleReq.Operation = logical.UpdateOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["token_num_uses"].(int) != 60 { |
| t.Fatalf("bad: token_num_uses: expected:60 actual:%d\n", resp.Data["token_num_uses"].(int)) |
| } |
| |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["token_num_uses"].(int) != 0 { |
| t.Fatalf("expected value to be reset") |
| } |
| |
| // RUD for 'period' field |
| roleReq.Path = "role/role1/period" |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Data = map[string]interface{}{"period": 9001} |
| roleReq.Operation = logical.UpdateOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["period"].(time.Duration) != 9001 { |
| t.Fatalf("bad: period: expected:9001 actual:%d\n", resp.Data["9001"].(time.Duration)) |
| } |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["token_period"].(time.Duration) != 0 { |
| t.Fatalf("expected value to be reset") |
| } |
| |
| // RUD for token_ttl field |
| roleReq.Path = "role/role1/token-ttl" |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Data = map[string]interface{}{"token_ttl": 4001} |
| roleReq.Operation = logical.UpdateOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["token_ttl"].(time.Duration) != 4001 { |
| t.Fatalf("bad: token_ttl: expected:4001 actual:%d\n", resp.Data["token_ttl"].(time.Duration)) |
| } |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["token_ttl"].(time.Duration) != 0 { |
| t.Fatalf("expected value to be reset") |
| } |
| |
| // RUD for token_max_ttl field |
| roleReq.Path = "role/role1/token-max-ttl" |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Data = map[string]interface{}{"token_max_ttl": 5001} |
| roleReq.Operation = logical.UpdateOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["token_max_ttl"].(time.Duration) != 5001 { |
| t.Fatalf("bad: token_max_ttl: expected:5001 actual:%d\n", resp.Data["token_max_ttl"].(time.Duration)) |
| } |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["token_max_ttl"].(time.Duration) != 0 { |
| t.Fatalf("expected value to be reset") |
| } |
| |
| // Delete test for role |
| roleReq.Path = "role/role1" |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp, err = b.HandleRequest(context.Background(), roleReq) |
| if err != nil || (resp != nil && resp.IsError()) { |
| t.Fatalf("err:%v resp:%#v", err, resp) |
| } |
| |
| if resp != nil { |
| t.Fatalf("expected a nil response") |
| } |
| } |
| |
| func TestAppRole_RoleWithTokenBoundCIDRsCRUD(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "policies": "p,q,r,s", |
| "secret_id_num_uses": 10, |
| "secret_id_ttl": 300, |
| "token_ttl": 400, |
| "token_max_ttl": 500, |
| "token_num_uses": 600, |
| "secret_id_bound_cidrs": "127.0.0.1/32,127.0.0.1/16", |
| "token_bound_cidrs": "127.0.0.1/32,127.0.0.1/16", |
| } |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/role1", |
| Storage: storage, |
| Data: roleData, |
| } |
| |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| expected := map[string]interface{}{ |
| "bind_secret_id": true, |
| "policies": []string{"p", "q", "r", "s"}, |
| "secret_id_num_uses": 10, |
| "secret_id_ttl": 300, |
| "token_ttl": 400, |
| "token_max_ttl": 500, |
| "token_num_uses": 600, |
| "token_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"}, |
| "secret_id_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"}, |
| "token_type": "default", |
| } |
| |
| var expectedStruct roleStorageEntry |
| err = mapstructure.Decode(expected, &expectedStruct) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| var actualStruct roleStorageEntry |
| err = mapstructure.Decode(resp.Data, &actualStruct) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| expectedStruct.RoleID = actualStruct.RoleID |
| if !reflect.DeepEqual(expectedStruct, actualStruct) { |
| t.Fatalf("bad:\nexpected:%#v\nactual:%#v\n", expectedStruct, actualStruct) |
| } |
| |
| roleData = map[string]interface{}{ |
| "role_id": "test_role_id", |
| "policies": "a,b,c,d", |
| "secret_id_num_uses": 100, |
| "secret_id_ttl": 3000, |
| "token_ttl": 4000, |
| "token_max_ttl": 5000, |
| } |
| roleReq.Data = roleData |
| roleReq.Operation = logical.UpdateOperation |
| |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| expected = map[string]interface{}{ |
| "policies": []string{"a", "b", "c", "d"}, |
| "secret_id_num_uses": 100, |
| "secret_id_ttl": 3000, |
| "token_ttl": 4000, |
| "token_max_ttl": 5000, |
| } |
| err = mapstructure.Decode(expected, &expectedStruct) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| err = mapstructure.Decode(resp.Data, &actualStruct) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if !reflect.DeepEqual(expectedStruct, actualStruct) { |
| t.Fatalf("bad:\nexpected:%#v\nactual:%#v\n", expectedStruct, actualStruct) |
| } |
| |
| // RUD for secret-id-bound-cidrs field |
| roleReq.Path = "role/role1/secret-id-bound-cidrs" |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["secret_id_bound_cidrs"].([]string)[0] != "127.0.0.1/32" || |
| resp.Data["secret_id_bound_cidrs"].([]string)[1] != "127.0.0.1/16" { |
| t.Fatalf("bad: secret_id_bound_cidrs: expected:127.0.0.1/32,127.0.0.1/16 actual:%d\n", resp.Data["secret_id_bound_cidrs"].(int)) |
| } |
| |
| roleReq.Data = map[string]interface{}{"secret_id_bound_cidrs": []string{"127.0.0.1/20"}} |
| roleReq.Operation = logical.UpdateOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["secret_id_bound_cidrs"].([]string)[0] != "127.0.0.1/20" { |
| t.Fatalf("bad: secret_id_bound_cidrs: expected:127.0.0.1/20 actual:%s\n", resp.Data["secret_id_bound_cidrs"].([]string)[0]) |
| } |
| |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if len(resp.Data["secret_id_bound_cidrs"].([]string)) != 0 { |
| t.Fatalf("expected value to be reset") |
| } |
| |
| // RUD for token-bound-cidrs field |
| roleReq.Path = "role/role1/token-bound-cidrs" |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" || |
| resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[1].String() != "127.0.0.1/16" { |
| m, err := json.Marshal(resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)) |
| if err != nil { |
| t.Fatal(err) |
| } |
| t.Fatalf("bad: token_bound_cidrs: expected:127.0.0.1/32,127.0.0.1/16 actual:%s\n", string(m)) |
| } |
| |
| roleReq.Data = map[string]interface{}{"token_bound_cidrs": []string{"127.0.0.1/20"}} |
| roleReq.Operation = logical.UpdateOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1/20" { |
| t.Fatalf("bad: token_bound_cidrs: expected:127.0.0.1/20 actual:%s\n", resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0]) |
| } |
| |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| if len(resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)) != 0 { |
| t.Fatalf("expected value to be reset") |
| } |
| |
| // Delete test for role |
| roleReq.Path = "role/role1" |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp, err = b.HandleRequest(context.Background(), roleReq) |
| if err != nil || (resp != nil && resp.IsError()) { |
| t.Fatalf("err:%v resp:%#v", err, resp) |
| } |
| if resp != nil { |
| t.Fatalf("expected a nil response") |
| } |
| } |
| |
| func TestAppRole_RoleWithTokenTypeCRUD(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| b, storage := createBackendWithStorage(t) |
| |
| roleData := map[string]interface{}{ |
| "policies": "p,q,r,s", |
| "secret_id_num_uses": 10, |
| "secret_id_ttl": 300, |
| "token_ttl": 400, |
| "token_max_ttl": 500, |
| "token_num_uses": 600, |
| "token_type": "default-service", |
| } |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/role1", |
| Storage: storage, |
| Data: roleData, |
| } |
| |
| resp = b.requestNoErr(t, roleReq) |
| |
| if 0 == len(resp.Warnings) { |
| t.Fatalf("bad:\nexpected warning in resp:%#v\n", resp.Warnings) |
| } |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| expected := map[string]interface{}{ |
| "bind_secret_id": true, |
| "policies": []string{"p", "q", "r", "s"}, |
| "secret_id_num_uses": 10, |
| "secret_id_ttl": 300, |
| "token_ttl": 400, |
| "token_max_ttl": 500, |
| "token_num_uses": 600, |
| "token_type": "service", |
| } |
| |
| var expectedStruct roleStorageEntry |
| err = mapstructure.Decode(expected, &expectedStruct) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| var actualStruct roleStorageEntry |
| err = mapstructure.Decode(resp.Data, &actualStruct) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| expectedStruct.RoleID = actualStruct.RoleID |
| if !reflect.DeepEqual(expectedStruct, actualStruct) { |
| t.Fatalf("bad:\nexpected:%#v\nactual:%#v\n", expectedStruct, actualStruct) |
| } |
| |
| roleData = map[string]interface{}{ |
| "role_id": "test_role_id", |
| "policies": "a,b,c,d", |
| "secret_id_num_uses": 100, |
| "secret_id_ttl": 3000, |
| "token_ttl": 4000, |
| "token_max_ttl": 5000, |
| "token_type": "default-service", |
| } |
| roleReq.Data = roleData |
| roleReq.Operation = logical.UpdateOperation |
| |
| resp = b.requestNoErr(t, roleReq) |
| |
| if 0 == len(resp.Warnings) { |
| t.Fatalf("bad:\nexpected a warning in resp:%#v\n", resp.Warnings) |
| } |
| |
| roleReq.Operation = logical.ReadOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| expected = map[string]interface{}{ |
| "policies": []string{"a", "b", "c", "d"}, |
| "secret_id_num_uses": 100, |
| "secret_id_ttl": 3000, |
| "token_ttl": 4000, |
| "token_max_ttl": 5000, |
| "token_type": "service", |
| } |
| err = mapstructure.Decode(expected, &expectedStruct) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| err = mapstructure.Decode(resp.Data, &actualStruct) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if !reflect.DeepEqual(expectedStruct, actualStruct) { |
| t.Fatalf("bad:\nexpected:%#v\nactual:%#v\n", expectedStruct, actualStruct) |
| } |
| |
| // Delete test for role |
| roleReq.Path = "role/role1" |
| roleReq.Operation = logical.DeleteOperation |
| resp = b.requestNoErr(t, roleReq) |
| |
| roleReq.Operation = logical.ReadOperation |
| resp, err = b.HandleRequest(context.Background(), roleReq) |
| if err != nil || (resp != nil && resp.IsError()) { |
| t.Fatalf("err:%v resp:%#v", err, resp) |
| } |
| if resp != nil { |
| t.Fatalf("expected a nil response") |
| } |
| } |
| |
| func createRole(t *testing.T, b *backend, s logical.Storage, roleName, policies string) { |
| roleData := map[string]interface{}{ |
| "policies": policies, |
| "secret_id_num_uses": 10, |
| "secret_id_ttl": 300, |
| "token_ttl": 400, |
| "token_max_ttl": 500, |
| } |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/" + roleName, |
| Storage: s, |
| Data: roleData, |
| } |
| _ = b.requestNoErr(t, roleReq) |
| } |
| |
| // TestAppRole_TokenutilUpgrade ensures that when we read values out that are |
| // values with upgrade logic we see the correct struct entries populated |
| func TestAppRole_TokenutilUpgrade(t *testing.T) { |
| tests := []struct { |
| name string |
| storageValMissing bool |
| storageVal string |
| expectedTokenType logical.TokenType |
| }{ |
| { |
| "token_type_missing", |
| true, |
| "", |
| logical.TokenTypeDefault, |
| }, |
| { |
| "token_type_empty", |
| false, |
| "", |
| logical.TokenTypeDefault, |
| }, |
| { |
| "token_type_service", |
| false, |
| "service", |
| logical.TokenTypeService, |
| }, |
| } |
| |
| s := &logical.InmemStorage{} |
| |
| config := logical.TestBackendConfig() |
| config.StorageView = s |
| |
| ctx := context.Background() |
| |
| b, err := Backend(config) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if b == nil { |
| t.Fatalf("failed to create backend") |
| } |
| if err := b.Setup(ctx, config); err != nil { |
| t.Fatal(err) |
| } |
| |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| // Construct the storage entry object based on our test case. |
| tokenTypeKV := "" |
| if !tt.storageValMissing { |
| tokenTypeKV = fmt.Sprintf(`, "token_type": "%s"`, tt.storageVal) |
| } |
| entryVal := fmt.Sprintf(`{"policies": ["foo"], "period": 300000000000, "token_bound_cidrs": ["127.0.0.1", "10.10.10.10/24"]%s}`, tokenTypeKV) |
| |
| // Hand craft JSON because there is overlap between fields |
| if err := s.Put(ctx, &logical.StorageEntry{ |
| Key: "role/" + tt.name, |
| Value: []byte(entryVal), |
| }); err != nil { |
| t.Fatal(err) |
| } |
| |
| resEntry, err := b.roleEntry(ctx, s, tt.name) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| exp := &roleStorageEntry{ |
| SecretIDPrefix: "secret_id/", |
| Policies: []string{"foo"}, |
| Period: 300 * time.Second, |
| TokenParams: tokenutil.TokenParams{ |
| TokenPolicies: []string{"foo"}, |
| TokenPeriod: 300 * time.Second, |
| TokenBoundCIDRs: []*sockaddr.SockAddrMarshaler{ |
| {SockAddr: sockaddr.MustIPAddr("127.0.0.1")}, |
| {SockAddr: sockaddr.MustIPAddr("10.10.10.10/24")}, |
| }, |
| TokenType: tt.expectedTokenType, |
| }, |
| } |
| if diff := deep.Equal(resEntry, exp); diff != nil { |
| t.Fatal(diff) |
| } |
| }) |
| } |
| } |
| |
| func TestAppRole_SecretID_WithTTL(t *testing.T) { |
| tests := []struct { |
| name string |
| roleName string |
| ttl int64 |
| sysTTLCap bool |
| }{ |
| { |
| "zero ttl", |
| "role-zero-ttl", |
| 0, |
| false, |
| }, |
| { |
| "custom ttl", |
| "role-custom-ttl", |
| 60, |
| false, |
| }, |
| { |
| "system ttl capped", |
| "role-sys-ttl-cap", |
| 700000000, |
| true, |
| }, |
| } |
| |
| b, storage := createBackendWithStorage(t) |
| |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| // Create role |
| roleData := map[string]interface{}{ |
| "policies": "default", |
| "secret_id_ttl": tt.ttl, |
| } |
| |
| roleReq := &logical.Request{ |
| Operation: logical.CreateOperation, |
| Path: "role/" + tt.roleName, |
| Storage: storage, |
| Data: roleData, |
| } |
| resp := b.requestNoErr(t, roleReq) |
| |
| // Generate secret ID |
| secretIDReq := &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Path: "role/" + tt.roleName + "/secret-id", |
| Storage: storage, |
| } |
| resp = b.requestNoErr(t, secretIDReq) |
| |
| // Extract the "ttl" value from the response data if it exists |
| ttlRaw, okTTL := resp.Data["secret_id_ttl"] |
| if !okTTL { |
| t.Fatalf("expected TTL value in response") |
| } |
| |
| var ( |
| respTTL int64 |
| ok bool |
| ) |
| respTTL, ok = ttlRaw.(int64) |
| if !ok { |
| t.Fatalf("expected ttl to be an integer, got: %T", ttlRaw) |
| } |
| |
| // Verify secret ID response for different cases |
| switch { |
| case tt.sysTTLCap: |
| if respTTL != int64(b.System().MaxLeaseTTL().Seconds()) { |
| t.Fatalf("expected TTL value to be system's max lease TTL, got: %d", respTTL) |
| } |
| default: |
| if respTTL != tt.ttl { |
| t.Fatalf("expected TTL value to be %d, got: %d", tt.ttl, respTTL) |
| } |
| } |
| }) |
| } |
| } |
| |
| // TestAppRole_RoleSecretIDAccessorCrossDelete tests deleting a secret id via |
| // secret id accessor belonging to a different role |
| func TestAppRole_RoleSecretIDAccessorCrossDelete(t *testing.T) { |
| var resp *logical.Response |
| var err error |
| b, storage := createBackendWithStorage(t) |
| |
| // Create First Role |
| createRole(t, b, storage, "role1", "a,b") |
| _ = b.requestNoErr(t, &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id", |
| }) |
| |
| // Create Second Role |
| createRole(t, b, storage, "role2", "a,b") |
| _ = b.requestNoErr(t, &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/role2/secret-id", |
| }) |
| |
| // Get role2 secretID Accessor |
| resp = b.requestNoErr(t, &logical.Request{ |
| Operation: logical.ListOperation, |
| Storage: storage, |
| Path: "role/role2/secret-id", |
| }) |
| |
| // Read back role2 secretID Accessor information |
| hmacSecretID := resp.Data["keys"].([]string)[0] |
| _ = b.requestNoErr(t, &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/role2/secret-id-accessor/lookup", |
| Data: map[string]interface{}{ |
| "secret_id_accessor": hmacSecretID, |
| }, |
| }) |
| |
| // Attempt to destroy role2 secretID accessor using role1 path |
| _, err = b.HandleRequest(context.Background(), &logical.Request{ |
| Operation: logical.UpdateOperation, |
| Storage: storage, |
| Path: "role/role1/secret-id-accessor/destroy", |
| Data: map[string]interface{}{ |
| "secret_id_accessor": hmacSecretID, |
| }, |
| }) |
| |
| if err == nil { |
| t.Fatalf("expected error") |
| } |
| } |