mirror of
https://github.com/versity/versitygw.git
synced 2026-09-24 00:44:23 +00:00
fix: infer UserName from the calling access key in the access-key APIs
`CreateAccessKey`, `UpdateAccessKey`, `DeleteAccessKey` and `ListAccessKeys` required an explicit `UserName`. Real IAM treats it as optional and resolves it from the access key signing the request, so an IAM user managing its own keys had to name itself. They now infer it, matching AWS: only an entirely absent parameter is inferred, while a present-but-empty one stays a `ValidationError`, and the inferred scope is strictly the caller's own user — another user's key id returns `NoSuchEntity` rather than being touched. A caller with no IAM user of its own gets IAM's own `Must specify userName when calling with non-User credentials` `ValidationError`, shared with `GetUser` as `iamerr.MustSpecifyUserName`. That covers assumed-role sessions and also the gateway's root credential, which is configured rather than stored as an IAM user and so owns no access keys the API could manage — real IAM manages the root account's own keys here, which has no equivalent in this gateway. The policy middleware resolves the same four actions through `callerOrNamedUserResource`, so the resource-level check targets the caller's own user ARN when UserName is omitted instead of falling back to no resource at all, which would have denied every request authorized by an own-ARN-scoped grant.
This commit is contained in:
@@ -1243,6 +1243,8 @@ func TestIAMListUserTags(ts *TestState) {
|
||||
|
||||
func TestIAMCreateAccessKey(ts *TestState) {
|
||||
ts.Run(IAMCreateAccessKey_missing_user_name)
|
||||
ts.Run(IAMCreateAccessKey_empty_user_name)
|
||||
ts.Run(IAMCreateAccessKey_infers_caller_user_name)
|
||||
ts.Run(IAMCreateAccessKey_invalid_user_name)
|
||||
ts.Run(IAMCreateAccessKey_long_user_name)
|
||||
ts.Run(IAMCreateAccessKey_non_existing_user)
|
||||
@@ -1252,6 +1254,8 @@ func TestIAMCreateAccessKey(ts *TestState) {
|
||||
|
||||
func TestIAMUpdateAccessKey(ts *TestState) {
|
||||
ts.Run(IAMUpdateAccessKey_missing_user_name)
|
||||
ts.Run(IAMUpdateAccessKey_infers_caller_user_name)
|
||||
ts.Run(IAMUpdateAccessKey_inferred_user_name_scoped_to_caller)
|
||||
ts.Run(IAMUpdateAccessKey_invalid_user_name)
|
||||
ts.Run(IAMUpdateAccessKey_long_user_name)
|
||||
ts.Run(IAMUpdateAccessKey_missing_access_key_id)
|
||||
@@ -1267,6 +1271,8 @@ func TestIAMUpdateAccessKey(ts *TestState) {
|
||||
|
||||
func TestIAMDeleteAccessKey(ts *TestState) {
|
||||
ts.Run(IAMDeleteAccessKey_missing_user_name)
|
||||
ts.Run(IAMDeleteAccessKey_infers_caller_user_name)
|
||||
ts.Run(IAMDeleteAccessKey_inferred_user_name_scoped_to_caller)
|
||||
ts.Run(IAMDeleteAccessKey_invalid_user_name)
|
||||
ts.Run(IAMDeleteAccessKey_long_user_name)
|
||||
ts.Run(IAMDeleteAccessKey_missing_access_key_id)
|
||||
@@ -1289,6 +1295,7 @@ func TestIAMGetAccessKeyLastUsed(ts *TestState) {
|
||||
|
||||
func TestIAMListAccessKeys(ts *TestState) {
|
||||
ts.Run(IAMListAccessKeys_missing_user_name)
|
||||
ts.Run(IAMListAccessKeys_infers_caller_user_name)
|
||||
ts.Run(IAMListAccessKeys_invalid_user_name)
|
||||
ts.Run(IAMListAccessKeys_long_user_name)
|
||||
ts.Run(IAMListAccessKeys_invalid_max_items)
|
||||
@@ -2327,12 +2334,16 @@ func GetIntTests() IntTests {
|
||||
"IAMListUserTags_success": IAMListUserTags_success,
|
||||
"IAMListUserTags_pagination": IAMListUserTags_pagination,
|
||||
"IAMCreateAccessKey_missing_user_name": IAMCreateAccessKey_missing_user_name,
|
||||
"IAMCreateAccessKey_empty_user_name": IAMCreateAccessKey_empty_user_name,
|
||||
"IAMCreateAccessKey_infers_caller_user_name": IAMCreateAccessKey_infers_caller_user_name,
|
||||
"IAMCreateAccessKey_invalid_user_name": IAMCreateAccessKey_invalid_user_name,
|
||||
"IAMCreateAccessKey_long_user_name": IAMCreateAccessKey_long_user_name,
|
||||
"IAMCreateAccessKey_non_existing_user": IAMCreateAccessKey_non_existing_user,
|
||||
"IAMCreateAccessKey_limit_exceeded": IAMCreateAccessKey_limit_exceeded,
|
||||
"IAMCreateAccessKey_success": IAMCreateAccessKey_success,
|
||||
"IAMUpdateAccessKey_missing_user_name": IAMUpdateAccessKey_missing_user_name,
|
||||
"IAMUpdateAccessKey_infers_caller_user_name": IAMUpdateAccessKey_infers_caller_user_name,
|
||||
"IAMUpdateAccessKey_inferred_user_name_scoped_to_caller": IAMUpdateAccessKey_inferred_user_name_scoped_to_caller,
|
||||
"IAMUpdateAccessKey_invalid_user_name": IAMUpdateAccessKey_invalid_user_name,
|
||||
"IAMUpdateAccessKey_long_user_name": IAMUpdateAccessKey_long_user_name,
|
||||
"IAMUpdateAccessKey_missing_access_key_id": IAMUpdateAccessKey_missing_access_key_id,
|
||||
@@ -2345,6 +2356,8 @@ func GetIntTests() IntTests {
|
||||
"IAMUpdateAccessKey_non_existing_access_key": IAMUpdateAccessKey_non_existing_access_key,
|
||||
"IAMUpdateAccessKey_success": IAMUpdateAccessKey_success,
|
||||
"IAMDeleteAccessKey_missing_user_name": IAMDeleteAccessKey_missing_user_name,
|
||||
"IAMDeleteAccessKey_infers_caller_user_name": IAMDeleteAccessKey_infers_caller_user_name,
|
||||
"IAMDeleteAccessKey_inferred_user_name_scoped_to_caller": IAMDeleteAccessKey_inferred_user_name_scoped_to_caller,
|
||||
"IAMDeleteAccessKey_invalid_user_name": IAMDeleteAccessKey_invalid_user_name,
|
||||
"IAMDeleteAccessKey_long_user_name": IAMDeleteAccessKey_long_user_name,
|
||||
"IAMDeleteAccessKey_missing_access_key_id": IAMDeleteAccessKey_missing_access_key_id,
|
||||
@@ -2361,6 +2374,7 @@ func GetIntTests() IntTests {
|
||||
"IAMGetAccessKeyLastUsed_non_existing_access_key": IAMGetAccessKeyLastUsed_non_existing_access_key,
|
||||
"IAMGetAccessKeyLastUsed_success": IAMGetAccessKeyLastUsed_success,
|
||||
"IAMListAccessKeys_missing_user_name": IAMListAccessKeys_missing_user_name,
|
||||
"IAMListAccessKeys_infers_caller_user_name": IAMListAccessKeys_infers_caller_user_name,
|
||||
"IAMListAccessKeys_invalid_user_name": IAMListAccessKeys_invalid_user_name,
|
||||
"IAMListAccessKeys_long_user_name": IAMListAccessKeys_long_user_name,
|
||||
"IAMListAccessKeys_invalid_max_items": IAMListAccessKeys_invalid_max_items,
|
||||
|
||||
@@ -17,8 +17,11 @@ package integration
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
|
||||
@@ -29,11 +32,57 @@ import (
|
||||
|
||||
var integrationIAMAccessKeyIDPattern = regexp.MustCompile(`^AKIA[A-Z2-7]{17}$`)
|
||||
|
||||
// IAMCreateAccessKey_missing_user_name calls as root, which is a configured
|
||||
// credential rather than a stored IAM user and so has no user name to infer
|
||||
// — unlike a caller signing with an IAM user's own access key, covered by
|
||||
// IAMCreateAccessKey_infers_caller_user_name.
|
||||
func IAMCreateAccessKey_missing_user_name(s *S3Conf) error {
|
||||
testName := "IAMCreateAccessKey_missing_user_name"
|
||||
return iamActionHandler(s, testName, func(client *iam.Client) error {
|
||||
_, err := createIAMAccessKey(client, &iam.CreateAccessKeyInput{})
|
||||
return checkIAMApiErr(err, iamerr.MissingParameter("UserName"))
|
||||
return checkIAMApiErr(err, iamerr.MustSpecifyUserName())
|
||||
})
|
||||
}
|
||||
|
||||
// IAMCreateAccessKey_empty_user_name confirms only an entirely absent
|
||||
// UserName is inferred: sending the parameter with an empty value stays a
|
||||
// validation failure rather than silently creating a key for the caller.
|
||||
func IAMCreateAccessKey_empty_user_name(s *S3Conf) error {
|
||||
testName := "IAMCreateAccessKey_empty_user_name"
|
||||
body := []byte(url.Values{
|
||||
"Action": {"CreateAccessKey"},
|
||||
"Version": {"2010-05-08"},
|
||||
"UserName": {""},
|
||||
}.Encode())
|
||||
return authHandler(s, &authConfig{
|
||||
testName: testName,
|
||||
method: http.MethodPost,
|
||||
service: "iam",
|
||||
region: iamAuthRegion,
|
||||
body: body,
|
||||
date: time.Now().UTC(),
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
}, func(req *http.Request) error {
|
||||
return checkIAMAuthRequest(s, req, iamerr.InvalidUserName("userName"))
|
||||
})
|
||||
}
|
||||
|
||||
func IAMCreateAccessKey_infers_caller_user_name(s *S3Conf) error {
|
||||
testName := "IAMCreateAccessKey_infers_caller_user_name"
|
||||
return iamActionHandler(s, testName, func(root *iam.Client) error {
|
||||
caller, cleanup, err := newAccessKeyCaller(root, s, "iam:CreateAccessKey")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
out, err := createIAMAccessKey(caller.client, &iam.CreateAccessKeyInput{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return checkCreateAccessKeyOutput(out, caller.userName)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -149,3 +198,20 @@ func checkCreateAccessKeyOutput(out *iam.CreateAccessKeyOutput, userName string)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// newAccessKeyCaller creates an IAM user with one access key and an inline
|
||||
// policy granting actions on its own user ARN, plus an *iam.Client signing
|
||||
// as that user — the fixture every "UserName inferred from the calling
|
||||
// access key" test in the access-key files needs. Scoping the grant to the
|
||||
// caller's own ARN (rather than "*") means a passing test also proves the
|
||||
// inferred name reaches the resource-level authorization check, not just
|
||||
// the controller.
|
||||
func newAccessKeyCaller(root *iam.Client, s *S3Conf, actions ...string) (*accessControlCaller, func(), error) {
|
||||
userName := newIAMUserName()
|
||||
grant := policyDoc(accessStatement{
|
||||
Effect: "Allow",
|
||||
Action: actions,
|
||||
Resource: "arn:aws:iam::" + testAccountID + ":user/" + userName,
|
||||
})
|
||||
return newAccessControlCaller(root, s, userName, map[string]string{"self-access-keys": grant})
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -23,14 +24,89 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/iam"
|
||||
iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
|
||||
"github.com/versity/versitygw/iamapi/iamerr"
|
||||
)
|
||||
|
||||
// IAMDeleteAccessKey_missing_user_name calls as root, which is a configured
|
||||
// credential rather than a stored IAM user and so has no user name to infer
|
||||
// — unlike a caller signing with an IAM user's own access key, covered by
|
||||
// IAMDeleteAccessKey_infers_caller_user_name.
|
||||
func IAMDeleteAccessKey_missing_user_name(s *S3Conf) error {
|
||||
testName := "IAMDeleteAccessKey_missing_user_name"
|
||||
return iamActionHandler(s, testName, func(client *iam.Client) error {
|
||||
err := deleteIAMAccessKey(client, "", genRandString(20))
|
||||
return checkIAMApiErr(err, iamerr.MissingParameter("UserName"))
|
||||
return checkIAMApiErr(err, iamerr.MustSpecifyUserName())
|
||||
})
|
||||
}
|
||||
|
||||
func IAMDeleteAccessKey_infers_caller_user_name(s *S3Conf) error {
|
||||
testName := "IAMDeleteAccessKey_infers_caller_user_name"
|
||||
return iamActionHandler(s, testName, func(root *iam.Client) error {
|
||||
caller, cleanup, err := newAccessKeyCaller(root, s, "iam:DeleteAccessKey")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
created, err := createIAMAccessKey(root, &iam.CreateAccessKeyInput{UserName: &caller.userName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accessKeyID := aws.ToString(created.AccessKey.AccessKeyId)
|
||||
|
||||
if err := deleteIAMAccessKey(caller.client, "", accessKeyID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listOut, err := listIAMAccessKeys(root, &iam.ListAccessKeysInput{UserName: &caller.userName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, key := range listOut.AccessKeyMetadata {
|
||||
if aws.ToString(key.AccessKeyId) == accessKeyID {
|
||||
return fmt.Errorf("expected access key %q to be deleted", accessKeyID)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// IAMDeleteAccessKey_inferred_user_name_scoped_to_caller confirms the
|
||||
// inferred user name is the caller's own and nothing else: another user's
|
||||
// access key is simply not found, rather than being deleted.
|
||||
func IAMDeleteAccessKey_inferred_user_name_scoped_to_caller(s *S3Conf) error {
|
||||
testName := "IAMDeleteAccessKey_inferred_user_name_scoped_to_caller"
|
||||
return iamActionHandler(s, testName, func(root *iam.Client) error {
|
||||
caller, cleanup, err := newAccessKeyCaller(root, s, "iam:DeleteAccessKey")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
otherName := newIAMUserName()
|
||||
if _, err := createIAMUser(root, &iam.CreateUserInput{UserName: &otherName}); err != nil {
|
||||
return err
|
||||
}
|
||||
defer deleteIAMUserAndAccessKeys(root, otherName)
|
||||
|
||||
otherKey, err := createIAMAccessKey(root, &iam.CreateAccessKeyInput{UserName: &otherName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
otherKeyID := aws.ToString(otherKey.AccessKey.AccessKeyId)
|
||||
|
||||
err = deleteIAMAccessKey(caller.client, "", otherKeyID)
|
||||
if err := checkIAMApiErr(err, iamerr.NoSuchEntityAccessKey(otherKeyID)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listOut, err := listIAMAccessKeys(root, &iam.ListAccessKeysInput{UserName: &otherName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return checkIAMListAccessKeys(listOut.AccessKeyMetadata, otherName,
|
||||
map[string]iamtypes.StatusType{otherKeyID: iamtypes.StatusTypeActive})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,64 @@ import (
|
||||
"github.com/versity/versitygw/iamapi/iamerr"
|
||||
)
|
||||
|
||||
// IAMListAccessKeys_missing_user_name calls as root, which is a configured
|
||||
// credential rather than a stored IAM user and so has no user name to infer
|
||||
// — unlike a caller signing with an IAM user's own access key, covered by
|
||||
// IAMListAccessKeys_infers_caller_user_name.
|
||||
func IAMListAccessKeys_missing_user_name(s *S3Conf) error {
|
||||
testName := "IAMListAccessKeys_missing_user_name"
|
||||
return iamActionHandler(s, testName, func(client *iam.Client) error {
|
||||
_, err := listIAMAccessKeys(client, &iam.ListAccessKeysInput{})
|
||||
return checkIAMApiErr(err, iamerr.MissingParameter("UserName"))
|
||||
return checkIAMApiErr(err, iamerr.MustSpecifyUserName())
|
||||
})
|
||||
}
|
||||
|
||||
// IAMListAccessKeys_infers_caller_user_name also covers the scoping the
|
||||
// other inferred-user-name tests assert through a NoSuchEntity: a listing
|
||||
// with no UserName returns the caller's own keys and only those, so a
|
||||
// second user's key must not appear in it.
|
||||
func IAMListAccessKeys_infers_caller_user_name(s *S3Conf) error {
|
||||
testName := "IAMListAccessKeys_infers_caller_user_name"
|
||||
return iamActionHandler(s, testName, func(root *iam.Client) error {
|
||||
caller, cleanup, err := newAccessKeyCaller(root, s, "iam:ListAccessKeys")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
if _, err := createIAMAccessKey(root, &iam.CreateAccessKeyInput{UserName: &caller.userName}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
otherName := newIAMUserName()
|
||||
if _, err := createIAMUser(root, &iam.CreateUserInput{UserName: &otherName}); err != nil {
|
||||
return err
|
||||
}
|
||||
defer deleteIAMUserAndAccessKeys(root, otherName)
|
||||
if _, err := createIAMAccessKey(root, &iam.CreateAccessKeyInput{UserName: &otherName}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
callerKeys, err := listIAMAccessKeys(root, &iam.ListAccessKeysInput{UserName: &caller.userName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(callerKeys.AccessKeyMetadata) != 2 {
|
||||
return fmt.Errorf("expected the caller to own 2 access keys, instead got %d", len(callerKeys.AccessKeyMetadata))
|
||||
}
|
||||
expected := make(map[string]iamtypes.StatusType, len(callerKeys.AccessKeyMetadata))
|
||||
for _, key := range callerKeys.AccessKeyMetadata {
|
||||
expected[aws.ToString(key.AccessKeyId)] = key.Status
|
||||
}
|
||||
|
||||
out, err := listIAMAccessKeys(caller.client, &iam.ListAccessKeysInput{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if out.IsTruncated {
|
||||
return fmt.Errorf("expected ListAccessKeys not to be truncated")
|
||||
}
|
||||
return checkIAMListAccessKeys(out.AccessKeyMetadata, caller.userName, expected)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@ import (
|
||||
"github.com/versity/versitygw/iamapi/iamerr"
|
||||
)
|
||||
|
||||
// IAMUpdateAccessKey_missing_user_name calls as root, which is a configured
|
||||
// credential rather than a stored IAM user and so has no user name to infer
|
||||
// — unlike a caller signing with an IAM user's own access key, covered by
|
||||
// IAMUpdateAccessKey_infers_caller_user_name.
|
||||
func IAMUpdateAccessKey_missing_user_name(s *S3Conf) error {
|
||||
testName := "IAMUpdateAccessKey_missing_user_name"
|
||||
return iamActionHandler(s, testName, func(client *iam.Client) error {
|
||||
@@ -36,7 +40,87 @@ func IAMUpdateAccessKey_missing_user_name(s *S3Conf) error {
|
||||
AccessKeyId: aws.String(genRandString(20)),
|
||||
Status: iamtypes.StatusTypeActive,
|
||||
})
|
||||
return checkIAMApiErr(err, iamerr.MissingParameter("UserName"))
|
||||
return checkIAMApiErr(err, iamerr.MustSpecifyUserName())
|
||||
})
|
||||
}
|
||||
|
||||
func IAMUpdateAccessKey_infers_caller_user_name(s *S3Conf) error {
|
||||
testName := "IAMUpdateAccessKey_infers_caller_user_name"
|
||||
return iamActionHandler(s, testName, func(root *iam.Client) error {
|
||||
caller, cleanup, err := newAccessKeyCaller(root, s, "iam:UpdateAccessKey")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
created, err := createIAMAccessKey(root, &iam.CreateAccessKeyInput{UserName: &caller.userName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accessKeyID := aws.ToString(created.AccessKey.AccessKeyId)
|
||||
|
||||
if _, err := updateIAMAccessKey(caller.client, &iam.UpdateAccessKeyInput{
|
||||
AccessKeyId: &accessKeyID,
|
||||
Status: iamtypes.StatusTypeInactive,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listOut, err := listIAMAccessKeys(root, &iam.ListAccessKeysInput{UserName: &caller.userName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, key := range listOut.AccessKeyMetadata {
|
||||
want := iamtypes.StatusTypeActive
|
||||
if aws.ToString(key.AccessKeyId) == accessKeyID {
|
||||
want = iamtypes.StatusTypeInactive
|
||||
}
|
||||
if key.Status != want {
|
||||
return fmt.Errorf("expected access key %q status %q, instead got %q", aws.ToString(key.AccessKeyId), want, key.Status)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// IAMUpdateAccessKey_inferred_user_name_scoped_to_caller confirms the
|
||||
// inferred user name is the caller's own and nothing else: another user's
|
||||
// access key is simply not found, rather than being updated.
|
||||
func IAMUpdateAccessKey_inferred_user_name_scoped_to_caller(s *S3Conf) error {
|
||||
testName := "IAMUpdateAccessKey_inferred_user_name_scoped_to_caller"
|
||||
return iamActionHandler(s, testName, func(root *iam.Client) error {
|
||||
caller, cleanup, err := newAccessKeyCaller(root, s, "iam:UpdateAccessKey")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
otherName := newIAMUserName()
|
||||
if _, err := createIAMUser(root, &iam.CreateUserInput{UserName: &otherName}); err != nil {
|
||||
return err
|
||||
}
|
||||
defer deleteIAMUserAndAccessKeys(root, otherName)
|
||||
|
||||
otherKey, err := createIAMAccessKey(root, &iam.CreateAccessKeyInput{UserName: &otherName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
otherKeyID := aws.ToString(otherKey.AccessKey.AccessKeyId)
|
||||
|
||||
_, err = updateIAMAccessKey(caller.client, &iam.UpdateAccessKeyInput{
|
||||
AccessKeyId: &otherKeyID,
|
||||
Status: iamtypes.StatusTypeInactive,
|
||||
})
|
||||
if err := checkIAMApiErr(err, iamerr.NoSuchEntityAccessKey(otherKeyID)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listOut, err := listIAMAccessKeys(root, &iam.ListAccessKeysInput{UserName: &otherName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return checkIAMListAccessKeys(listOut.AccessKeyMetadata, otherName,
|
||||
map[string]iamtypes.StatusType{otherKeyID: iamtypes.StatusTypeActive})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user