Files
vault/builtin/credential/approle/path_role_test.go
hashicorp-copywrite[bot] 0b12cdcfd1 [COMPLIANCE] License changes (#22290)
* Adding explicit MPL license for sub-package.

This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository.

* Adding explicit MPL license for sub-package.

This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository.

* Updating the license from MPL to Business Source License.

Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at https://hashi.co/bsl-blog, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl.

* add missing license headers

* Update copyright file headers to BUS-1.1

* Fix test that expected exact offset on hcl file

---------

Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
Co-authored-by: Sarah Thompson <sthompson@hashicorp.com>
Co-authored-by: Brian Kassouf <bkassouf@hashicorp.com>
2023-08-10 18:14:03 -07:00

2135 lines
58 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
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")
}
}