diff --git a/pkg/clients/iam/iam_client_test.go b/pkg/clients/iam/iam_client_test.go index 4c6a7d7..bcd35c5 100644 --- a/pkg/clients/iam/iam_client_test.go +++ b/pkg/clients/iam/iam_client_test.go @@ -2,6 +2,7 @@ package iamclient_test import ( "context" + "errors" "fmt" "testing" @@ -12,6 +13,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/aws/smithy-go" iamclient "github.com/scality/cosi-driver/pkg/clients/iam" "github.com/scality/cosi-driver/pkg/mock" "github.com/scality/cosi-driver/pkg/util" @@ -276,14 +278,20 @@ var _ = Describe("IAMClient", func() { Describe("RevokeBucketAccess", func() { var mockIAM *mock.MockIAMClient + noSuchEntityError := &types.NoSuchEntityException{} + accessDeniedError := &smithy.GenericAPIError{ + Code: "AccessDenied", + Message: "Access Denied", + } BeforeEach(func() { mockIAM = &mock.MockIAMClient{} }) It("should fail on non-existent user gracefully", func(ctx SpecContext) { + mockIAM.GetUserFunc = func(ctx context.Context, input *iam.GetUserInput, opts ...func(*iam.Options)) (*iam.GetUserOutput, error) { - return nil, &types.NoSuchEntityException{} + return nil, noSuchEntityError } client, _ := iamclient.InitIAMClient(params) @@ -292,11 +300,12 @@ var _ = Describe("IAMClient", func() { err := client.RevokeBucketAccess(ctx, "non-existent-user", "test-bucket") Expect(err).NotTo(BeNil()) Expect(err.Error()).To(ContainSubstring("failed to get IAM user non-existent-user")) + Expect(errors.As(err, &noSuchEntityError)).To(BeTrue()) }) It("should return an error if getting user fails", func(ctx SpecContext) { mockIAM.GetUserFunc = func(ctx context.Context, input *iam.GetUserInput, opts ...func(*iam.Options)) (*iam.GetUserOutput, error) { - return nil, fmt.Errorf("simulated GetUser failure") + return nil, noSuchEntityError } client, _ := iamclient.InitIAMClient(params) @@ -304,12 +313,12 @@ var _ = Describe("IAMClient", func() { err := client.RevokeBucketAccess(ctx, "test-user", "test-bucket") Expect(err).NotTo(BeNil()) - Expect(err.Error()).To(ContainSubstring("failed to get IAM user test-user")) + Expect(errors.As(err, &noSuchEntityError)).To(BeTrue()) }) It("should skip deletion if inline policy does not exist", func(ctx SpecContext) { mockIAM.DeleteUserPolicyFunc = func(ctx context.Context, input *iam.DeleteUserPolicyInput, opts ...func(*iam.Options)) (*iam.DeleteUserPolicyOutput, error) { - return nil, &types.NoSuchEntityException{} + return nil, noSuchEntityError } client, _ := iamclient.InitIAMClient(params) @@ -321,7 +330,7 @@ var _ = Describe("IAMClient", func() { It("should return an error if deleting inline policy fails", func(ctx SpecContext) { mockIAM.DeleteUserPolicyFunc = func(ctx context.Context, input *iam.DeleteUserPolicyInput, opts ...func(*iam.Options)) (*iam.DeleteUserPolicyOutput, error) { - return nil, fmt.Errorf("simulated DeleteUserPolicy failure") + return nil, accessDeniedError } client, _ := iamclient.InitIAMClient(params) @@ -329,7 +338,19 @@ var _ = Describe("IAMClient", func() { err := client.RevokeBucketAccess(ctx, "test-user", "test-bucket") Expect(err).NotTo(BeNil()) - Expect(err.Error()).To(ContainSubstring("failed to delete inline policy test-bucket for user test-user")) + Expect(errors.As(err, &accessDeniedError)).To(BeTrue()) + }) + + It("should not return an error if inline policy is not found", func(ctx SpecContext) { + mockIAM.DeleteUserPolicyFunc = func(ctx context.Context, input *iam.DeleteUserPolicyInput, opts ...func(*iam.Options)) (*iam.DeleteUserPolicyOutput, error) { + return nil, &types.NoSuchEntityException{} + } + + client, _ := iamclient.InitIAMClient(params) + client.IAMService = mockIAM + + err := client.RevokeBucketAccess(ctx, "test-user", "test-bucket") + Expect(err).To(BeNil()) }) It("should successfully delete all access keys for the user", func(ctx SpecContext) { @@ -341,18 +362,8 @@ var _ = Describe("IAMClient", func() { }) It("should return an error if deleting access key fails", func(ctx SpecContext) { - mockIAM.GetUserFunc = func(ctx context.Context, input *iam.GetUserInput, opts ...func(*iam.Options)) (*iam.GetUserOutput, error) { - return &iam.GetUserOutput{}, nil - } - mockIAM.ListAccessKeysFunc = func(ctx context.Context, input *iam.ListAccessKeysInput, opts ...func(*iam.Options)) (*iam.ListAccessKeysOutput, error) { - return &iam.ListAccessKeysOutput{ - AccessKeyMetadata: []types.AccessKeyMetadata{ - {AccessKeyId: aws.String("key-1")}, - }, - }, nil - } mockIAM.DeleteAccessKeyFunc = func(ctx context.Context, input *iam.DeleteAccessKeyInput, opts ...func(*iam.Options)) (*iam.DeleteAccessKeyOutput, error) { - return nil, fmt.Errorf("simulated DeleteAccessKey failure") + return nil, accessDeniedError } client, _ := iamclient.InitIAMClient(params) @@ -360,7 +371,19 @@ var _ = Describe("IAMClient", func() { err := client.RevokeBucketAccess(ctx, "test-user", "test-bucket") Expect(err).NotTo(BeNil()) - Expect(err.Error()).To(ContainSubstring("failed to delete access key key-1 for IAM user test-user")) + Expect(errors.As(err, &accessDeniedError)).To(BeTrue()) + }) + + It("should not return an error if access key is not found", func(ctx SpecContext) { + mockIAM.DeleteAccessKeyFunc = func(ctx context.Context, input *iam.DeleteAccessKeyInput, opts ...func(*iam.Options)) (*iam.DeleteAccessKeyOutput, error) { + return nil, noSuchEntityError + } + + client, _ := iamclient.InitIAMClient(params) + client.IAMService = mockIAM + + err := client.RevokeBucketAccess(ctx, "test-user", "test-bucket") + Expect(err).To(BeNil()) }) It("should successfully delete the user", func(ctx SpecContext) { @@ -373,7 +396,7 @@ var _ = Describe("IAMClient", func() { It("should return an error if deleting user fails", func(ctx SpecContext) { mockIAM.DeleteUserFunc = func(ctx context.Context, input *iam.DeleteUserInput, opts ...func(*iam.Options)) (*iam.DeleteUserOutput, error) { - return nil, fmt.Errorf("simulated DeleteUser failure") + return nil, accessDeniedError } client, _ := iamclient.InitIAMClient(params) @@ -381,7 +404,7 @@ var _ = Describe("IAMClient", func() { err := client.RevokeBucketAccess(ctx, "test-user", "test-bucket") Expect(err).NotTo(BeNil()) - Expect(err.Error()).To(ContainSubstring("failed to delete IAM user test-user")) + Expect(errors.As(err, &accessDeniedError)).To(BeTrue()) }) It("should successfully delete an inline policy", func(ctx SpecContext) { @@ -418,18 +441,6 @@ var _ = Describe("IAMClient", func() { Expect(err).To(BeNil()) }) - It("should return an error if deleting any access key fails", func(ctx SpecContext) { - mockIAM.DeleteAccessKeyFunc = func(ctx context.Context, input *iam.DeleteAccessKeyInput, opts ...func(*iam.Options)) (*iam.DeleteAccessKeyOutput, error) { - return nil, fmt.Errorf("simulated DeleteAccessKey failure") - } - - client, _ := iamclient.InitIAMClient(params) - client.IAMService = mockIAM - - err := client.DeleteAllAccessKeys(ctx, "test-user") - Expect(err).NotTo(BeNil()) - Expect(err.Error()).To(ContainSubstring("simulated DeleteAccessKey failure")) - }) It("should successfully delete a user", func(ctx SpecContext) { mockIAM.DeleteUserFunc = func(ctx context.Context, input *iam.DeleteUserInput, opts ...func(*iam.Options)) (*iam.DeleteUserOutput, error) { Expect(*input.UserName).To(Equal("test-user")) @@ -456,19 +467,16 @@ var _ = Describe("IAMClient", func() { }) It("should return an error if listing access keys fails", func(ctx SpecContext) { - mockIAM := &mock.MockIAMClient{} mockIAM.ListAccessKeysFunc = func(ctx context.Context, input *iam.ListAccessKeysInput, opts ...func(*iam.Options)) (*iam.ListAccessKeysOutput, error) { - return nil, fmt.Errorf("simulated ListAccessKeys failure") + return nil, accessDeniedError } - client := &iamclient.IAMClient{ - IAMService: mockIAM, - } + client, _ := iamclient.InitIAMClient(params) + client.IAMService = mockIAM err := client.DeleteAllAccessKeys(ctx, "test-user") - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("failed to list access keys for IAM user test-user")) - Expect(err.Error()).To(ContainSubstring("simulated ListAccessKeys failure")) + Expect(err).NotTo(BeNil()) + Expect(errors.As(err, &accessDeniedError)).To(BeTrue()) }) }) })