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>
361 lines
9.1 KiB
Go
361 lines
9.1 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package approle
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
)
|
|
|
|
func TestAppRole_BoundCIDRLogin(t *testing.T) {
|
|
var resp *logical.Response
|
|
var err error
|
|
b, s := createBackendWithStorage(t)
|
|
|
|
// Create a role with secret ID binding disabled and only bound cidr list
|
|
// enabled
|
|
resp = b.requestNoErr(t, &logical.Request{
|
|
Path: "role/testrole",
|
|
Operation: logical.CreateOperation,
|
|
Data: map[string]interface{}{
|
|
"bind_secret_id": false,
|
|
"bound_cidr_list": []string{"127.0.0.1/8"},
|
|
"token_bound_cidrs": []string{"10.0.0.0/8"},
|
|
},
|
|
Storage: s,
|
|
})
|
|
|
|
// Read the role ID
|
|
resp = b.requestNoErr(t, &logical.Request{
|
|
Path: "role/testrole/role-id",
|
|
Operation: logical.ReadOperation,
|
|
Storage: s,
|
|
})
|
|
|
|
roleID := resp.Data["role_id"]
|
|
|
|
// Fill in the connection information and login with just the role ID
|
|
resp = b.requestNoErr(t, &logical.Request{
|
|
Path: "login",
|
|
Operation: logical.UpdateOperation,
|
|
Data: map[string]interface{}{
|
|
"role_id": roleID,
|
|
},
|
|
Storage: s,
|
|
Connection: &logical.Connection{RemoteAddr: "127.0.0.1"},
|
|
})
|
|
|
|
if resp.Auth == nil {
|
|
t.Fatal("expected login to succeed")
|
|
}
|
|
if len(resp.Auth.BoundCIDRs) != 1 {
|
|
t.Fatal("bad token bound cidrs")
|
|
}
|
|
if resp.Auth.BoundCIDRs[0].String() != "10.0.0.0/8" {
|
|
t.Fatalf("bad: %s", resp.Auth.BoundCIDRs[0].String())
|
|
}
|
|
|
|
// Override with a secret-id value, verify it doesn't pass
|
|
resp = b.requestNoErr(t, &logical.Request{
|
|
Path: "role/testrole",
|
|
Operation: logical.UpdateOperation,
|
|
Data: map[string]interface{}{
|
|
"bind_secret_id": true,
|
|
},
|
|
Storage: s,
|
|
})
|
|
|
|
roleSecretIDReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "role/testrole/secret-id",
|
|
Storage: s,
|
|
Data: map[string]interface{}{
|
|
"token_bound_cidrs": []string{"11.0.0.0/24"},
|
|
},
|
|
}
|
|
resp, err = b.HandleRequest(context.Background(), roleSecretIDReq)
|
|
if err == nil {
|
|
t.Fatal("expected error due to mismatching subnet relationship")
|
|
}
|
|
|
|
roleSecretIDReq.Data["token_bound_cidrs"] = "10.0.0.0/24"
|
|
resp = b.requestNoErr(t, roleSecretIDReq)
|
|
|
|
secretID := resp.Data["secret_id"]
|
|
|
|
resp = b.requestNoErr(t, &logical.Request{
|
|
Path: "login",
|
|
Operation: logical.UpdateOperation,
|
|
Data: map[string]interface{}{
|
|
"role_id": roleID,
|
|
"secret_id": secretID,
|
|
},
|
|
Storage: s,
|
|
Connection: &logical.Connection{RemoteAddr: "127.0.0.1"},
|
|
})
|
|
|
|
if resp.Auth == nil {
|
|
t.Fatal("expected login to succeed")
|
|
}
|
|
if len(resp.Auth.BoundCIDRs) != 1 {
|
|
t.Fatal("bad token bound cidrs")
|
|
}
|
|
if resp.Auth.BoundCIDRs[0].String() != "10.0.0.0/24" {
|
|
t.Fatalf("bad: %s", resp.Auth.BoundCIDRs[0].String())
|
|
}
|
|
}
|
|
|
|
func TestAppRole_RoleLogin(t *testing.T) {
|
|
var resp *logical.Response
|
|
var err error
|
|
b, storage := createBackendWithStorage(t)
|
|
|
|
createRole(t, b, storage, "role1", "a,b,c")
|
|
roleRoleIDReq := &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "role/role1/role-id",
|
|
Storage: storage,
|
|
}
|
|
resp = b.requestNoErr(t, roleRoleIDReq)
|
|
|
|
roleID := resp.Data["role_id"]
|
|
|
|
roleSecretIDReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "role/role1/secret-id",
|
|
Storage: storage,
|
|
}
|
|
resp = b.requestNoErr(t, roleSecretIDReq)
|
|
|
|
secretID := resp.Data["secret_id"]
|
|
|
|
loginData := map[string]interface{}{
|
|
"role_id": roleID,
|
|
"secret_id": secretID,
|
|
}
|
|
loginReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "login",
|
|
Storage: storage,
|
|
Data: loginData,
|
|
Connection: &logical.Connection{
|
|
RemoteAddr: "127.0.0.1",
|
|
},
|
|
}
|
|
loginResp, err := b.HandleRequest(context.Background(), loginReq)
|
|
if err != nil || (loginResp != nil && loginResp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, loginResp)
|
|
}
|
|
|
|
if loginResp.Auth == nil {
|
|
t.Fatalf("expected a non-nil auth object in the response")
|
|
}
|
|
|
|
if loginResp.Auth.Metadata == nil {
|
|
t.Fatalf("expected a non-nil metadata object in the response")
|
|
}
|
|
|
|
if val := loginResp.Auth.Metadata["role_name"]; val != "role1" {
|
|
t.Fatalf("expected metadata.role_name to equal 'role1', got: %v", val)
|
|
}
|
|
|
|
if loginResp.Auth.Alias.Metadata == nil {
|
|
t.Fatalf("expected a non-nil alias metadata object in the response")
|
|
}
|
|
|
|
if val := loginResp.Auth.Alias.Metadata["role_name"]; val != "role1" {
|
|
t.Fatalf("expected metadata.alias.role_name to equal 'role1', got: %v", val)
|
|
}
|
|
|
|
// Test renewal
|
|
renewReq := generateRenewRequest(storage, loginResp.Auth)
|
|
|
|
resp, err = b.HandleRequest(context.Background(), renewReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
if resp.Auth.TTL != 400*time.Second {
|
|
t.Fatalf("expected period value from response to be 400s, got: %s", resp.Auth.TTL)
|
|
}
|
|
|
|
///
|
|
// Test renewal with period
|
|
///
|
|
|
|
// Create role
|
|
period := 600 * time.Second
|
|
roleData := map[string]interface{}{
|
|
"policies": "a,b,c",
|
|
"period": period.String(),
|
|
}
|
|
roleReq := &logical.Request{
|
|
Operation: logical.CreateOperation,
|
|
Path: "role/" + "role-period",
|
|
Storage: storage,
|
|
Data: roleData,
|
|
}
|
|
resp = b.requestNoErr(t, roleReq)
|
|
|
|
roleRoleIDReq = &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "role/role-period/role-id",
|
|
Storage: storage,
|
|
}
|
|
resp = b.requestNoErr(t, roleRoleIDReq)
|
|
|
|
roleID = resp.Data["role_id"]
|
|
|
|
roleSecretIDReq = &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "role/role-period/secret-id",
|
|
Storage: storage,
|
|
}
|
|
resp = b.requestNoErr(t, roleSecretIDReq)
|
|
|
|
secretID = resp.Data["secret_id"]
|
|
|
|
loginData["role_id"] = roleID
|
|
loginData["secret_id"] = secretID
|
|
|
|
loginResp, err = b.HandleRequest(context.Background(), loginReq)
|
|
if err != nil || (loginResp != nil && loginResp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, loginResp)
|
|
}
|
|
|
|
if loginResp.Auth == nil {
|
|
t.Fatalf("expected a non-nil auth object in the response")
|
|
}
|
|
|
|
renewReq = generateRenewRequest(storage, loginResp.Auth)
|
|
|
|
resp, err = b.HandleRequest(context.Background(), renewReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
if resp.Auth.Period != period {
|
|
t.Fatalf("expected period value of %d in the response, got: %s", period, resp.Auth.Period)
|
|
}
|
|
|
|
// Test input validation with secret_id that exceeds max length
|
|
loginData["secret_id"] = strings.Repeat("a", maxHmacInputLength+1)
|
|
|
|
loginReq = &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "login",
|
|
Storage: storage,
|
|
Data: loginData,
|
|
Connection: &logical.Connection{
|
|
RemoteAddr: "127.0.0.1",
|
|
},
|
|
}
|
|
|
|
loginResp, err = b.HandleRequest(context.Background(), loginReq)
|
|
|
|
expectedErr := "failed to create HMAC of secret_id"
|
|
if loginResp != nil || err == nil || !strings.Contains(err.Error(), expectedErr) {
|
|
t.Fatalf("expected login test to fail with error %q, resp: %#v, err: %v", expectedErr, loginResp, err)
|
|
}
|
|
}
|
|
|
|
func generateRenewRequest(s logical.Storage, auth *logical.Auth) *logical.Request {
|
|
renewReq := &logical.Request{
|
|
Operation: logical.RenewOperation,
|
|
Storage: s,
|
|
Auth: &logical.Auth{},
|
|
}
|
|
renewReq.Auth.InternalData = auth.InternalData
|
|
renewReq.Auth.Metadata = auth.Metadata
|
|
renewReq.Auth.LeaseOptions = auth.LeaseOptions
|
|
renewReq.Auth.Policies = auth.Policies
|
|
renewReq.Auth.Period = auth.Period
|
|
|
|
return renewReq
|
|
}
|
|
|
|
func TestAppRole_RoleResolve(t *testing.T) {
|
|
b, storage := createBackendWithStorage(t)
|
|
|
|
role := "role1"
|
|
createRole(t, b, storage, role, "a,b,c")
|
|
roleRoleIDReq := &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "role/role1/role-id",
|
|
Storage: storage,
|
|
}
|
|
resp := b.requestNoErr(t, roleRoleIDReq)
|
|
|
|
roleID := resp.Data["role_id"]
|
|
|
|
roleSecretIDReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "role/role1/secret-id",
|
|
Storage: storage,
|
|
}
|
|
resp = b.requestNoErr(t, roleSecretIDReq)
|
|
|
|
secretID := resp.Data["secret_id"]
|
|
|
|
loginData := map[string]interface{}{
|
|
"role_id": roleID,
|
|
"secret_id": secretID,
|
|
}
|
|
loginReq := &logical.Request{
|
|
Operation: logical.ResolveRoleOperation,
|
|
Path: "login",
|
|
Storage: storage,
|
|
Data: loginData,
|
|
Connection: &logical.Connection{
|
|
RemoteAddr: "127.0.0.1",
|
|
},
|
|
}
|
|
|
|
resp = b.requestNoErr(t, loginReq)
|
|
|
|
if resp.Data["role"] != role {
|
|
t.Fatalf("Role was not as expected. Expected %s, received %s", role, resp.Data["role"])
|
|
}
|
|
}
|
|
|
|
func TestAppRole_RoleDoesNotExist(t *testing.T) {
|
|
var resp *logical.Response
|
|
var err error
|
|
b, storage := createBackendWithStorage(t)
|
|
|
|
roleID := "roleDoesNotExist"
|
|
|
|
loginData := map[string]interface{}{
|
|
"role_id": roleID,
|
|
"secret_id": "secret",
|
|
}
|
|
loginReq := &logical.Request{
|
|
Operation: logical.ResolveRoleOperation,
|
|
Path: "login",
|
|
Storage: storage,
|
|
Data: loginData,
|
|
Connection: &logical.Connection{
|
|
RemoteAddr: "127.0.0.1",
|
|
},
|
|
}
|
|
|
|
resp, err = b.HandleRequest(context.Background(), loginReq)
|
|
if resp == nil && !resp.IsError() {
|
|
t.Fatalf("Response was not an error: err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
errString, ok := resp.Data["error"].(string)
|
|
if !ok {
|
|
t.Fatal("Error not part of response.")
|
|
}
|
|
|
|
if !strings.Contains(errString, "invalid role ID") {
|
|
t.Fatalf("Error was not due to invalid role ID. Error: %s", errString)
|
|
}
|
|
}
|