27bb03bbc0
* adding copyright header * fix fmt and a test
2135 lines
58 KiB
Go
2135 lines
58 KiB
Go
// 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")
|
|
}
|
|
}
|