Skip to content

Commit d4f4788

Browse files
authored
Merge pull request #1735 from awetomaton/gh-1080-permissionsboundary
2 parents 78c7005 + af8e198 commit d4f4788

7 files changed

Lines changed: 225 additions & 60 deletions

File tree

pkg/clients/iam/fake/role.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ var _ clientset.RoleClient = (*MockRoleClient)(nil)
2929

3030
// MockRoleClient is a type that implements all the methods for RoleClient interface
3131
type MockRoleClient struct {
32-
MockGetRole func(ctx context.Context, input *iam.GetRoleInput, opts []func(*iam.Options)) (*iam.GetRoleOutput, error)
33-
MockCreateRole func(ctx context.Context, input *iam.CreateRoleInput, opts []func(*iam.Options)) (*iam.CreateRoleOutput, error)
34-
MockDeleteRole func(ctx context.Context, input *iam.DeleteRoleInput, opts []func(*iam.Options)) (*iam.DeleteRoleOutput, error)
35-
MockUpdateRole func(ctx context.Context, input *iam.UpdateRoleInput, opts []func(*iam.Options)) (*iam.UpdateRoleOutput, error)
36-
MockUpdateAssumeRolePolicy func(ctx context.Context, input *iam.UpdateAssumeRolePolicyInput, opts []func(*iam.Options)) (*iam.UpdateAssumeRolePolicyOutput, error)
37-
MockTagRole func(ctx context.Context, input *iam.TagRoleInput, opts []func(*iam.Options)) (*iam.TagRoleOutput, error)
38-
MockUntagRole func(ctx context.Context, input *iam.UntagRoleInput, opts []func(*iam.Options)) (*iam.UntagRoleOutput, error)
32+
MockGetRole func(ctx context.Context, input *iam.GetRoleInput, opts []func(*iam.Options)) (*iam.GetRoleOutput, error)
33+
MockCreateRole func(ctx context.Context, input *iam.CreateRoleInput, opts []func(*iam.Options)) (*iam.CreateRoleOutput, error)
34+
MockDeleteRole func(ctx context.Context, input *iam.DeleteRoleInput, opts []func(*iam.Options)) (*iam.DeleteRoleOutput, error)
35+
MockUpdateRole func(ctx context.Context, input *iam.UpdateRoleInput, opts []func(*iam.Options)) (*iam.UpdateRoleOutput, error)
36+
MockPutRolePermissionsBoundary func(ctx context.Context, input *iam.PutRolePermissionsBoundaryInput, opts []func(*iam.Options)) (*iam.PutRolePermissionsBoundaryOutput, error)
37+
MockDeleteRolePermissionsBoundary func(ctx context.Context, input *iam.DeleteRolePermissionsBoundaryInput, opts []func(*iam.Options)) (*iam.DeleteRolePermissionsBoundaryOutput, error)
38+
MockUpdateAssumeRolePolicy func(ctx context.Context, input *iam.UpdateAssumeRolePolicyInput, opts []func(*iam.Options)) (*iam.UpdateAssumeRolePolicyOutput, error)
39+
MockTagRole func(ctx context.Context, input *iam.TagRoleInput, opts []func(*iam.Options)) (*iam.TagRoleOutput, error)
40+
MockUntagRole func(ctx context.Context, input *iam.UntagRoleInput, opts []func(*iam.Options)) (*iam.UntagRoleOutput, error)
3941
}
4042

4143
// GetRole mocks GetRole method
@@ -58,6 +60,16 @@ func (m *MockRoleClient) UpdateRole(ctx context.Context, input *iam.UpdateRoleIn
5860
return m.MockUpdateRole(ctx, input, opts)
5961
}
6062

