Skip to content

Commit bd51234

Browse files
authored
Pass Session Tags to credential validation check (#4862)
The provider wasn't sending the session tags when validating credentials. This caused perpetual credential check failures if users used the tags for enforcing access. This change fixes that. Fixes #4849
1 parent d0ed212 commit bd51234

File tree

4 files changed

+172
-0
lines changed

4 files changed

+172
-0
lines changed

provider/provider_yaml_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,17 @@ func TestAccDefaultTagsWithImport(t *testing.T) {
753753
}
754754
}
755755

756+
func TestAssumeRoleSessionTags(t *testing.T) {
757+
t.Parallel()
758+
ptest := pulumiTest(t, filepath.Join("test-programs", "assume-role-session-tags"), opttest.SkipInstall())
759+
result := ptest.Up(t)
760+
t.Logf("STDOUT: %v", result.StdOut)
761+
t.Logf("STDERR: %v", result.StdErr)
762+
763+
require.Contains(t, result.Outputs, "bucketArn")
764+
assert.NotEmpty(t, result.Outputs["bucketArn"].Value.(string))
765+
}
766+
756767
// testTagsPulumiLifecycle tests the complete lifecycle of a pulumi program
757768
// Scenarios that this tests:
758769
// 1. `Up` with both provider `defaultTags`/`ignoreTags` and resource level `tags`

provider/resources.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,26 @@ func arrayValue(vars resource.PropertyMap, prop resource.PropertyKey, envs []str
579579
return vals
580580
}
581581

582+
func extractTags(vars resource.PropertyMap, prop resource.PropertyKey) map[string]string {
583+
val, ok := vars[prop]
584+
585+
if !ok || !val.IsObject() {
586+
return nil
587+
}
588+
589+
tagProp := val.ObjectValue()
590+
tags := make(map[string]string, len(tagProp))
591+
592+
for k, v := range tagProp {
593+
if !v.IsString() {
594+
continue
595+
}
596+
tags[string(k)] = v.StringValue()
597+
}
598+
599+
return tags
600+
}
601+
582602
// returns a pointer so we can distinguish between a zero value and a missing value
583603
func durationFromConfig(vars resource.PropertyMap, prop resource.PropertyKey) (*time.Duration, error) {
584604
val, ok := vars[prop]
@@ -630,6 +650,7 @@ func validateCredentials(vars resource.PropertyMap, c shim.ResourceConfig) error
630650
SessionName: stringValue(details.ObjectValue(), "sessionName", []string{}),
631651
SourceIdentity: stringValue(details.ObjectValue(), "sourceIdentity", []string{}),
632652
TransitiveTagKeys: arrayValue(details.ObjectValue(), "transitiveTagKeys", []string{}),
653+
Tags: extractTags(details.ObjectValue(), "tags"),
633654
}
634655
duration, err := durationFromConfig(details.ObjectValue(), "durationSeconds")
635656
if err != nil {

provider/resources_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,68 @@ func TestHasOptionalOrRequiredNamePropertyOptimized(t *testing.T) {
6666
}
6767
}
6868
}
69+
func TestExtractTags(t *testing.T) {
70+
tests := []struct {
71+
name string
72+
vars resource.PropertyMap
73+
prop resource.PropertyKey
74+
expected map[string]string
75+
}{
76+
{
77+
name: "valid tags",
78+
vars: resource.PropertyMap{
79+
"tags": resource.NewObjectProperty(resource.PropertyMap{
80+
"Name": resource.NewStringProperty("example"),
81+
"Env": resource.NewStringProperty("production"),
82+
}),
83+
},
84+
prop: "tags",
85+
expected: map[string]string{
86+
"Name": "example",
87+
"Env": "production",
88+
},
89+
},
90+
{
91+
name: "no tags",
92+
vars: resource.PropertyMap{
93+
"tags": resource.NewObjectProperty(resource.PropertyMap{}),
94+
},
95+
prop: "tags",
96+
expected: map[string]string{},
97+
},
98+
{
99+
name: "non-string tags",
100+
vars: resource.PropertyMap{
101+
"tags": resource.NewObjectProperty(resource.PropertyMap{
102+
"Name": resource.NewStringProperty("example"),
103+
"Count": resource.NewNumberProperty(1),
104+
}),
105+
},
106+
prop: "tags",
107+
expected: map[string]string{
108+
"Name": "example",
109+
},
110+
},
111+
{
112+
name: "missing tags property",
113+
vars: resource.PropertyMap{},
114+
prop: "tags",
115+
expected: nil,
116+
},
117+
{
118+
name: "tags property is not an object",
119+
vars: resource.PropertyMap{
120+
"tags": resource.NewStringProperty("not-an-object"),
121+
},
122+
prop: "tags",
123+
expected: nil,
124+
},
125+
}
126+
127+
for _, tt := range tests {
128+
t.Run(tt.name, func(t *testing.T) {
129+
actual := extractTags(tt.vars, tt.prop)
130+
assert.Equal(t, tt.expected, actual)
131+
})
132+
}
133+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
name: assume-role-session-tags
2+
runtime: yaml
3+
4+
variables:
5+
awsAccount:
6+
fn::invoke:
7+
function: aws:getCallerIdentity
8+
currentRole:
9+
fn::invoke:
10+
function: aws:iam:getSessionContext
11+
arguments:
12+
arn: ${awsAccount.arn}
13+
14+
resources:
15+
bootstrapProvider:
16+
type: pulumi:providers:aws
17+
18+
iamRole:
19+
type: aws:iam:Role
20+
properties:
21+
assumeRolePolicy:
22+
fn::toJSON:
23+
Version: "2012-10-17"
24+
Statement:
25+
- Action:
26+
- "sts:AssumeRole"
27+
- "sts:TagSession"
28+
Effect: "Allow"
29+
Principal:
30+
AWS: ${currentRole.issuerArn}
31+
Condition:
32+
StringEquals:
33+
"aws:RequestTag/Repository":
34+
- "my-org/my-repo"
35+
inlinePolicies:
36+
- name: "inline-policy"
37+
policy:
38+
fn::toJSON:
39+
Version: "2012-10-17"
40+
Statement:
41+
- Action:
42+
- "s3:*"
43+
Effect: "Allow"
44+
Resource: "*"
45+
options:
46+
provider: ${bootstrapProvider}
47+
48+
# IAM has a delay in propagating the new role, so we need to wait for it to be available
49+
# AWS is aiming for P99 below 2s so 6s should be enough
50+
wait6s:
51+
type: time:Sleep
52+
properties:
53+
createDuration: 6s
54+
55+
provider:
56+
type: pulumi:providers:aws
57+
properties:
58+
assumeRole:
59+
roleArn: ${iamRole.arn}
60+
sessionName: "session-tagging-test"
61+
tags:
62+
Repository: "my-org/my-repo"
63+
options:
64+
dependsOn:
65+
- ${wait6s}
66+
67+
myTestBucket:
68+
type: aws:s3:Bucket
69+
options:
70+
provider: ${provider}
71+
72+
outputs:
73+
bucketArn: ${myTestBucket.arn}
74+
roleId: ${iamRole.id}
75+
roleARN: ${iamRole.arn}

0 commit comments

Comments
 (0)