open-vault/builtin/credential/aws/backend_test.go
Joel Thompson e06a78a474 Create unified aws auth backend (#2441)
* Rename builtin/credential/aws-ec2 to aws

The aws-ec2 authentication backend is being expanded and will become the
generic aws backend. This is a small rename commit to keep the commit
history clean.

* Expand aws-ec2 backend to more generic aws

This adds the ability to authenticate arbitrary AWS IAM principals using
AWS's sts:GetCallerIdentity method. The AWS-EC2 auth backend is being to
just AWS with the expansion.

* Add missing aws auth handler to CLI

This was omitted from the previous commit

* aws auth backend general variable name cleanup

Also fixed a bug where allowed auth types weren't being checked upon
login, and added tests for it.

* Update docs for the aws auth backend

* Refactor aws bind validation

* Fix env var override in aws backend test

Intent is to override the AWS environment variables with the TEST_*
versions if they are set, but the reverse was happening.

* Update docs on use of IAM authentication profile

AWS now allows you to change the instance profile of a running instance,
so the use case of "a long-lived instance that's not in an instance
profile" no longer means you have to use the the EC2 auth method. You
can now just change the instance profile on the fly.

* Fix typo in aws auth cli help

* Respond to PR feedback

* More PR feedback

* Respond to additional PR feedback

* Address more feedback on aws auth PR

* Make aws auth_type immutable per role

* Address more aws auth PR feedback

* Address more iam auth PR feedback

* Rename aws-ec2.html.md to aws.html.md

Per PR feedback, to go along with new backend name.

* Add MountType to logical.Request

* Make default aws auth_type dependent upon MountType

When MountType is aws-ec2, default to ec2 auth_type for backwards
compatibility with legacy roles. Otherwise, default to iam.

* Pass MountPoint and MountType back up to the core

Previously the request router reset the MountPoint and MountType back to
the empty string before returning to the core. This ensures they get set
back to the correct values.
2017-04-24 15:15:50 -04:00

1505 lines
41 KiB
Go

package awsauth
import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"testing"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/hashicorp/vault/helper/policyutil"
"github.com/hashicorp/vault/logical"
logicaltest "github.com/hashicorp/vault/logical/testing"
)
func TestBackend_CreateParseVerifyRoleTag(t *testing.T) {
// create a backend
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
// create a role entry
data := map[string]interface{}{
"auth_type": "ec2",
"policies": "p,q,r,s",
"bound_ami_id": "abcd-123",
}
resp, err := b.HandleRequest(&logical.Request{
Operation: logical.CreateOperation,
Path: "role/abcd-123",
Storage: storage,
Data: data,
})
if resp != nil && resp.IsError() {
t.Fatalf("failed to create role")
}
if err != nil {
t.Fatal(err)
}
// read the created role entry
roleEntry, err := b.lockedAWSRole(storage, "abcd-123")
if err != nil {
t.Fatal(err)
}
// create a nonce for the role tag
nonce, err := createRoleTagNonce()
if err != nil {
t.Fatal(err)
}
rTag1 := &roleTag{
Version: "v1",
Role: "abcd-123",
Nonce: nonce,
Policies: []string{"p", "q", "r"},
MaxTTL: 200000000000, // 200s
}
// create a role tag against the role entry
val, err := createRoleTagValue(rTag1, roleEntry)
if err != nil {
t.Fatal(err)
}
if val == "" {
t.Fatalf("failed to create role tag")
}
// parse the created role tag
rTag2, err := b.parseAndVerifyRoleTagValue(storage, val)
if err != nil {
t.Fatal(err)
}
// check the values in parsed role tag
if rTag2.Version != "v1" ||
rTag2.Nonce != nonce ||
rTag2.Role != "abcd-123" ||
rTag2.MaxTTL != 200000000000 || // 200s
!policyutil.EquivalentPolicies(rTag2.Policies, []string{"p", "q", "r"}) ||
len(rTag2.HMAC) == 0 {
t.Fatalf("parsed role tag is invalid")
}
// verify the tag contents using role specific HMAC key
verified, err := verifyRoleTagValue(rTag2, roleEntry)
if err != nil {
t.Fatal(err)
}
if !verified {
t.Fatalf("failed to verify the role tag")
}
// register a different role
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.CreateOperation,
Path: "role/ami-6789",
Storage: storage,
Data: data,
})
if resp != nil && resp.IsError() {
t.Fatalf("failed to create role")
}
if err != nil {
t.Fatal(err)
}
// get the entry of the newly created role entry
roleEntry2, err := b.lockedAWSRole(storage, "ami-6789")
if err != nil {
t.Fatal(err)
}
// try to verify the tag created with previous role's HMAC key
// with the newly registered entry's HMAC key
verified, err = verifyRoleTagValue(rTag2, roleEntry2)
if err != nil {
t.Fatal(err)
}
if verified {
t.Fatalf("verification of role tag should have failed")
}
// modify any value in role tag and try to verify it
rTag2.Version = "v2"
verified, err = verifyRoleTagValue(rTag2, roleEntry)
if err != nil {
t.Fatal(err)
}
if verified {
t.Fatalf("verification of role tag should have failed: invalid Version")
}
}
func TestBackend_prepareRoleTagPlaintextValue(t *testing.T) {
// create a nonce for the role tag
nonce, err := createRoleTagNonce()
if err != nil {
t.Fatal(err)
}
rTag := &roleTag{
Version: "v1",
Nonce: nonce,
Role: "abcd-123",
}
rTag.Version = ""
// try to create plaintext part of role tag
// without specifying version
val, err := prepareRoleTagPlaintextValue(rTag)
if err == nil {
t.Fatalf("expected error for missing version")
}
rTag.Version = "v1"
rTag.Nonce = ""
// try to create plaintext part of role tag
// without specifying nonce
val, err = prepareRoleTagPlaintextValue(rTag)
if err == nil {
t.Fatalf("expected error for missing nonce")
}
rTag.Nonce = nonce
rTag.Role = ""
// try to create plaintext part of role tag
// without specifying role
val, err = prepareRoleTagPlaintextValue(rTag)
if err == nil {
t.Fatalf("expected error for missing role")
}
rTag.Role = "abcd-123"
// create the plaintext part of the tag
val, err = prepareRoleTagPlaintextValue(rTag)
if err != nil {
t.Fatal(err)
}
// verify if it contains known fields
if !strings.Contains(val, "r=") ||
!strings.Contains(val, "d=") ||
!strings.Contains(val, "m=") ||
!strings.HasPrefix(val, "v1") {
t.Fatalf("incorrect information in role tag plaintext value")
}
rTag.InstanceID = "instance-123"
// create the role tag with instance_id specified
val, err = prepareRoleTagPlaintextValue(rTag)
if err != nil {
t.Fatal(err)
}
// verify it
if !strings.Contains(val, "i=") {
t.Fatalf("missing instance ID in role tag plaintext value")
}
rTag.MaxTTL = 200000000000
// create the role tag with max_ttl specified
val, err = prepareRoleTagPlaintextValue(rTag)
if err != nil {
t.Fatal(err)
}
// verify it
if !strings.Contains(val, "t=") {
t.Fatalf("missing max_ttl field in role tag plaintext value")
}
}
func TestBackend_CreateRoleTagNonce(t *testing.T) {
// create a nonce for the role tag
nonce, err := createRoleTagNonce()
if err != nil {
t.Fatal(err)
}
if nonce == "" {
t.Fatalf("failed to create role tag nonce")
}
// verify that the value returned is base64 encoded
nonceBytes, err := base64.StdEncoding.DecodeString(nonce)
if err != nil {
t.Fatal(err)
}
if len(nonceBytes) == 0 {
t.Fatalf("length of role tag nonce is zero")
}
}
func TestBackend_ConfigTidyIdentities(t *testing.T) {
// create a backend
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
// test update operation
tidyRequest := &logical.Request{
Operation: logical.UpdateOperation,
Path: "config/tidy/identity-whitelist",
Storage: storage,
}
data := map[string]interface{}{
"safety_buffer": "60",
"disable_periodic_tidy": true,
}
tidyRequest.Data = data
_, err = b.HandleRequest(tidyRequest)
if err != nil {
t.Fatal(err)
}
// test read operation
tidyRequest.Operation = logical.ReadOperation
resp, err := b.HandleRequest(tidyRequest)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.IsError() {
t.Fatalf("failed to read config/tidy/identity-whitelist endpoint")
}
if resp.Data["safety_buffer"].(int) != 60 || !resp.Data["disable_periodic_tidy"].(bool) {
t.Fatalf("bad: expected: safety_buffer:60 disable_periodic_tidy:true actual: safety_buffer:%s disable_periodic_tidy:%t\n", resp.Data["safety_buffer"].(int), resp.Data["disable_periodic_tidy"].(bool))
}
// test delete operation
tidyRequest.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(tidyRequest)
if err != nil {
t.Fatal(err)
}
if resp != nil {
t.Fatalf("failed to delete config/tidy/identity-whitelist")
}
}
func TestBackend_ConfigTidyRoleTags(t *testing.T) {
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
// test update operation
tidyRequest := &logical.Request{
Operation: logical.UpdateOperation,
Path: "config/tidy/roletag-blacklist",
Storage: storage,
}
data := map[string]interface{}{
"safety_buffer": "60",
"disable_periodic_tidy": true,
}
tidyRequest.Data = data
_, err = b.HandleRequest(tidyRequest)
if err != nil {
t.Fatal(err)
}
// test read operation
tidyRequest.Operation = logical.ReadOperation
resp, err := b.HandleRequest(tidyRequest)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.IsError() {
t.Fatalf("failed to read config/tidy/roletag-blacklist endpoint")
}
if resp.Data["safety_buffer"].(int) != 60 || !resp.Data["disable_periodic_tidy"].(bool) {
t.Fatalf("bad: expected: safety_buffer:60 disable_periodic_tidy:true actual: safety_buffer:%s disable_periodic_tidy:%t\n", resp.Data["safety_buffer"].(int), resp.Data["disable_periodic_tidy"].(bool))
}
// test delete operation
tidyRequest.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(tidyRequest)
if err != nil {
t.Fatal(err)
}
if resp != nil {
t.Fatalf("failed to delete config/tidy/roletag-blacklist")
}
}
func TestBackend_TidyIdentities(t *testing.T) {
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
// test update operation
_, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "tidy/identity-whitelist",
Storage: storage,
})
if err != nil {
t.Fatal(err)
}
}
func TestBackend_TidyRoleTags(t *testing.T) {
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
// test update operation
_, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "tidy/roletag-blacklist",
Storage: storage,
})
if err != nil {
t.Fatal(err)
}
}
func TestBackend_ConfigClient(t *testing.T) {
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
data := map[string]interface{}{"access_key": "AKIAJBRHKV6EVTTNXDHA",
"secret_key": "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",
}
stepCreate := logicaltest.TestStep{
Operation: logical.CreateOperation,
Path: "config/client",
Data: data,
}
stepUpdate := logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "config/client",
Data: data,
}
data3 := map[string]interface{}{"access_key": "",
"secret_key": "mCtSM8ZUEQ3mOFVZYPBQkf2sO6F/W7a5TVzrl3Oj",
}
stepInvalidAccessKey := logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "config/client",
Data: data3,
ErrorOk: true,
}
data4 := map[string]interface{}{"access_key": "accesskey",
"secret_key": "",
}
stepInvalidSecretKey := logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "config/client",
Data: data4,
ErrorOk: true,
}
logicaltest.Test(t, logicaltest.TestCase{
AcceptanceTest: false,
Backend: b,
Steps: []logicaltest.TestStep{
stepCreate,
stepInvalidAccessKey,
stepInvalidSecretKey,
stepUpdate,
},
})
// test existence check returning false
checkFound, exists, err := b.HandleExistenceCheck(&logical.Request{
Operation: logical.CreateOperation,
Path: "config/client",
Storage: storage,
})
if err != nil {
t.Fatal(err)
}
if !checkFound {
t.Fatal("existence check not found for path 'config/client'")
}
if exists {
t.Fatal("existence check should have returned 'false' for 'config/client'")
}
// create an entry
configClientCreateRequest := &logical.Request{
Operation: logical.UpdateOperation,
Path: "config/client",
Data: data,
Storage: storage,
}
_, err = b.HandleRequest(configClientCreateRequest)
if err != nil {
t.Fatal(err)
}
//test existence check returning true
checkFound, exists, err = b.HandleExistenceCheck(&logical.Request{
Operation: logical.CreateOperation,
Path: "config/client",
Storage: storage,
})
if err != nil {
t.Fatal(err)
}
if !checkFound {
t.Fatal("existence check not found for path 'config/client'")
}
if !exists {
t.Fatal("existence check should have returned 'true' for 'config/client'")
}
endpointData := map[string]interface{}{
"secret_key": "secretkey",
"access_key": "accesskey",
"endpoint": "endpointvalue",
}
endpointReq := &logical.Request{
Operation: logical.UpdateOperation,
Path: "config/client",
Storage: storage,
Data: endpointData,
}
_, err = b.HandleRequest(endpointReq)
if err != nil {
t.Fatal(err)
}
endpointReq.Operation = logical.ReadOperation
resp, err := b.HandleRequest(endpointReq)
if err != nil {
t.Fatal(err)
}
if resp == nil ||
resp.IsError() {
t.Fatalf("")
}
actual := resp.Data["endpoint"].(string)
if actual != "endpointvalue" {
t.Fatalf("bad: endpoint: expected:endpointvalue actual:%s\n", actual)
}
}
func TestBackend_pathConfigCertificate(t *testing.T) {
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
certReq := &logical.Request{
Operation: logical.CreateOperation,
Storage: storage,
Path: "config/certificate/cert1",
}
checkFound, exists, err := b.HandleExistenceCheck(certReq)
if err != nil {
t.Fatal(err)
}
if !checkFound {
t.Fatal("existence check not found for path 'config/certificate/cert1'")
}
if exists {
t.Fatal("existence check should have returned 'false' for 'config/certificate/cert1'")
}
data := map[string]interface{}{
"type": "pkcs7",
"aws_public_cert": `LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM3VENDQXEwQ0NRQ1d1a2paNVY0YVp6QUpC
Z2NxaGtqT09BUURNRnd4Q3pBSkJnTlZCQVlUQWxWVE1Sa3cKRndZRFZRUUlFeEJYWVhOb2FXNW5k
Rzl1SUZOMFlYUmxNUkF3RGdZRFZRUUhFd2RUWldGMGRHeGxNU0F3SGdZRApWUVFLRXhkQmJXRjZi
MjRnVjJWaUlGTmxjblpwWTJWeklFeE1RekFlRncweE1qQXhNRFV4TWpVMk1USmFGdzB6Ck9EQXhN
RFV4TWpVMk1USmFNRnd4Q3pBSkJnTlZCQVlUQWxWVE1Sa3dGd1lEVlFRSUV4QlhZWE5vYVc1bmRH
OXUKSUZOMFlYUmxNUkF3RGdZRFZRUUhFd2RUWldGMGRHeGxNU0F3SGdZRFZRUUtFeGRCYldGNmIy
NGdWMlZpSUZObApjblpwWTJWeklFeE1RekNDQWJjd2dnRXNCZ2NxaGtqT09BUUJNSUlCSHdLQmdR
Q2prdmNTMmJiMVZRNHl0LzVlCmloNU9PNmtLL24xTHpsbHI3RDhad3RRUDhmT0VwcDVFMm5nK0Q2
VWQxWjFnWWlwcjU4S2ozbnNzU05wSTZiWDMKVnlJUXpLN3dMY2xuZC9Zb3pxTk5tZ0l5WmVjTjdF
Z2xLOUlUSEpMUCt4OEZ0VXB0M1FieVlYSmRtVk1lZ042UApodmlZdDVKSC9uWWw0aGgzUGExSEpk
c2tnUUlWQUxWSjNFUjExK0tvNHRQNm53dkh3aDYrRVJZUkFvR0JBSTFqCmsrdGtxTVZIdUFGY3ZB
R0tvY1Rnc2pKZW02LzVxb216SnVLRG1iSk51OVF4dzNyQW90WGF1OFFlK01CY0psL1UKaGh5MUtI
VnBDR2w5ZnVlUTJzNklMMENhTy9idXljVTFDaVlRazQwS05IQ2NIZk5pWmJkbHgxRTlycFVwN2Ju
RgpsUmEydjFudE1YM2NhUlZEZGJ0UEVXbWR4U0NZc1lGRGs0bVpyT0xCQTRHRUFBS0JnRWJtZXZl
NWY4TElFL0dmCk1ObVA5Q001ZW92UU9HeDVobzhXcUQrYVRlYnMrazJ0bjkyQkJQcWVacXBXUmE1
UC8ranJkS21sMXF4NGxsSFcKTVhyczNJZ0liNitoVUlCK1M4ZHo4L21tTzBicHI3NlJvWlZDWFlh
YjJDWmVkRnV0N3FjM1dVSDkrRVVBSDVtdwp2U2VEQ09VTVlRUjdSOUxJTll3b3VISXppcVFZTUFr
R0J5cUdTTTQ0QkFNREx3QXdMQUlVV1hCbGs0MHhUd1N3CjdIWDMyTXhYWXJ1c2U5QUNGQk5HbWRY
MlpCclZOR3JOOU4yZjZST2swazlLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
`,
}
certReq.Data = data
// test create operation
resp, err := b.HandleRequest(certReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("resp: %#v, err: %v", resp, err)
}
certReq.Data = nil
// test existence check
checkFound, exists, err = b.HandleExistenceCheck(certReq)
if err != nil {
t.Fatal(err)
}
if !checkFound {
t.Fatal("existence check not found for path 'config/certificate/cert1'")
}
if !exists {
t.Fatal("existence check should have returned 'true' for 'config/certificate/cert1'")
}
certReq.Operation = logical.ReadOperation
// test read operation
resp, err = b.HandleRequest(certReq)
expectedCert := `-----BEGIN CERTIFICATE-----
MIIC7TCCAq0CCQCWukjZ5V4aZzAJBgcqhkjOOAQDMFwxCzAJBgNVBAYTAlVTMRkw
FwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYD
VQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0xMjAxMDUxMjU2MTJaFw0z
ODAxMDUxMjU2MTJaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9u
IFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNl
cnZpY2VzIExMQzCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQCjkvcS2bb1VQ4yt/5e
ih5OO6kK/n1Lzllr7D8ZwtQP8fOEpp5E2ng+D6Ud1Z1gYipr58Kj3nssSNpI6bX3
VyIQzK7wLclnd/YozqNNmgIyZecN7EglK9ITHJLP+x8FtUpt3QbyYXJdmVMegN6P
hviYt5JH/nYl4hh3Pa1HJdskgQIVALVJ3ER11+Ko4tP6nwvHwh6+ERYRAoGBAI1j
k+tkqMVHuAFcvAGKocTgsjJem6/5qomzJuKDmbJNu9Qxw3rAotXau8Qe+MBcJl/U
hhy1KHVpCGl9fueQ2s6IL0CaO/buycU1CiYQk40KNHCcHfNiZbdlx1E9rpUp7bnF
lRa2v1ntMX3caRVDdbtPEWmdxSCYsYFDk4mZrOLBA4GEAAKBgEbmeve5f8LIE/Gf
MNmP9CM5eovQOGx5ho8WqD+aTebs+k2tn92BBPqeZqpWRa5P/+jrdKml1qx4llHW
MXrs3IgIb6+hUIB+S8dz8/mmO0bpr76RoZVCXYab2CZedFut7qc3WUH9+EUAH5mw
vSeDCOUMYQR7R9LINYwouHIziqQYMAkGByqGSM44BAMDLwAwLAIUWXBlk40xTwSw
7HX32MxXYruse9ACFBNGmdX2ZBrVNGrN9N2f6ROk0k9K
-----END CERTIFICATE-----
`
if resp.Data["aws_public_cert"].(string) != expectedCert {
t.Fatalf("bad: expected:%s\n got:%s\n", expectedCert, resp.Data["aws_public_cert"].(string))
}
certReq.Operation = logical.CreateOperation
certReq.Path = "config/certificate/cert2"
certReq.Data = data
// create another entry to test the list operation
_, err = b.HandleRequest(certReq)
if err != nil {
t.Fatal(err)
}
certReq.Operation = logical.ListOperation
certReq.Path = "config/certificates"
// test list operation
resp, err = b.HandleRequest(certReq)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.IsError() {
t.Fatalf("failed to list config/certificates")
}
keys := resp.Data["keys"].([]string)
if len(keys) != 2 {
t.Fatalf("invalid keys listed: %#v\n", keys)
}
certReq.Operation = logical.DeleteOperation
certReq.Path = "config/certificate/cert1"
_, err = b.HandleRequest(certReq)
if err != nil {
t.Fatal(err)
}
certReq.Path = "config/certificate/cert2"
_, err = b.HandleRequest(certReq)
if err != nil {
t.Fatal(err)
}
certReq.Operation = logical.ListOperation
certReq.Path = "config/certificates"
// test list operation
resp, err = b.HandleRequest(certReq)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.IsError() {
t.Fatalf("failed to list config/certificates")
}
if resp.Data["keys"] != nil {
t.Fatalf("no entries should be present")
}
}
func TestBackend_parseAndVerifyRoleTagValue(t *testing.T) {
// create a backend
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
// create a role
data := map[string]interface{}{
"auth_type": "ec2",
"policies": "p,q,r,s",
"max_ttl": "120s",
"role_tag": "VaultRole",
"bound_ami_id": "abcd-123",
}
resp, err := b.HandleRequest(&logical.Request{
Operation: logical.CreateOperation,
Path: "role/abcd-123",
Storage: storage,
Data: data,
})
if resp != nil && resp.IsError() {
t.Fatalf("failed to create role")
}
if err != nil {
t.Fatal(err)
}
// verify that the entry is created
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.ReadOperation,
Path: "role/abcd-123",
Storage: storage,
})
if resp == nil {
t.Fatalf("expected an role entry for abcd-123")
}
// create a role tag
data2 := map[string]interface{}{
"policies": "p,q,r,s",
}
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "role/abcd-123/tag",
Storage: storage,
Data: data2,
})
if err != nil {
t.Fatal(err)
}
if resp.Data["tag_key"].(string) == "" ||
resp.Data["tag_value"].(string) == "" {
t.Fatalf("invalid tag response: %#v\n", resp)
}
tagValue := resp.Data["tag_value"].(string)
// parse the value and check if the verifiable values match
rTag, err := b.parseAndVerifyRoleTagValue(storage, tagValue)
if err != nil {
t.Fatalf("err: %s", err)
}
if rTag == nil {
t.Fatalf("failed to parse role tag")
}
if rTag.Version != "v1" ||
!policyutil.EquivalentPolicies(rTag.Policies, []string{"p", "q", "r", "s"}) ||
rTag.Role != "abcd-123" {
t.Fatalf("bad: parsed role tag contains incorrect values. Got: %#v\n", rTag)
}
}
func TestBackend_PathRoleTag(t *testing.T) {
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
data := map[string]interface{}{
"auth_type": "ec2",
"policies": "p,q,r,s",
"max_ttl": "120s",
"role_tag": "VaultRole",
"bound_ami_id": "abcd-123",
}
resp, err := b.HandleRequest(&logical.Request{
Operation: logical.CreateOperation,
Path: "role/abcd-123",
Storage: storage,
Data: data,
})
if resp != nil && resp.IsError() {
t.Fatalf("failed to create role")
}
if err != nil {
t.Fatal(err)
}
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.ReadOperation,
Path: "role/abcd-123",
Storage: storage,
})
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatalf("failed to find a role entry for abcd-123")
}
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "role/abcd-123/tag",
Storage: storage,
})
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.Data == nil {
t.Fatalf("failed to create a tag on role: abcd-123")
}
if resp.IsError() {
t.Fatalf("failed to create a tag on role: abcd-123: %s\n", resp.Data["error"])
}
if resp.Data["tag_value"].(string) == "" {
t.Fatalf("role tag not present in the response data: %#v\n", resp.Data)
}
}
func TestBackend_PathBlacklistRoleTag(t *testing.T) {
// create the backend
storage := &logical.InmemStorage{}
config := logical.TestBackendConfig()
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
// create an role entry
data := map[string]interface{}{
"auth_type": "ec2",
"policies": "p,q,r,s",
"role_tag": "VaultRole",
"bound_ami_id": "abcd-123",
}
resp, err := b.HandleRequest(&logical.Request{
Operation: logical.CreateOperation,
Path: "role/abcd-123",
Storage: storage,
Data: data,
})
if resp != nil && resp.IsError() {
t.Fatalf("failed to create role")
}
if err != nil {
t.Fatal(err)
}
// create a role tag against an role registered before
data2 := map[string]interface{}{
"policies": "p,q,r,s",
}
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "role/abcd-123/tag",
Storage: storage,
Data: data2,
})
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.Data == nil {
t.Fatalf("failed to create a tag on role: abcd-123")
}
if resp.IsError() {
t.Fatalf("failed to create a tag on role: abcd-123: %s\n", resp.Data["error"])
}
tag := resp.Data["tag_value"].(string)
if tag == "" {
t.Fatalf("role tag not present in the response data: %#v\n", resp.Data)
}
// blacklist that role tag
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "roletag-blacklist/" + tag,
Storage: storage,
})
if err != nil {
t.Fatal(err)
}
if resp != nil {
t.Fatalf("failed to blacklist the roletag: %s\n", tag)
}
// read the blacklist entry
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.ReadOperation,
Path: "roletag-blacklist/" + tag,
Storage: storage,
})
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.Data == nil {
t.Fatalf("failed to read the blacklisted role tag: %s\n", tag)
}
if resp.IsError() {
t.Fatalf("failed to read the blacklisted role tag:%s. Err: %s\n", tag, resp.Data["error"])
}
// delete the blacklisted entry
_, err = b.HandleRequest(&logical.Request{
Operation: logical.DeleteOperation,
Path: "roletag-blacklist/" + tag,
Storage: storage,
})
if err != nil {
t.Fatal(err)
}
// try to read the deleted entry
tagEntry, err := b.lockedBlacklistRoleTagEntry(storage, tag)
if err != nil {
t.Fatal(err)
}
if tagEntry != nil {
t.Fatalf("role tag should not have been present: %s\n", tag)
}
}
// This is an acceptance test.
// Requires the following env vars:
// TEST_AWS_EC2_PKCS7
// TEST_AWS_EC2_AMI_ID
// TEST_AWS_EC2_ACCOUNT_ID
// TEST_AWS_EC2_IAM_ROLE_ARN
//
// If the test is not being run on an EC2 instance that has access to
// credentials using EC2RoleProvider, on top of the above vars, following
// needs to be set:
// TEST_AWS_SECRET_KEY
// TEST_AWS_ACCESS_KEY
func TestBackendAcc_LoginWithInstanceIdentityDocAndWhitelistIdentity(t *testing.T) {
// This test case should be run only when certain env vars are set and
// executed as an acceptance test.
if os.Getenv(logicaltest.TestEnvVar) == "" {
t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", logicaltest.TestEnvVar))
return
}
pkcs7 := os.Getenv("TEST_AWS_EC2_PKCS7")
if pkcs7 == "" {
t.Fatalf("env var TEST_AWS_EC2_PKCS7 not set")
}
amiID := os.Getenv("TEST_AWS_EC2_AMI_ID")
if amiID == "" {
t.Fatalf("env var TEST_AWS_EC2_AMI_ID not set")
}
iamARN := os.Getenv("TEST_AWS_EC2_IAM_ROLE_ARN")
if iamARN == "" {
t.Fatalf("env var TEST_AWS_EC2_IAM_ROLE_ARN not set")
}
accountID := os.Getenv("TEST_AWS_EC2_ACCOUNT_ID")
if accountID == "" {
t.Fatalf("env var TEST_AWS_EC2_ACCOUNT_ID not set")
}
roleName := amiID
// create the backend
storage := &logical.InmemStorage{}
config := logical.TestBackendConfig()
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
accessKey := os.Getenv("TEST_AWS_ACCESS_KEY")
secretKey := os.Getenv("TEST_AWS_SECRET_KEY")
// In case of problems with making API calls using the credentials (2FA enabled,
// for instance), the keys need not be set if the test is running on an EC2
// instance with permissions to get the credentials using EC2RoleProvider.
if accessKey != "" && secretKey != "" {
// get the API credentials from env vars
clientConfig := map[string]interface{}{
"access_key": accessKey,
"secret_key": secretKey,
}
if clientConfig["access_key"] == "" ||
clientConfig["secret_key"] == "" {
t.Fatalf("credentials not configured")
}
// store the credentials
_, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Storage: storage,
Path: "config/client",
Data: clientConfig,
})
if err != nil {
t.Fatal(err)
}
}
loginInput := map[string]interface{}{
"pkcs7": pkcs7,
"nonce": "vault-client-nonce",
}
// Perform the login operation with a AMI ID that is not matching
// the bound on the role.
loginRequest := &logical.Request{
Operation: logical.UpdateOperation,
Path: "login",
Storage: storage,
Data: loginInput,
}
// Place the wrong AMI ID in the role data.
data := map[string]interface{}{
"auth_type": "ec2",
"policies": "root",
"max_ttl": "120s",
"bound_ami_id": "wrong_ami_id",
"bound_account_id": accountID,
"bound_iam_role_arn": iamARN,
}
roleReq := &logical.Request{
Operation: logical.CreateOperation,
Path: "role/" + roleName,
Storage: storage,
Data: data,
}
// Save the role with wrong AMI ID
resp, err := b.HandleRequest(roleReq)
if err != nil && (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr:%v", resp, err)
}
// Expect failure when tried to login with wrong AMI ID
resp, err = b.HandleRequest(loginRequest)
if err != nil || resp == nil || (resp != nil && !resp.IsError()) {
t.Fatalf("bad: expected error response: resp:%#v\nerr:%v", resp, err)
}
// Place the correct AMI ID, but make the AccountID wrong
roleReq.Operation = logical.UpdateOperation
data["bound_ami_id"] = amiID
data["bound_account_id"] = "wrong-account-id"
resp, err = b.HandleRequest(roleReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: failed to create role: resp:%#v\nerr:%v", resp, err)
}
// Expect failure when tried to login with incorrect AccountID
resp, err = b.HandleRequest(loginRequest)
if err != nil || resp == nil || (resp != nil && !resp.IsError()) {
t.Fatalf("bad: expected error response: resp:%#v\nerr:%v", resp, err)
}
// Place the correct AccountID, but make the wrong IAMRoleARN
data["bound_account_id"] = accountID
data["bound_iam_role_arn"] = "wrong_iam_role_arn"
resp, err = b.HandleRequest(roleReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: failed to create role: resp:%#v\nerr:%v", resp, err)
}
// Attempt to login and expect a fail because IAM Role ARN is wrong
resp, err = b.HandleRequest(loginRequest)
if err != nil || resp == nil || (resp != nil && !resp.IsError()) {
t.Fatalf("bad: expected error response: resp:%#v\nerr:%v", resp, err)
}
// place the correct IAM role ARN
data["bound_iam_role_arn"] = iamARN
resp, err = b.HandleRequest(roleReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: failed to create role: resp:%#v\nerr:%v", resp, err)
}
// Now, the login attempt should succeed
resp, err = b.HandleRequest(loginRequest)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.Auth == nil || resp.IsError() {
t.Fatalf("bad: failed to login: resp:%#v\nerr:%v", resp, err)
}
// verify the presence of instance_id in the response object.
instanceID := resp.Auth.Metadata["instance_id"]
if instanceID == "" {
t.Fatalf("instance ID not present in the response object")
}
loginInput["nonce"] = "changed-vault-client-nonce"
// try to login again with changed nonce
resp, err = b.HandleRequest(loginRequest)
if err != nil {
t.Fatal(err)
}
if resp == nil || !resp.IsError() {
t.Fatalf("login attempt should have failed due to client nonce mismatch")
}
// Check if a whitelist identity entry is created after the login.
wlRequest := &logical.Request{
Operation: logical.ReadOperation,
Path: "identity-whitelist/" + instanceID,
Storage: storage,
}
resp, err = b.HandleRequest(wlRequest)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.Data == nil || resp.Data["role"] != roleName {
t.Fatalf("failed to read whitelist identity")
}
// Delete the whitelist identity entry.
wlRequest.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(wlRequest)
if err != nil {
t.Fatal(err)
}
if resp.IsError() {
t.Fatalf("failed to delete whitelist identity")
}
// Allow a fresh login.
resp, err = b.HandleRequest(loginRequest)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.Auth == nil || resp.IsError() {
t.Fatalf("login attempt failed")
}
}
func TestBackend_pathStsConfig(t *testing.T) {
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
stsReq := &logical.Request{
Operation: logical.CreateOperation,
Storage: storage,
Path: "config/sts/account1",
}
checkFound, exists, err := b.HandleExistenceCheck(stsReq)
if err != nil {
t.Fatal(err)
}
if !checkFound {
t.Fatal("existence check not found for path 'config/sts/account1'")
}
if exists {
t.Fatal("existence check should have returned 'false' for 'config/sts/account1'")
}
data := map[string]interface{}{
"sts_role": "arn:aws:iam:account1:role/myRole",
}
stsReq.Data = data
// test create operation
resp, err := b.HandleRequest(stsReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("resp: %#v, err: %v", resp, err)
}
stsReq.Data = nil
// test existence check
checkFound, exists, err = b.HandleExistenceCheck(stsReq)
if err != nil {
t.Fatal(err)
}
if !checkFound {
t.Fatal("existence check not found for path 'config/sts/account1'")
}
if !exists {
t.Fatal("existence check should have returned 'true' for 'config/sts/account1'")
}
stsReq.Operation = logical.ReadOperation
// test read operation
resp, err = b.HandleRequest(stsReq)
expectedStsRole := "arn:aws:iam:account1:role/myRole"
if resp.Data["sts_role"].(string) != expectedStsRole {
t.Fatalf("bad: expected:%s\n got:%s\n", expectedStsRole, resp.Data["sts_role"].(string))
}
stsReq.Operation = logical.CreateOperation
stsReq.Path = "config/sts/account2"
stsReq.Data = data
// create another entry to test the list operation
resp, err = b.HandleRequest(stsReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatal(err)
}
stsReq.Operation = logical.ListOperation
stsReq.Path = "config/sts"
// test list operation
resp, err = b.HandleRequest(stsReq)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.IsError() {
t.Fatalf("failed to list config/sts")
}
keys := resp.Data["keys"].([]string)
if len(keys) != 2 {
t.Fatalf("invalid keys listed: %#v\n", keys)
}
stsReq.Operation = logical.DeleteOperation
stsReq.Path = "config/sts/account1"
resp, err = b.HandleRequest(stsReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatal(err)
}
stsReq.Path = "config/sts/account2"
resp, err = b.HandleRequest(stsReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatal(err)
}
stsReq.Operation = logical.ListOperation
stsReq.Path = "config/sts"
// test list operation
resp, err = b.HandleRequest(stsReq)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.IsError() {
t.Fatalf("failed to list config/sts")
}
if resp.Data["keys"] != nil {
t.Fatalf("no entries should be present")
}
}
func buildCallerIdentityLoginData(request *http.Request, roleName string) (map[string]interface{}, error) {
headersJson, err := json.Marshal(request.Header)
if err != nil {
return nil, err
}
requestBody, err := ioutil.ReadAll(request.Body)
if err != nil {
return nil, err
}
return map[string]interface{}{
"iam_http_request_method": request.Method,
"iam_request_url": base64.StdEncoding.EncodeToString([]byte(request.URL.String())),
"iam_request_headers": base64.StdEncoding.EncodeToString(headersJson),
"iam_request_body": base64.StdEncoding.EncodeToString(requestBody),
"request_role": roleName,
}, nil
}
// This is an acceptance test.
// If the test is NOT being run on an AWS EC2 instance in an instance profile,
// it requires the following environment variables to be set:
// TEST_AWS_ACCESS_KEY_ID
// TEST_AWS_SECRET_ACCESS_KEY
// TEST_AWS_SECURITY_TOKEN or TEST_AWS_SESSION_TOKEN (optional, if you are using short-lived creds)
// These are intentionally NOT the "standard" variables to prevent accidentally
// using prod creds in acceptance tests
func TestBackendAcc_LoginWithCallerIdentity(t *testing.T) {
// This test case should be run only when certain env vars are set and
// executed as an acceptance test.
if os.Getenv(logicaltest.TestEnvVar) == "" {
t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", logicaltest.TestEnvVar))
return
}
storage := &logical.InmemStorage{}
config := logical.TestBackendConfig()
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
// Override the default AWS env vars (if set) with our test creds
// so that the credential provider chain will pick them up
// NOTE that I'm not bothing to override the shared config file location,
// so if creds are specified there, they will be used before IAM
// instance profile creds
// This doesn't provide perfect leakage protection (e.g., it will still
// potentially pick up credentials from the ~/.config files), but probably
// good enough rather than having to muck around in the low-level details
for _, envvar := range []string{
"AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SECURITY_TOKEN", "AWS_SESSION_TOKEN"} {
// restore existing environment variables (in case future tests need them)
defer os.Setenv(envvar, os.Getenv(envvar))
os.Setenv(envvar, os.Getenv("TEST_"+envvar))
}
awsSession, err := session.NewSession()
if err != nil {
fmt.Println("failed to create session,", err)
return
}
stsService := sts.New(awsSession)
stsInputParams := &sts.GetCallerIdentityInput{}
testIdentity, err := stsService.GetCallerIdentity(stsInputParams)
if err != nil {
t.Fatalf("Received error retrieving identity: %s", err)
}
testIdentityArn, _, _, err := parseIamArn(*testIdentity.Arn)
if err != nil {
t.Fatal(err)
}
// Test setup largely done
// At this point, we're going to:
// 1. Configure the client to require our test header value
// 2. Configure two different roles:
// a. One bound to our test user
// b. One bound to a garbage ARN
// 3. Pass in a request that doesn't have the signed header, ensure
// we're not allowed to login
// 4. Passin a request that has a validly signed header, but the wrong
// value, ensure it doesn't allow login
// 5. Pass in a request that has a validly signed request, ensure
// it allows us to login to our role
// 6. Pass in a request that has a validly signed request, asking for
// the other role, ensure it fails
const testVaultHeaderValue = "VaultAcceptanceTesting"
const testValidRoleName = "valid-role"
const testInvalidRoleName = "invalid-role"
clientConfigData := map[string]interface{}{
"iam_server_id_header_value": testVaultHeaderValue,
}
clientRequest := &logical.Request{
Operation: logical.UpdateOperation,
Path: "config/client",
Storage: storage,
Data: clientConfigData,
}
_, err = b.HandleRequest(clientRequest)
if err != nil {
t.Fatal(err)
}
// configuring the valid role we'll be able to login to
roleData := map[string]interface{}{
"bound_iam_principal_arn": testIdentityArn,
"policies": "root",
"auth_type": iamAuthType,
}
roleRequest := &logical.Request{
Operation: logical.CreateOperation,
Path: "role/" + testValidRoleName,
Storage: storage,
Data: roleData,
}
resp, err := b.HandleRequest(roleRequest)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: failed to create role: resp:%#v\nerr:%v", resp, err)
}
// configuring a valid role we won't be able to login to
roleDataEc2 := map[string]interface{}{
"auth_type": "ec2",
"policies": "root",
"bound_ami_id": "ami-1234567",
}
roleRequestEc2 := &logical.Request{
Operation: logical.CreateOperation,
Path: "role/ec2only",
Storage: storage,
Data: roleDataEc2,
}
resp, err = b.HandleRequest(roleRequestEc2)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: failed to create role; resp:%#v\nerr:%v", resp, err)
}
// now we're creating the invalid role we won't be able to login to
roleData["bound_iam_principal_arn"] = "arn:aws:iam::123456789012:role/FakeRole"
roleRequest.Path = "role/" + testInvalidRoleName
resp, err = b.HandleRequest(roleRequest)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: didn't fail to create role: resp:%#v\nerr:%v", resp, err)
}
// now, create the request without the signed header
stsRequestNoHeader, _ := stsService.GetCallerIdentityRequest(stsInputParams)
stsRequestNoHeader.Sign()
loginData, err := buildCallerIdentityLoginData(stsRequestNoHeader.HTTPRequest, testValidRoleName)
if err != nil {
t.Fatal(err)
}
loginRequest := &logical.Request{
Operation: logical.UpdateOperation,
Path: "login",
Storage: storage,
Data: loginData,
}
resp, err = b.HandleRequest(loginRequest)
if err != nil || resp == nil || !resp.IsError() {
t.Errorf("bad: expected failed login due to missing header: resp:%#v\nerr:%v", resp, err)
}
// create the request with the invalid header value
// Not reusing stsRequestNoHeader because the process of signing the request
// and reading the body modifies the underlying request, so it's just cleaner
// to get new requests.
stsRequestInvalidHeader, _ := stsService.GetCallerIdentityRequest(stsInputParams)
stsRequestInvalidHeader.HTTPRequest.Header.Add(iamServerIdHeader, "InvalidValue")
stsRequestInvalidHeader.Sign()
loginData, err = buildCallerIdentityLoginData(stsRequestInvalidHeader.HTTPRequest, testValidRoleName)
if err != nil {
t.Fatal(err)
}
loginRequest = &logical.Request{
Operation: logical.UpdateOperation,
Path: "login",
Storage: storage,
Data: loginData,
}
resp, err = b.HandleRequest(loginRequest)
if err != nil || resp == nil || !resp.IsError() {
t.Errorf("bad: expected failed login due to invalid header: resp:%#v\nerr:%v", resp, err)
}
// Now, valid request against invalid role
stsRequestValid, _ := stsService.GetCallerIdentityRequest(stsInputParams)
stsRequestValid.HTTPRequest.Header.Add(iamServerIdHeader, testVaultHeaderValue)
stsRequestValid.Sign()
loginData, err = buildCallerIdentityLoginData(stsRequestValid.HTTPRequest, testInvalidRoleName)
if err != nil {
t.Fatal(err)
}
loginRequest = &logical.Request{
Operation: logical.UpdateOperation,
Path: "login",
Storage: storage,
Data: loginData,
}
resp, err = b.HandleRequest(loginRequest)
if err != nil || resp == nil || !resp.IsError() {
t.Errorf("bad: expected failed login due to invalid role: resp:%#v\nerr:%v", resp, err)
}
loginData["role"] = "ec2only"
resp, err = b.HandleRequest(loginRequest)
if err != nil || resp == nil || !resp.IsError() {
t.Errorf("bad: expected failed login due to bad auth type: resp:%#v\nerr:%v", resp, err)
}
// finally, the happy path tests :)
loginData["role"] = testValidRoleName
resp, err = b.HandleRequest(loginRequest)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.Auth == nil || resp.IsError() {
t.Errorf("bad: expected valid login: resp:%#v", resp)
}
}