63+
// PutRolePermissionsBoundary mocks PutRolePermissionsBoundary method
64+
func (m *MockRoleClient) PutRolePermissionsBoundary(ctx context.Context, input *iam.PutRolePermissionsBoundaryInput, opts ...func(*iam.Options)) (*iam.PutRolePermissionsBoundaryOutput, error) {
65+
return m.MockPutRolePermissionsBoundary(ctx, input, opts)
66+
}
67+
68+
// DeleteRolePermissionsBoundary mocks DeleteRolePermissionsBoundary method
69+
func (m *MockRoleClient) DeleteRolePermissionsBoundary(ctx context.Context, input *iam.DeleteRolePermissionsBoundaryInput, opts ...func(*iam.Options)) (*iam.DeleteRolePermissionsBoundaryOutput, error) {
70+
return m.MockDeleteRolePermissionsBoundary(ctx, input, opts)
71+
}
72+
6173
// UpdateAssumeRolePolicy mocks UpdateAssumeRolePolicy method
6274
func (m *MockRoleClient) UpdateAssumeRolePolicy(ctx context.Context, input *iam.UpdateAssumeRolePolicyInput, opts ...func(*iam.Options)) (*iam.UpdateAssumeRolePolicyOutput, error) {
6375
return m.MockUpdateAssumeRolePolicy(ctx, input, opts)

pkg/clients/iam/fake/user.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ type MockUserInput struct {
3535

3636
// MockUserClient is a type that implements all the methods for RoleClient interface
3737
type MockUserClient struct {
38-
MockUserInput MockUserInput
39-
MockGetUser func(ctx context.Context, input *iam.GetUserInput, opts []func(*iam.Options)) (*iam.GetUserOutput, error)
40-
MockCreateUser func(ctx context.Context, input *iam.CreateUserInput, opts []func(*iam.Options)) (*iam.CreateUserOutput, error)
41-
MockDeleteUser func(ctx context.Context, input *iam.DeleteUserInput, opts []func(*iam.Options)) (*iam.DeleteUserOutput, error)
42-
MockUpdateUser func(ctx context.Context, input *iam.UpdateUserInput, opts []func(*iam.Options)) (*iam.UpdateUserOutput, error)
43-
MockTagUser func(ctx context.Context, input *iam.TagUserInput, opt []func(*iam.Options)) (*iam.TagUserOutput, error)
44-
MockUntagUser func(ctx context.Context, input *iam.UntagUserInput, opts []func(*iam.Options)) (*iam.UntagUserOutput, error)
38+
MockUserInput MockUserInput
39+
MockGetUser func(ctx context.Context, input *iam.GetUserInput, opts []func(*iam.Options)) (*iam.GetUserOutput, error)
40+
MockCreateUser func(ctx context.Context, input *iam.CreateUserInput, opts []func(*iam.Options)) (*iam.CreateUserOutput, error)
41+
MockDeleteUser func(ctx context.Context, input *iam.DeleteUserInput, opts []func(*iam.Options)) (*iam.DeleteUserOutput, error)
42+
MockUpdateUser func(ctx context.Context, input *iam.UpdateUserInput, opts []func(*iam.Options)) (*iam.UpdateUserOutput, error)
43+
MockPutUserPermissionsBoundary func(ctx context.Context, input *iam.PutUserPermissionsBoundaryInput, opts []func(*iam.Options)) (*iam.PutUserPermissionsBoundaryOutput, error)
44+
MockDeleteUserPermissionsBoundary func(ctx context.Context, input *iam.DeleteUserPermissionsBoundaryInput, opts []func(*iam.Options)) (*iam.DeleteUserPermissionsBoundaryOutput, error)
45+
MockTagUser func(ctx context.Context, input *iam.TagUserInput, opt []func(*iam.Options)) (*iam.TagUserOutput, error)
46+
MockUntagUser func(ctx context.Context, input *iam.UntagUserInput, opts []func(*iam.Options)) (*iam.UntagUserOutput, error)
4547
}
4648

4749
// GetUser mocks GetUser method
@@ -64,6 +66,16 @@ func (m *MockUserClient) UpdateUser(ctx context.Context, input *iam.UpdateUserIn
6466
return m.MockUpdateUser(ctx, input, opts)
6567
}
6668

69+
// PutUserPermissionsBoundary mocks PutUserPermissionsBoundary method
70+
func (m *MockUserClient) PutUserPermissionsBoundary(ctx context.Context, input *iam.PutUserPermissionsBoundaryInput, opts ...func(*iam.Options)) (*iam.PutUserPermissionsBoundaryOutput, error) {
71+
return m.MockPutUserPermissionsBoundary(ctx, input, opts)
72+
}
73+
74+
// DeleteUserPermissionsBoundary mocks DeleteUserPermissionsBoundary method
75+
func (m *MockUserClient) DeleteUserPermissionsBoundary(ctx context.Context, input *iam.DeleteUserPermissionsBoundaryInput, opts ...func(*iam.Options)) (*iam.DeleteUserPermissionsBoundaryOutput, error) {
76+
return m.MockDeleteUserPermissionsBoundary(ctx, input, opts)
77+
}
78+
6779
// TagUser mocks TagUser method
6880
func (m *MockUserClient) TagUser(ctx context.Context, input *iam.TagUserInput, opts ...func(*iam.Options)) (*iam.TagUserOutput, error) {
6981
m.MockUserInput.TagUserInput = input

pkg/clients/iam/role.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ type RoleClient interface {
3333
CreateRole(ctx context.Context, input *iam.CreateRoleInput, opts ...func(*iam.Options)) (*iam.CreateRoleOutput, error)
3434
DeleteRole(ctx context.Context, input *iam.DeleteRoleInput, opts ...func(*iam.Options)) (*iam.DeleteRoleOutput, error)
3535
UpdateRole(ctx context.Context, input *iam.UpdateRoleInput, opts ...func(*iam.Options)) (*iam.UpdateRoleOutput, error)
36+
PutRolePermissionsBoundary(ctx context.Context, params *iam.PutRolePermissionsBoundaryInput, optFns ...func(*iam.Options)) (*iam.PutRolePermissionsBoundaryOutput, error)
37+
DeleteRolePermissionsBoundary(ctx context.Context, params *iam.DeleteRolePermissionsBoundaryInput, optFns ...func(*iam.Options)) (*iam.DeleteRolePermissionsBoundaryOutput, error)
3638
UpdateAssumeRolePolicy(ctx context.Context, input *iam.UpdateAssumeRolePolicyInput, opts ...func(*iam.Options)) (*iam.UpdateAssumeRolePolicyOutput, error)
3739
TagRole(ctx context.Context, input *iam.TagRoleInput, opts ...func(*iam.Options)) (*iam.TagRoleOutput, error)
3840
UntagRole(ctx context.Context, input *iam.UntagRoleInput, opts ...func(*iam.Options)) (*iam.UntagRoleOutput, error)
@@ -89,6 +91,11 @@ func GenerateRole(in v1beta1.RoleParameters, role *iamtypes.Role) error {
8991
role.Description = in.Description
9092
role.MaxSessionDuration = in.MaxSessionDuration
9193
role.Path = in.Path
94+
if in.PermissionsBoundary != nil {
95+
role.PermissionsBoundary = &iamtypes.AttachedPermissionsBoundary{
96+
PermissionsBoundaryArn: in.PermissionsBoundary,
97+
}
98+
}
9299

93100
if len(in.Tags) != 0 {
94101
role.Tags = make([]iamtypes.Tag, len(in.Tags))

pkg/clients/iam/user.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ type UserClient interface {
1717
CreateUser(ctx context.Context, input *iam.CreateUserInput, opts ...func(*iam.Options)) (*iam.CreateUserOutput, error)
1818
DeleteUser(ctx context.Context, input *iam.DeleteUserInput, opts ...func(*iam.Options)) (*iam.DeleteUserOutput, error)
1919
UpdateUser(ctx context.Context, input *iam.UpdateUserInput, opts ...func(*iam.Options)) (*iam.UpdateUserOutput, error)
20+
PutUserPermissionsBoundary(ctx context.Context, params *iam.PutUserPermissionsBoundaryInput, optFns ...func(*iam.Options)) (*iam.PutUserPermissionsBoundaryOutput, error)
21+
DeleteUserPermissionsBoundary(ctx context.Context, params *iam.DeleteUserPermissionsBoundaryInput, optFns ...func(*iam.Options)) (*iam.DeleteUserPermissionsBoundaryOutput, error)
2022
TagUser(ctx context.Context, params *iam.TagUserInput, opts ...func(*iam.Options)) (*iam.TagUserOutput, error)
2123
UntagUser(ctx context.Context, params *iam.UntagUserInput, opts ...func(*iam.Options)) (*iam.UntagUserOutput, error)
2224
}

pkg/controller/iam/role/controller.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,27 @@ func (e *external) Update(ctx context.Context, mgd resource.Managed) (managed.Ex
213213
}
214214
}
215215

216+
boundaryArn := ""
217+
if observed.Role.PermissionsBoundary != nil {
218+
boundaryArn = *observed.Role.PermissionsBoundary.PermissionsBoundaryArn
219+
}
220+
if aws.ToString(&boundaryArn) != aws.ToString(cr.Spec.ForProvider.PermissionsBoundary) {
221+
if aws.ToString(cr.Spec.ForProvider.PermissionsBoundary) == "" {
222+
_, err = e.client.DeleteRolePermissionsBoundary(ctx, &awsiam.DeleteRolePermissionsBoundaryInput{
223+
RoleName: aws.String(meta.GetExternalName(cr)),
224+
})
225+
} else {
226+
_, err = e.client.PutRolePermissionsBoundary(ctx, &awsiam.PutRolePermissionsBoundaryInput{
227+
PermissionsBoundary: cr.Spec.ForProvider.PermissionsBoundary,
228+
RoleName: aws.String(meta.GetExternalName(cr)),
229+
})
230+
}
231+
232+
if err != nil {
233+
return managed.ExternalUpdate{}, awsclient.Wrap(err, errUpdate)
234+
}
235+
}
236+
216237
if patch.AssumeRolePolicyDocument != "" {
217238
_, err = e.client.UpdateAssumeRolePolicy(ctx, &awsiam.UpdateAssumeRolePolicyInput{
218239
PolicyDocument: &cr.Spec.ForProvider.AssumeRolePolicyDocument,

pkg/controller/iam/user/controller.go

Lines changed: 112 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
"github.com/aws/aws-sdk-go-v2/aws"
2323
awsiam "github.com/aws/aws-sdk-go-v2/service/iam"
24+
"github.com/aws/aws-sdk-go-v2/service/iam/types"
2425
"github.com/google/go-cmp/cmp"
2526
"github.com/pkg/errors"
2627

@@ -45,13 +46,15 @@ import (
4546
const (
4647
errUnexpectedObject = "The managed resource is not an IAM User resource"
4748

48-
errGet = "cannot get IAM User"
49-
errCreate = "cannot create the IAM User resource"
50-
errDelete = "cannot delete the IAM User resource"
51-
errUpdate = "cannot update the IAM User resource"
52-
errSDK = "empty IAM User received from IAM API"
53-
errTag = "cannot tag the IAM User resource"
54-
errUntag = "cannot remove tags from the IAM User resource"
49+
errGet = "cannot get IAM User"
50+
errCreate = "cannot create the IAM User resource"
51+
errDelete = "cannot delete the IAM User resource"
52+
errUpdateUser = "cannot update the IAM User resource"
53+
errPutUserPermissionsBoundary = "cannot update the IAM User permission boundary"
54+
errDeleteUserPermissionsBoundary = "cannot delete the IAM User permission boundary"
55+
errSDK = "empty IAM User received from IAM API"
56+
errTag = "cannot tag the IAM User resource"
57+
errUntag = "cannot remove tags from the IAM User resource"
5558

5659
errKubeUpdateFailed = "cannot late initialize IAM User"
5760
)
@@ -135,16 +138,9 @@ func (e *external) Observe(ctx context.Context, mgd resource.Managed) (managed.E
135138
UserID: aws.ToString(user.UserId),
136139
}
137140

138-
crTagMap := make(map[string]string, len(cr.Spec.ForProvider.Tags))
139-
for _, v := range cr.Spec.ForProvider.Tags {
140-
crTagMap[v.Key] = v.Value
141-
}
142-
_, _, areTagsUpdated := iam.DiffIAMTags(crTagMap, observed.User.Tags)
143-
144141
return managed.ExternalObservation{
145-
ResourceExists: true,
146-
ResourceUpToDate: aws.ToString(cr.Spec.ForProvider.Path) == aws.ToString(user.Path) &&
147-
areTagsUpdated,
142+
ResourceExists: true,
143+
ResourceUpToDate: isUpToDate(cr, &user),
148144
}, nil
149145
}
150146

@@ -171,44 +167,29 @@ func (e *external) Update(ctx context.Context, mgd resource.Managed) (managed.Ex
171167
return managed.ExternalUpdate{}, errors.New(errUnexpectedObject)
172168
}
173169

174-
_, err := e.client.UpdateUser(ctx, &awsiam.UpdateUserInput{
175-
NewPath: cr.Spec.ForProvider.Path,
176-
UserName: aws.String(meta.GetExternalName(cr)),
177-
})
178-
179-
if err != nil {
180-
return managed.ExternalUpdate{}, awsclient.Wrap(err, errUpdate)
181-
}
182-
183170
observed, err := e.client.GetUser(ctx, &awsiam.GetUserInput{
184171
UserName: aws.String(meta.GetExternalName(cr)),
185172
})
186173

187174
if err != nil {
188-
return managed.ExternalUpdate{}, awsclient.Wrap(err, errGet)
175+
return managed.ExternalUpdate{}, awsclient.Wrap(resource.Ignore(iam.IsErrorNotFound, err), errGet)
189176
}
190177

191-
add, remove, _ := iam.DiffIAMTagsWithUpdates(cr.Spec.ForProvider.Tags, observed.User.Tags)
192-
193-
if len(add) > 0 {
194-
if _, err := e.client.TagUser(ctx, &awsiam.TagUserInput{
195-
UserName: aws.String(meta.GetExternalName(cr)),
196-
Tags: add,
197-
}); err != nil {
198-
return managed.ExternalUpdate{}, awsclient.Wrap(err, errTag)
199-
}
178+
// take care of changes to path (only call if necessary)
179+
err = e.updateUser(ctx, observed, cr)
180+
if err != nil {
181+
return managed.ExternalUpdate{}, err
200182
}
201183

202-
if len(remove) > 0 {
203-
if _, err := e.client.UntagUser(ctx, &awsiam.UntagUserInput{
204-
TagKeys: remove,
205-
UserName: aws.String(meta.GetExternalName(cr)),
206-
}); err != nil {
207-
return managed.ExternalUpdate{}, awsclient.Wrap(err, errUntag)
208-
}
184+
// take care of changes to PermissionBoundary (only call if necessary)
185+
err = e.updatePermissionsBoundary(ctx, observed, cr)
186+
if err != nil {
187+
return managed.ExternalUpdate{}, err
209188
}
210189

211-
return managed.ExternalUpdate{}, awsclient.Wrap(err, errUpdate)
190+
// take care of changes to Tags
191+
err = e.updateTags(ctx, observed, cr)
192+
return managed.ExternalUpdate{}, err
212193
}
213194

214195
func (e *external) Delete(ctx context.Context, mgd resource.Managed) error {
@@ -258,3 +239,91 @@ func (t *tagger) Initialize(ctx context.Context, mgd resource.Managed) error {
258239
}
259240
return errors.Wrap(t.kube.Update(ctx, cr), errKubeUpdateFailed)
260241
}
242+
243+
func (e *external) updateUser(ctx context.Context, observed *awsiam.GetUserOutput, cr *v1beta1.User) error {
244+
if aws.ToString(observed.User.Path) != aws.ToString(cr.Spec.ForProvider.Path) {
245+
_, err := e.client.UpdateUser(ctx, &awsiam.UpdateUserInput{
246+
NewPath: cr.Spec.ForProvider.Path,
247+
UserName: aws.String(meta.GetExternalName(cr)),
248+
})
249+
250+
return awsclient.Wrap(err, errUpdateUser)
251+
}
252+
253+
return nil
254+
}
255+
256+
func (e *external) updatePermissionsBoundary(ctx context.Context, observed *awsiam.GetUserOutput, cr *v1beta1.User) error {
257+
boundaryArn := ""
258+
var err error
259+
260+
if observed.User.PermissionsBoundary != nil {
261+
boundaryArn = *observed.User.PermissionsBoundary.PermissionsBoundaryArn
262+
}
263+
if aws.ToString(&boundaryArn) != aws.ToString(cr.Spec.ForProvider.PermissionsBoundary) {
264+
// is this a delete?
265+
if aws.ToString(cr.Spec.ForProvider.PermissionsBoundary) == "" {
266+
_, err = e.client.DeleteUserPermissionsBoundary(ctx, &awsiam.DeleteUserPermissionsBoundaryInput{
267+
UserName: aws.String(meta.GetExternalName(cr)),
268+
})
269+
270+
return awsclient.Wrap(err, errDeleteUserPermissionsBoundary)
271+
}
272+
273+
// must be an update
274+
_, err = e.client.PutUserPermissionsBoundary(ctx, &awsiam.PutUserPermissionsBoundaryInput{
275+
PermissionsBoundary: cr.Spec.ForProvider.PermissionsBoundary,
276+
UserName: aws.String(meta.GetExternalName(cr)),
277+
})
278+
279+
return awsclient.Wrap(err, errPutUserPermissionsBoundary)
280+
}
281+
282+
return nil
283+
}
284+
285+
func (e *external) updateTags(ctx context.Context, observed *awsiam.GetUserOutput, cr *v1beta1.User) error {
286+
add, remove, _ := iam.DiffIAMTagsWithUpdates(cr.Spec.ForProvider.Tags, observed.User.Tags)
287+
288+
if len(add) > 0 {
289+
if _, err := e.client.TagUser(ctx, &awsiam.TagUserInput{
290+
UserName: aws.String(meta.GetExternalName(cr)),
291+
Tags: add,
292+
}); err != nil {
293+
return awsclient.Wrap(err, errTag)
294+
}
295+
}
296+
297+
if len(remove) > 0 {
298+
if _, err := e.client.UntagUser(ctx, &awsiam.UntagUserInput{
299+
TagKeys: remove,
300+
UserName: aws.String(meta.GetExternalName(cr)),
301+
}); err != nil {
302+
return awsclient.Wrap(err, errUntag)
303+
}
304+
}
305+
306+
return nil
307+
}
308+
309+
func isUpToDate(cr *v1beta1.User, user *types.User) bool {
310+
// check path
311+
isPathUpdated := aws.ToString(cr.Spec.ForProvider.Path) == aws.ToString(user.Path)
312+
313+
// check tags
314+
crTagMap := make(map[string]string, len(cr.Spec.ForProvider.Tags))
315+
for _, v := range cr.Spec.ForProvider.Tags {
316+
crTagMap[v.Key] = v.Value
317+
}
318+
_, _, areTagsUpdated := iam.DiffIAMTags(crTagMap, user.Tags)
319+
320+
// check permissions boundary
321+
boundaryArn := ""
322+
if user.PermissionsBoundary != nil {
323+
boundaryArn = *user.PermissionsBoundary.PermissionsBoundaryArn
324+
}
325+
isBoundaryUpdated :=
326+
aws.ToString(cr.Spec.ForProvider.PermissionsBoundary) == aws.ToString(&boundaryArn)
327+
328+
return isPathUpdated && areTagsUpdated && isBoundaryUpdated
329+
}

0 commit comments

Comments
 (0)