blob: 2c0262075ad38184515fe0990a0b5d05c2dad068 [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package awsauth
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"strings"
"testing"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/hashicorp/vault/sdk/logical"
)
func TestBackend_pathLogin_getCallerIdentityResponse(t *testing.T) {
responseFromUser := `<GetCallerIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<GetCallerIdentityResult>
<Arn>arn:aws:iam::123456789012:user/MyUserName</Arn>
<UserId>ASOMETHINGSOMETHINGSOMETHING</UserId>
<Account>123456789012</Account>
</GetCallerIdentityResult>
<ResponseMetadata>
<RequestId>7f4fc40c-853a-11e6-8848-8d035d01eb87</RequestId>
</ResponseMetadata>
</GetCallerIdentityResponse>`
expectedUserArn := "arn:aws:iam::123456789012:user/MyUserName"
responseFromAssumedRole := `<GetCallerIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<GetCallerIdentityResult>
<Arn>arn:aws:sts::123456789012:assumed-role/RoleName/RoleSessionName</Arn>
<UserId>ASOMETHINGSOMETHINGELSE:RoleSessionName</UserId>
<Account>123456789012</Account>
</GetCallerIdentityResult>
<ResponseMetadata>
<RequestId>7f4fc40c-853a-11e6-8848-8d035d01eb87</RequestId>
</ResponseMetadata>
</GetCallerIdentityResponse>`
expectedRoleArn := "arn:aws:sts::123456789012:assumed-role/RoleName/RoleSessionName"
parsedUserResponse, err := parseGetCallerIdentityResponse(responseFromUser)
if err != nil {
t.Fatal(err)
}
if parsedArn := parsedUserResponse.GetCallerIdentityResult[0].Arn; parsedArn != expectedUserArn {
t.Errorf("expected to parse arn %#v, got %#v", expectedUserArn, parsedArn)
}
parsedRoleResponse, err := parseGetCallerIdentityResponse(responseFromAssumedRole)
if err != nil {
t.Fatal(err)
}
if parsedArn := parsedRoleResponse.GetCallerIdentityResult[0].Arn; parsedArn != expectedRoleArn {
t.Errorf("expected to parn arn %#v; got %#v", expectedRoleArn, parsedArn)
}
_, err = parseGetCallerIdentityResponse("SomeRandomGibberish")
if err == nil {
t.Errorf("expected to NOT parse random giberish, but didn't get an error")
}
}
func TestBackend_pathLogin_parseIamArn(t *testing.T) {
testParser := func(inputArn, expectedCanonicalArn string, expectedEntity iamEntity) {
entity, err := parseIamArn(inputArn)
if err != nil {
t.Fatal(err)
}
if expectedCanonicalArn != "" && entity.canonicalArn() != expectedCanonicalArn {
t.Fatalf("expected to canonicalize ARN %q into %q but got %q instead", inputArn, expectedCanonicalArn, entity.canonicalArn())
}
if *entity != expectedEntity {
t.Fatalf("expected to get iamEntity %#v from input ARN %q but instead got %#v", expectedEntity, inputArn, *entity)
}
}
testParser("arn:aws:iam::123456789012:user/UserPath/MyUserName",
"arn:aws:iam::123456789012:user/MyUserName",
iamEntity{Partition: "aws", AccountNumber: "123456789012", Type: "user", Path: "UserPath", FriendlyName: "MyUserName"},
)
canonicalRoleArn := "arn:aws:iam::123456789012:role/RoleName"
testParser("arn:aws:sts::123456789012:assumed-role/RoleName/RoleSessionName",
canonicalRoleArn,
iamEntity{Partition: "aws", AccountNumber: "123456789012", Type: "assumed-role", FriendlyName: "RoleName", SessionInfo: "RoleSessionName"},
)
testParser("arn:aws:iam::123456789012:role/RolePath/RoleName",
canonicalRoleArn,
iamEntity{Partition: "aws", AccountNumber: "123456789012", Type: "role", Path: "RolePath", FriendlyName: "RoleName"},
)
testParser("arn:aws:iam::123456789012:instance-profile/profilePath/InstanceProfileName",
"",
iamEntity{Partition: "aws", AccountNumber: "123456789012", Type: "instance-profile", Path: "profilePath", FriendlyName: "InstanceProfileName"},
)
// Test that it properly handles pathological inputs...
_, err := parseIamArn("")
if err == nil {
t.Error("expected error from empty input string")
}
_, err = parseIamArn("arn:aws:iam::123456789012:role")
if err == nil {
t.Error("expected error from malformed ARN without a role name")
}
_, err = parseIamArn("arn:aws:iam")
if err == nil {
t.Error("expected error from incomplete ARN (arn:aws:iam)")
}
_, err = parseIamArn("arn:aws:iam::1234556789012:/")
if err == nil {
t.Error("expected error from empty principal type and no principal name (arn:aws:iam::1234556789012:/)")
}
_, err = parseIamArn("arn:aws:sts::1234556789012:assumed-role/role")
if err == nil {
t.Error("expected error from malformed assumed role ARN")
}
}
func TestBackend_validateVaultHeaderValue(t *testing.T) {
const canaryHeaderValue = "Vault-Server"
requestURL, err := url.Parse("https://sts.amazonaws.com/")
if err != nil {
t.Fatalf("error parsing test URL: %v", err)
}
postHeadersMissing := http.Header{
"Host": []string{"Foo"},
"Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-vault-aws-iam-server-id, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7"},
}
postHeadersInvalid := http.Header{
"Host": []string{"Foo"},
iamServerIdHeader: []string{"InvalidValue"},
"Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-vault-aws-iam-server-id, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7"},
}
postHeadersUnsigned := http.Header{
"Host": []string{"Foo"},
iamServerIdHeader: []string{canaryHeaderValue},
"Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7"},
}
postHeadersValid := http.Header{
"Host": []string{"Foo"},
iamServerIdHeader: []string{canaryHeaderValue},
"Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-vault-aws-iam-server-id, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7"},
}
postHeadersSplit := http.Header{
"Host": []string{"Foo"},
iamServerIdHeader: []string{canaryHeaderValue},
"Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request", "SignedHeaders=content-type;host;x-amz-date;x-vault-aws-iam-server-id, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7"},
}
err = validateVaultHeaderValue(postHeadersMissing, requestURL, canaryHeaderValue)
if err == nil {
t.Error("validated POST request with missing Vault header")
}
err = validateVaultHeaderValue(postHeadersInvalid, requestURL, canaryHeaderValue)
if err == nil {
t.Error("validated POST request with invalid Vault header value")
}
err = validateVaultHeaderValue(postHeadersUnsigned, requestURL, canaryHeaderValue)
if err == nil {
t.Error("validated POST request with unsigned Vault header")
}
err = validateVaultHeaderValue(postHeadersValid, requestURL, canaryHeaderValue)
if err != nil {
t.Errorf("did NOT validate valid POST request: %v", err)
}
err = validateVaultHeaderValue(postHeadersSplit, requestURL, canaryHeaderValue)
if err != nil {
t.Errorf("did NOT validate valid POST request with split Authorization header: %v", err)
}
}
// TestBackend_pathLogin_IAMHeaders tests login with iam_request_headers,
// supporting both base64 encoded string and JSON headers
func TestBackend_pathLogin_IAMHeaders(t *testing.T) {
storage := &logical.InmemStorage{}
config := logical.TestBackendConfig()
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
err = b.Setup(context.Background(), config)
if err != nil {
t.Fatal(err)
}
// sets up a test server to stand in for STS service
ts := setupIAMTestServer()
defer ts.Close()
clientConfigData := map[string]interface{}{
"iam_server_id_header_value": testVaultHeaderValue,
"sts_endpoint": ts.URL,
}
clientRequest := &logical.Request{
Operation: logical.UpdateOperation,
Path: "config/client",
Storage: storage,
Data: clientConfigData,
}
_, err = b.HandleRequest(context.Background(), clientRequest)
if err != nil {
t.Fatal(err)
}
// Configure identity.
_, err = b.HandleRequest(context.Background(), &logical.Request{
Operation: logical.UpdateOperation,
Path: "config/identity",
Storage: storage,
Data: map[string]interface{}{
"iam_alias": "role_id",
"iam_metadata": []string{
"account_id",
"auth_type",
"canonical_arn",
"client_arn",
"client_user_id",
"inferred_aws_region",
"inferred_entity_id",
"inferred_entity_type",
},
"ec2_alias": "role_id",
"ec2_metadata": []string{
"account_id",
"ami_id",
"instance_id",
"region",
},
},
})
if err != nil {
t.Fatal(err)
}
// create a role entry
roleEntry := &awsRoleEntry{
RoleID: "foo",
Version: currentRoleStorageVersion,
AuthType: iamAuthType,
}
if err := b.setRole(context.Background(), storage, testValidRoleName, roleEntry); err != nil {
t.Fatalf("failed to set entry: %s", err)
}
// create a baseline loginData map structure, including iam_request_headers
// already base64encoded. This is the "Default" loginData used for all tests.
// Each sub test can override the map's iam_request_headers entry
loginData, err := defaultLoginData()
if err != nil {
t.Fatal(err)
}
expectedAuthMetadata := map[string]string{
"account_id": "123456789012",
"auth_type": "iam",
"canonical_arn": "arn:aws:iam::123456789012:user/valid-role",
"client_arn": "arn:aws:iam::123456789012:user/valid-role",
"client_user_id": "ASOMETHINGSOMETHINGSOMETHING",
}
// expected errors for certain tests
missingHeaderErr := errors.New("error validating X-Vault-AWS-IAM-Server-ID header: missing header \"X-Vault-AWS-IAM-Server-ID\"")
parsingErr := errors.New("error making upstream request: error parsing STS response")
testCases := []struct {
Name string
Header interface{}
ExpectErr error
}{
{
Name: "Default",
},
{
Name: "Map-complete",
Header: map[string]interface{}{
"Content-Length": "43",
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"User-Agent": "aws-sdk-go/1.14.24 (go1.11; darwin; amd64)",
"X-Amz-Date": "20180910T203328Z",
"X-Vault-Aws-Iam-Server-Id": "VaultAcceptanceTesting",
"Authorization": "AWS4-HMAC-SHA256 Credential=AKIAJPQ466AIIQW4LPSQ/20180910/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-vault-aws-iam-server-id, Signature=cdef5819b2e97f1ff0f3e898fd2621aa03af00a4ec3e019122c20e5482534bf4",
},
},
{
Name: "Map-incomplete",
Header: map[string]interface{}{
"Content-Length": "43",
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"User-Agent": "aws-sdk-go/1.14.24 (go1.11; darwin; amd64)",
"X-Amz-Date": "20180910T203328Z",
"Authorization": "AWS4-HMAC-SHA256 Credential=AKIAJPQ466AIIQW4LPSQ/20180910/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-vault-aws-iam-server-id, Signature=cdef5819b2e97f1ff0f3e898fd2621aa03af00a4ec3e019122c20e5482534bf4",
},
ExpectErr: missingHeaderErr,
},
{
Name: "Map-illegal-header",
Header: map[string]interface{}{
"Content-Length": "43",
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"User-Agent": "aws-sdk-go/1.14.24 (go1.11; darwin; amd64)",
"X-Amz-Date": "20180910T203328Z",
"Authorization": "AWS4-HMAC-SHA256 Credential=AKIAJPQ466AIIQW4LPSQ/20180910/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-vault-aws-iam-server-id, Signature=cdef5819b2e97f1ff0f3e898fd2621aa03af00a4ec3e019122c20e5482534bf4",
"X-Vault-Aws-Iam-Server-Id": "VaultAcceptanceTesting",
"X-Amz-Mallory-Header": "<?xml><h4ck0r/>",
},
ExpectErr: errors.New("invalid request header: X-Amz-Mallory-Header"),
},
{
Name: "JSON-complete",
Header: `{
"Content-Length":"43",
"Content-Type":"application/x-www-form-urlencoded; charset=utf-8",
"User-Agent":"aws-sdk-go/1.14.24 (go1.11; darwin; amd64)",
"X-Amz-Date":"20180910T203328Z",
"X-Vault-Aws-Iam-Server-Id": "VaultAcceptanceTesting",
"Authorization":"AWS4-HMAC-SHA256 Credential=AKIAJPQ466AIIQW4LPSQ/20180910/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-vault-aws-iam-server-id, Signature=cdef5819b2e97f1ff0f3e898fd2621aa03af00a4ec3e019122c20e5482534bf4"
}`,
},
{
Name: "JSON-incomplete",
Header: `{
"Content-Length":"43",
"Content-Type":"application/x-www-form-urlencoded; charset=utf-8",
"User-Agent":"aws-sdk-go/1.14.24 (go1.11; darwin; amd64)",
"X-Amz-Date":"20180910T203328Z",
"X-Vault-Aws-Iam-Server-Id": "VaultAcceptanceTesting",
"Authorization":"AWS4-HMAC-SHA256 Credential=AKIAJPQ466AIIQW4LPSQ/20180910/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-vault-aws-iam-server-id"
}`,
ExpectErr: parsingErr,
},
{
Name: "Base64-complete",
Header: base64Complete(),
},
{
Name: "Base64-incomplete-missing-header",
Header: base64MissingVaultID(),
ExpectErr: missingHeaderErr,
},
{
Name: "Base64-incomplete-missing-auth-sig",
Header: base64MissingAuthField(),
ExpectErr: parsingErr,
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
if tc.Header != nil {
loginData["iam_request_headers"] = tc.Header
}
loginRequest := &logical.Request{
Operation: logical.UpdateOperation,
Path: "login",
Storage: storage,
Data: loginData,
Connection: &logical.Connection{},
}
resp, err := b.HandleRequest(context.Background(), loginRequest)
if err != nil || resp == nil || resp.IsError() {
if tc.ExpectErr != nil && tc.ExpectErr.Error() == resp.Error().Error() {
return
}
t.Errorf("un expected failed login:\nresp: %#v\n\nerr: %v", resp, err)
}
if !reflect.DeepEqual(expectedAuthMetadata, resp.Auth.Alias.Metadata) {
t.Errorf("expected metadata (%#v) to match (%#v)", expectedAuthMetadata, resp.Auth.Alias.Metadata)
}
})
}
}
// TestBackend_pathLogin_IAMRoleResolution tests role resolution for an Iam login
func TestBackend_pathLogin_IAMRoleResolution(t *testing.T) {
storage := &logical.InmemStorage{}
config := logical.TestBackendConfig()
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
err = b.Setup(context.Background(), config)
if err != nil {
t.Fatal(err)
}
// sets up a test server to stand in for STS service
ts := setupIAMTestServer()
defer ts.Close()
clientConfigData := map[string]interface{}{
"iam_server_id_header_value": testVaultHeaderValue,
"sts_endpoint": ts.URL,
}
clientRequest := &logical.Request{
Operation: logical.UpdateOperation,
Path: "config/client",
Storage: storage,
Data: clientConfigData,
}
_, err = b.HandleRequest(context.Background(), clientRequest)
if err != nil {
t.Fatal(err)
}
// Configure identity.
_, err = b.HandleRequest(context.Background(), &logical.Request{
Operation: logical.UpdateOperation,
Path: "config/identity",
Storage: storage,
Data: map[string]interface{}{
"iam_alias": "role_id",
"iam_metadata": []string{
"account_id",
"auth_type",
"canonical_arn",
"client_arn",
"client_user_id",
"inferred_aws_region",
"inferred_entity_id",
"inferred_entity_type",
},
"ec2_alias": "role_id",
"ec2_metadata": []string{
"account_id",
"ami_id",
"instance_id",
"region",
},
},
})
if err != nil {
t.Fatal(err)
}
// create a role entry
roleEntry := &awsRoleEntry{
RoleID: "foo",
Version: currentRoleStorageVersion,
AuthType: iamAuthType,
}
if err := b.setRole(context.Background(), storage, testValidRoleName, roleEntry); err != nil {
t.Fatalf("failed to set entry: %s", err)
}
// create a baseline loginData map structure, including iam_request_headers
// already base64encoded. This is the "Default" loginData used for all tests.
// Each sub test can override the map's iam_request_headers entry
loginData, err := defaultLoginData()
if err != nil {
t.Fatal(err)
}
loginRequest := &logical.Request{
Operation: logical.ResolveRoleOperation,
Path: "login",
Storage: storage,
Data: loginData,
Connection: &logical.Connection{},
}
resp, err := b.HandleRequest(context.Background(), loginRequest)
if err != nil || resp == nil || resp.IsError() {
t.Errorf("unexpected failed role resolution:\nresp: %#v\n\nerr: %v", resp, err)
}
if resp.Data["role"] != testValidRoleName {
t.Fatalf("Role was not as expected. Expected %s, received %s", testValidRoleName, resp.Data["role"])
}
}
func TestBackend_defaultAliasMetadata(t *testing.T) {
storage := &logical.InmemStorage{}
config := logical.TestBackendConfig()
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
err = b.Setup(context.Background(), config)
if err != nil {
t.Fatal(err)
}
// sets up a test server to stand in for STS service
ts := setupIAMTestServer()
defer ts.Close()
clientConfigData := map[string]interface{}{
"iam_server_id_header_value": testVaultHeaderValue,
"sts_endpoint": ts.URL,
}
clientRequest := &logical.Request{
Operation: logical.UpdateOperation,
Path: "config/client",
Storage: storage,
Data: clientConfigData,
}
_, err = b.HandleRequest(context.Background(), clientRequest)
if err != nil {
t.Fatal(err)
}
// Configure identity.
_, err = b.HandleRequest(context.Background(), &logical.Request{
Operation: logical.UpdateOperation,
Path: "config/identity",
Storage: storage,
Data: map[string]interface{}{
"iam_alias": "role_id",
"ec2_alias": "role_id",
},
})
if err != nil {
t.Fatal(err)
}
// create a role entry
roleEntry := &awsRoleEntry{
RoleID: "foo",
Version: currentRoleStorageVersion,
AuthType: iamAuthType,
}
if err := b.setRole(context.Background(), storage, testValidRoleName, roleEntry); err != nil {
t.Fatalf("failed to set entry: %s", err)
}
// create a baseline loginData map structure, including iam_request_headers
// already base64encoded. This is the "Default" loginData used for all tests.
// Each sub test can override the map's iam_request_headers entry
loginData, err := defaultLoginData()
if err != nil {
t.Fatal(err)
}
expectedAliasMetadata := map[string]string{
"account_id": "123456789012",
"auth_type": "iam",
}
testCases := []struct {
Name string
Header interface{}
ExpectErr error
}{
{
Name: "Default",
},
{
Name: "Map-complete",
Header: map[string]interface{}{
"Content-Length": "43",
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"User-Agent": "aws-sdk-go/1.14.24 (go1.11; darwin; amd64)",
"X-Amz-Date": "20180910T203328Z",
"X-Vault-Aws-Iam-Server-Id": "VaultAcceptanceTesting",
"Authorization": "AWS4-HMAC-SHA256 Credential=AKIAJPQ466AIIQW4LPSQ/20180910/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-vault-aws-iam-server-id, Signature=cdef5819b2e97f1ff0f3e898fd2621aa03af00a4ec3e019122c20e5482534bf4",
},
},
{
Name: "JSON-complete",
Header: `{
"Content-Length":"43",
"Content-Type":"application/x-www-form-urlencoded; charset=utf-8",
"User-Agent":"aws-sdk-go/1.14.24 (go1.11; darwin; amd64)",
"X-Amz-Date":"20180910T203328Z",
"X-Vault-Aws-Iam-Server-Id": "VaultAcceptanceTesting",
"Authorization":"AWS4-HMAC-SHA256 Credential=AKIAJPQ466AIIQW4LPSQ/20180910/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-vault-aws-iam-server-id, Signature=cdef5819b2e97f1ff0f3e898fd2621aa03af00a4ec3e019122c20e5482534bf4"
}`,
},
{
Name: "Base64-complete",
Header: base64Complete(),
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
if tc.Header != nil {
loginData["iam_request_headers"] = tc.Header
}
loginRequest := &logical.Request{
Operation: logical.UpdateOperation,
Path: "login",
Storage: storage,
Data: loginData,
Connection: &logical.Connection{},
}
resp, err := b.HandleRequest(context.Background(), loginRequest)
if err != nil || resp == nil || resp.IsError() {
if tc.ExpectErr != nil && tc.ExpectErr.Error() == resp.Error().Error() {
return
}
t.Errorf("un expected failed login:\nresp: %#v\n\nerr: %v", resp, err)
}
if !reflect.DeepEqual(expectedAliasMetadata, resp.Auth.Alias.Metadata) {
t.Errorf("expected metadata (%#v) to match (%#v)", expectedAliasMetadata, resp.Auth.Alias.Metadata)
}
})
}
}
func defaultLoginData() (map[string]interface{}, error) {
awsSession, err := session.NewSession()
if err != nil {
return nil, fmt.Errorf("failed to create session: %s", err)
}
stsService := sts.New(awsSession)
stsInputParams := &sts.GetCallerIdentityInput{}
stsRequestValid, _ := stsService.GetCallerIdentityRequest(stsInputParams)
stsRequestValid.HTTPRequest.Header.Add(iamServerIdHeader, testVaultHeaderValue)
stsRequestValid.HTTPRequest.Header.Add("Authorization", fmt.Sprintf("%s,%s,%s",
"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request",
"SignedHeaders=content-type;host;x-amz-date;x-vault-aws-iam-server-id",
"Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7"))
stsRequestValid.Sign()
return buildCallerIdentityLoginData(stsRequestValid.HTTPRequest, testValidRoleName)
}
// setupIAMTestServer configures httptest server to intercept and respond to the
// IAM login path's invocation of submitCallerIdentityRequest (which does not
// use the AWS SDK), which receieves the mocked response responseFromUser
// containing user information matching the role.
func setupIAMTestServer() *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
responseString := `<GetCallerIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<GetCallerIdentityResult>
<Arn>arn:aws:iam::123456789012:user/valid-role</Arn>
<UserId>ASOMETHINGSOMETHINGSOMETHING</UserId>
<Account>123456789012</Account>
</GetCallerIdentityResult>
<ResponseMetadata>
<RequestId>7f4fc40c-853a-11e6-8848-8d035d01eb87</RequestId>
</ResponseMetadata>
</GetCallerIdentityResponse>
`
auth := r.Header.Get("Authorization")
parts := strings.Split(auth, ",")
for i, s := range parts {
s = strings.TrimSpace(s)
key := strings.Split(s, "=")
parts[i] = key[0]
}
// verify the "Authorization" header contains all the expected parts
expectedAuthParts := []string{"AWS4-HMAC-SHA256 Credential", "SignedHeaders", "Signature"}
var matchingCount int
for _, v := range parts {
for _, z := range expectedAuthParts {
if z == v {
matchingCount++
}
}
}
if matchingCount != len(expectedAuthParts) {
responseString = "missing auth parts"
}
w.Header().Add("Content-Type", "text/xml")
fmt.Fprintln(w, responseString)
}))
}
// base64Complete returns a base64 encoded auth header as expected
func base64Complete() string {
min := `{"Authorization":["AWS4-HMAC-SHA256 Credential=AKIAJPQ466AIIQW4LPSQ/20180907/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-vault-aws-iam-server-id, Signature=97086b0531854844099fc52733fa2c88a2bfb54b2689600c6e249358a8353b52"],"Content-Length":["43"],"Content-Type":["application/x-www-form-urlencoded; charset=utf-8"],"User-Agent":["aws-sdk-go/1.14.24 (go1.11; darwin; amd64)"],"X-Amz-Date":["20180907T222145Z"],"X-Vault-Aws-Iam-Server-Id":["VaultAcceptanceTesting"]}`
return min
}
// base64MissingVaultID returns a base64 encoded auth header, that omits the
// Vault ID header
func base64MissingVaultID() string {
min := `{"Authorization":["AWS4-HMAC-SHA256 Credential=AKIAJPQ466AIIQW4LPSQ/20180907/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-vault-aws-iam-server-id, Signature=97086b0531854844099fc52733fa2c88a2bfb54b2689600c6e249358a8353b52"],"Content-Length":["43"],"Content-Type":["application/x-www-form-urlencoded; charset=utf-8"],"User-Agent":["aws-sdk-go/1.14.24 (go1.11; darwin; amd64)"],"X-Amz-Date":["20180907T222145Z"]}`
return min
}
// base64MissingAuthField returns a base64 encoded Auth header, that omits the
// "Signature" part
func base64MissingAuthField() string {
min := `{"Authorization":["AWS4-HMAC-SHA256 Credential=AKIAJPQ466AIIQW4LPSQ/20180907/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-vault-aws-iam-server-id"],"Content-Length":["43"],"Content-Type":["application/x-www-form-urlencoded; charset=utf-8"],"User-Agent":["aws-sdk-go/1.14.24 (go1.11; darwin; amd64)"],"X-Amz-Date":["20180907T222145Z"],"X-Vault-Aws-Iam-Server-Id":["VaultAcceptanceTesting"]}`
return min
}