mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 17:52:32 +00:00
* 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>
2135 lines
58 KiB
Go
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")
|
|
}
|
|
}
|