blob: a5ea9d1d51599891fcc936cd364f2a0b949123d6 [file] [log] [blame]
// 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")
}
}