acl: Fix tag parsing for IAM users and roles in IAM auth method (#12797)
* acl: Fix tag parsing on IAM users and roles in IAM auth method * Add changelog
This commit is contained in:
parent
e62745c82c
commit
1a938e4a49
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bug
|
||||||
|
acl: Fix parsing of IAM user and role tags in IAM auth method
|
||||||
|
```
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
"github.com/hashicorp/consul/internal/iamauth/iamauthtest"
|
"github.com/hashicorp/consul/internal/iamauth/iamauthtest"
|
||||||
|
"github.com/hashicorp/consul/internal/iamauth/responses"
|
||||||
"github.com/hashicorp/consul/internal/iamauth/responsestest"
|
"github.com/hashicorp/consul/internal/iamauth/responsestest"
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -18,11 +19,11 @@ func TestValidateLogin(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
serverForRoleMismatchedIds = &iamauthtest.Server{
|
serverForRoleMismatchedIds = &iamauthtest.Server{
|
||||||
GetCallerIdentityResponse: f.ServerForRole.GetCallerIdentityResponse,
|
GetCallerIdentityResponse: f.ServerForRole.GetCallerIdentityResponse,
|
||||||
GetRoleResponse: responsestest.MakeGetRoleResponse(f.RoleARN, "AAAAsomenonmatchingid"),
|
GetRoleResponse: responsestest.MakeGetRoleResponse(f.RoleARN, "AAAAsomenonmatchingid", responses.Tags{}),
|
||||||
}
|
}
|
||||||
serverForUserMismatchedIds = &iamauthtest.Server{
|
serverForUserMismatchedIds = &iamauthtest.Server{
|
||||||
GetCallerIdentityResponse: f.ServerForUser.GetCallerIdentityResponse,
|
GetCallerIdentityResponse: f.ServerForUser.GetCallerIdentityResponse,
|
||||||
GetUserResponse: responsestest.MakeGetUserResponse(f.UserARN, "AAAAsomenonmatchingid"),
|
GetUserResponse: responsestest.MakeGetUserResponse(f.UserARN, "AAAAsomenonmatchingid", responses.Tags{}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,7 @@ func MakeFixture() Fixture {
|
||||||
f.AssumedRoleARN, f.EntityIDWithSession, f.AccountID,
|
f.AssumedRoleARN, f.EntityIDWithSession, f.AccountID,
|
||||||
),
|
),
|
||||||
GetRoleResponse: responsestest.MakeGetRoleResponse(
|
GetRoleResponse: responsestest.MakeGetRoleResponse(
|
||||||
f.RoleARN, f.EntityID, toTags(f.RoleTags)...,
|
f.RoleARN, f.EntityID, toTags(f.RoleTags),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ func MakeFixture() Fixture {
|
||||||
f.UserARN, f.EntityID, f.AccountID,
|
f.UserARN, f.EntityID, f.AccountID,
|
||||||
),
|
),
|
||||||
GetUserResponse: responsestest.MakeGetUserResponse(
|
GetUserResponse: responsestest.MakeGetUserResponse(
|
||||||
f.UserARN, f.EntityID, toTags(f.UserTags)...,
|
f.UserARN, f.EntityID, toTags(f.UserTags),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,15 +155,15 @@ func (f *Fixture) RoleTagValues() []string { return values(f.RoleTags) }
|
||||||
func (f *Fixture) UserTagValues() []string { return values(f.UserTags) }
|
func (f *Fixture) UserTagValues() []string { return values(f.UserTags) }
|
||||||
|
|
||||||
// toTags converts the map to a slice of responses.Tag
|
// toTags converts the map to a slice of responses.Tag
|
||||||
func toTags(tags map[string]string) []responses.Tag {
|
func toTags(tags map[string]string) responses.Tags {
|
||||||
result := []responses.Tag{}
|
members := []responses.TagMember{}
|
||||||
for k, v := range tags {
|
for k, v := range tags {
|
||||||
result = append(result, responses.Tag{
|
members = append(members, responses.TagMember{
|
||||||
Key: k,
|
Key: k,
|
||||||
Value: v,
|
Value: v,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return result
|
return responses.Tags{Members: members}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ type Role struct {
|
||||||
Path string `xml:"Path"`
|
Path string `xml:"Path"`
|
||||||
RoleId string `xml:"RoleId"`
|
RoleId string `xml:"RoleId"`
|
||||||
RoleName string `xml:"RoleName"`
|
RoleName string `xml:"RoleName"`
|
||||||
Tags []Tag `xml:"Tags"`
|
Tags Tags `xml:"Tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Role) EntityPath() string { return r.Path }
|
func (r *Role) EntityPath() string { return r.Path }
|
||||||
|
@ -69,7 +69,7 @@ type User struct {
|
||||||
Path string `xml:"Path"`
|
Path string `xml:"Path"`
|
||||||
UserId string `xml:"UserId"`
|
UserId string `xml:"UserId"`
|
||||||
UserName string `xml:"UserName"`
|
UserName string `xml:"UserName"`
|
||||||
Tags []Tag `xml:"Tags"`
|
Tags Tags `xml:"Tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) EntityPath() string { return u.Path }
|
func (u *User) EntityPath() string { return u.Path }
|
||||||
|
@ -78,14 +78,18 @@ func (u *User) EntityName() string { return u.UserName }
|
||||||
func (u *User) EntityId() string { return u.UserId }
|
func (u *User) EntityId() string { return u.UserId }
|
||||||
func (u *User) EntityTags() map[string]string { return tagsToMap(u.Tags) }
|
func (u *User) EntityTags() map[string]string { return tagsToMap(u.Tags) }
|
||||||
|
|
||||||
type Tag struct {
|
type Tags struct {
|
||||||
|
Members []TagMember `xml:"member"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TagMember struct {
|
||||||
Key string `xml:"Key"`
|
Key string `xml:"Key"`
|
||||||
Value string `xml:"Value"`
|
Value string `xml:"Value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func tagsToMap(tags []Tag) map[string]string {
|
func tagsToMap(tags Tags) map[string]string {
|
||||||
result := map[string]string{}
|
result := map[string]string{}
|
||||||
for _, tag := range tags {
|
for _, tag := range tags.Members {
|
||||||
result[tag.Key] = tag.Value
|
result[tag.Key] = tag.Value
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package responses
|
package responses
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/xml"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -155,3 +156,138 @@ func TestCanonicalArn(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalXML(t *testing.T) {
|
||||||
|
t.Run("user xml", func(t *testing.T) {
|
||||||
|
var resp GetUserResponse
|
||||||
|
err := xml.Unmarshal([]byte(rawUserXML), &resp)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expectedParsedUserXML, resp)
|
||||||
|
})
|
||||||
|
t.Run("role xml", func(t *testing.T) {
|
||||||
|
var resp GetRoleResponse
|
||||||
|
err := xml.Unmarshal([]byte(rawRoleXML), &resp)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expectedParsedRoleXML, resp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
rawUserXML = `<GetUserResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||||
|
<GetUserResult>
|
||||||
|
<User>
|
||||||
|
<Path>/</Path>
|
||||||
|
<Arn>arn:aws:iam::000000000000:user/my-user</Arn>
|
||||||
|
<UserName>my-user</UserName>
|
||||||
|
<UserId>AIDAexampleuserid</UserId>
|
||||||
|
<CreateDate>2021-01-01T00:01:02Z</CreateDate>
|
||||||
|
<Tags>
|
||||||
|
<member>
|
||||||
|
<Value>some-value</Value>
|
||||||
|
<Key>some-tag</Key>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<Value>another-value</Value>
|
||||||
|
<Key>another-tag</Key>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<Value>third-value</Value>
|
||||||
|
<Key>third-tag</Key>
|
||||||
|
</member>
|
||||||
|
</Tags>
|
||||||
|
</User>
|
||||||
|
</GetUserResult>
|
||||||
|
<ResponseMetadata>
|
||||||
|
<RequestId>11815b96-cb16-4d33-b2cf-0042fa4db4cd</RequestId>
|
||||||
|
</ResponseMetadata>
|
||||||
|
</GetUserResponse>`
|
||||||
|
|
||||||
|
expectedParsedUserXML = GetUserResponse{
|
||||||
|
XMLName: xml.Name{
|
||||||
|
Space: "https://iam.amazonaws.com/doc/2010-05-08/",
|
||||||
|
Local: "GetUserResponse",
|
||||||
|
},
|
||||||
|
GetUserResult: []GetUserResult{
|
||||||
|
{
|
||||||
|
User: User{
|
||||||
|
Arn: "arn:aws:iam::000000000000:user/my-user",
|
||||||
|
Path: "/",
|
||||||
|
UserId: "AIDAexampleuserid",
|
||||||
|
UserName: "my-user",
|
||||||
|
Tags: Tags{
|
||||||
|
Members: []TagMember{
|
||||||
|
{Key: "some-tag", Value: "some-value"},
|
||||||
|
{Key: "another-tag", Value: "another-value"},
|
||||||
|
{Key: "third-tag", Value: "third-value"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResponseMetadata: []ResponseMetadata{
|
||||||
|
{RequestId: "11815b96-cb16-4d33-b2cf-0042fa4db4cd"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rawRoleXML = `<GetRoleResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||||
|
<GetRoleResult>
|
||||||
|
<Role>
|
||||||
|
<Path>/</Path>
|
||||||
|
<AssumeRolePolicyDocument>some-json-document-that-we-ignore</AssumeRolePolicyDocument>
|
||||||
|
<MaxSessionDuration>43200</MaxSessionDuration>
|
||||||
|
<RoleId>AROAsomeuniqueid</RoleId>
|
||||||
|
<RoleLastUsed>
|
||||||
|
<LastUsedDate>2022-01-01T01:02:03Z</LastUsedDate>
|
||||||
|
<Region>us-east-1</Region>
|
||||||
|
</RoleLastUsed>
|
||||||
|
<RoleName>my-role</RoleName>
|
||||||
|
<Arn>arn:aws:iam::000000000000:role/my-role</Arn>
|
||||||
|
<CreateDate>2020-01-01T00:00:01Z</CreateDate>
|
||||||
|
<Tags>
|
||||||
|
<member>
|
||||||
|
<Value>some-value</Value>
|
||||||
|
<Key>some-key</Key>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<Value>another-value</Value>
|
||||||
|
<Key>another-key</Key>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<Value>a-third-value</Value>
|
||||||
|
<Key>third-key</Key>
|
||||||
|
</member>
|
||||||
|
</Tags>
|
||||||
|
</Role>
|
||||||
|
</GetRoleResult>
|
||||||
|
<ResponseMetadata>
|
||||||
|
<RequestId>a9866067-c0e5-4b5e-86ba-429c1151e2fb</RequestId>
|
||||||
|
</ResponseMetadata>
|
||||||
|
</GetRoleResponse>`
|
||||||
|
|
||||||
|
expectedParsedRoleXML = GetRoleResponse{
|
||||||
|
XMLName: xml.Name{
|
||||||
|
Space: "https://iam.amazonaws.com/doc/2010-05-08/",
|
||||||
|
Local: "GetRoleResponse",
|
||||||
|
},
|
||||||
|
GetRoleResult: []GetRoleResult{
|
||||||
|
{
|
||||||
|
Role: Role{
|
||||||
|
Arn: "arn:aws:iam::000000000000:role/my-role",
|
||||||
|
Path: "/",
|
||||||
|
RoleId: "AROAsomeuniqueid",
|
||||||
|
RoleName: "my-role",
|
||||||
|
Tags: Tags{
|
||||||
|
Members: []TagMember{
|
||||||
|
{Key: "some-key", Value: "some-value"},
|
||||||
|
{Key: "another-key", Value: "another-value"},
|
||||||
|
{Key: "third-key", Value: "a-third-value"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResponseMetadata: []ResponseMetadata{
|
||||||
|
{RequestId: "a9866067-c0e5-4b5e-86ba-429c1151e2fb"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
|
@ -31,7 +31,7 @@ func MakeGetCallerIdentityResponse(arn, userId, accountId string) responses.GetC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeGetRoleResponse(arn, id string, tags ...responses.Tag) responses.GetRoleResponse {
|
func MakeGetRoleResponse(arn, id string, tags responses.Tags) responses.GetRoleResponse {
|
||||||
if strings.Contains(id, ":") {
|
if strings.Contains(id, ":") {
|
||||||
panic("RoleId in GetRole response must not contain ':'")
|
panic("RoleId in GetRole response must not contain ':'")
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ func MakeGetRoleResponse(arn, id string, tags ...responses.Tag) responses.GetRol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeGetUserResponse(arn, id string, tags ...responses.Tag) responses.GetUserResponse {
|
func MakeGetUserResponse(arn, id string, tags responses.Tags) responses.GetUserResponse {
|
||||||
if strings.Contains(id, ":") {
|
if strings.Contains(id, ":") {
|
||||||
panic("UserId in GetUser resposne must not contain ':'")
|
panic("UserId in GetUser resposne must not contain ':'")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue