blob: efed4676b1e437d88d281b1b39998f7ed491fd78 [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
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 or secret ID") {
t.Fatalf("Error was not due to invalid role ID. Error: %s", errString)
}
}