Add UpdateAccessKey support to IAM API (#8342)
* Add UpdateAccessKey support to IAM API * simplify
This commit is contained in:
@@ -34,7 +34,7 @@ func writeIamErrorResponse(w http.ResponseWriter, r *http.Request, iamError *Iam
|
|||||||
switch errCode {
|
switch errCode {
|
||||||
case iam.ErrCodeNoSuchEntityException:
|
case iam.ErrCodeNoSuchEntityException:
|
||||||
s3err.WriteXMLResponse(w, r, http.StatusNotFound, errorResp)
|
s3err.WriteXMLResponse(w, r, http.StatusNotFound, errorResp)
|
||||||
case iam.ErrCodeMalformedPolicyDocumentException:
|
case iam.ErrCodeMalformedPolicyDocumentException, iam.ErrCodeInvalidInputException:
|
||||||
s3err.WriteXMLResponse(w, r, http.StatusBadRequest, errorResp)
|
s3err.WriteXMLResponse(w, r, http.StatusBadRequest, errorResp)
|
||||||
case iam.ErrCodeServiceFailureException:
|
case iam.ErrCodeServiceFailureException:
|
||||||
// We do not want to expose internal server error to the client
|
// We do not want to expose internal server error to the client
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ const (
|
|||||||
StatementActionTagging = iamlib.StatementActionTagging
|
StatementActionTagging = iamlib.StatementActionTagging
|
||||||
StatementActionDelete = iamlib.StatementActionDelete
|
StatementActionDelete = iamlib.StatementActionDelete
|
||||||
USER_DOES_NOT_EXIST = iamlib.UserDoesNotExist
|
USER_DOES_NOT_EXIST = iamlib.UserDoesNotExist
|
||||||
|
accessKeyStatusActive = iamlib.AccessKeyStatusActive
|
||||||
|
accessKeyStatusInactive = iamlib.AccessKeyStatusInactive
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -67,6 +69,17 @@ func stringSlicesEqual(a, b []string) bool {
|
|||||||
return iamlib.StringSlicesEqual(a, b)
|
return iamlib.StringSlicesEqual(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateAccessKeyStatus(status string) error {
|
||||||
|
switch status {
|
||||||
|
case accessKeyStatusActive, accessKeyStatusInactive:
|
||||||
|
return nil
|
||||||
|
case "":
|
||||||
|
return fmt.Errorf("Status parameter is required")
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Status must be '%s' or '%s'", accessKeyStatusActive, accessKeyStatusInactive)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (iama *IamApiServer) ListUsers(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp ListUsersResponse) {
|
func (iama *IamApiServer) ListUsers(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp ListUsersResponse) {
|
||||||
for _, ident := range s3cfg.Identities {
|
for _, ident := range s3cfg.Identities {
|
||||||
resp.ListUsersResult.Users = append(resp.ListUsersResult.Users, &iam.User{UserName: &ident.Name})
|
resp.ListUsersResult.Users = append(resp.ListUsersResult.Users, &iam.User{UserName: &ident.Name})
|
||||||
@@ -75,15 +88,20 @@ func (iama *IamApiServer) ListUsers(s3cfg *iam_pb.S3ApiConfiguration, values url
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (iama *IamApiServer) ListAccessKeys(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp ListAccessKeysResponse) {
|
func (iama *IamApiServer) ListAccessKeys(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp ListAccessKeysResponse) {
|
||||||
status := iam.StatusTypeActive
|
|
||||||
userName := values.Get("UserName")
|
userName := values.Get("UserName")
|
||||||
for _, ident := range s3cfg.Identities {
|
for _, ident := range s3cfg.Identities {
|
||||||
if userName != "" && userName != ident.Name {
|
if userName != "" && userName != ident.Name {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, cred := range ident.Credentials {
|
for _, cred := range ident.Credentials {
|
||||||
|
status := cred.Status
|
||||||
|
if status == "" {
|
||||||
|
status = accessKeyStatusActive
|
||||||
|
}
|
||||||
|
identName := ident.Name
|
||||||
|
accessKey := cred.AccessKey
|
||||||
resp.ListAccessKeysResult.AccessKeyMetadata = append(resp.ListAccessKeysResult.AccessKeyMetadata,
|
resp.ListAccessKeysResult.AccessKeyMetadata = append(resp.ListAccessKeysResult.AccessKeyMetadata,
|
||||||
&iam.AccessKeyMetadata{UserName: &ident.Name, AccessKeyId: &cred.AccessKey, Status: &status},
|
&iam.AccessKeyMetadata{UserName: &identName, AccessKeyId: &accessKey, Status: &status},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,7 +343,7 @@ func (iama *IamApiServer) CreateAccessKey(s3cfg *iam_pb.S3ApiConfiguration, valu
|
|||||||
for _, ident := range s3cfg.Identities {
|
for _, ident := range s3cfg.Identities {
|
||||||
if userName == ident.Name {
|
if userName == ident.Name {
|
||||||
ident.Credentials = append(ident.Credentials,
|
ident.Credentials = append(ident.Credentials,
|
||||||
&iam_pb.Credential{AccessKey: accessKeyId, SecretKey: secretAccessKey})
|
&iam_pb.Credential{AccessKey: accessKeyId, SecretKey: secretAccessKey, Status: accessKeyStatusActive})
|
||||||
changed = true
|
changed = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -338,6 +356,7 @@ func (iama *IamApiServer) CreateAccessKey(s3cfg *iam_pb.S3ApiConfiguration, valu
|
|||||||
{
|
{
|
||||||
AccessKey: accessKeyId,
|
AccessKey: accessKeyId,
|
||||||
SecretKey: secretAccessKey,
|
SecretKey: secretAccessKey,
|
||||||
|
Status: accessKeyStatusActive,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -346,6 +365,37 @@ func (iama *IamApiServer) CreateAccessKey(s3cfg *iam_pb.S3ApiConfiguration, valu
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateAccessKey updates the status of an access key (Active or Inactive).
|
||||||
|
func (iama *IamApiServer) UpdateAccessKey(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp UpdateAccessKeyResponse, err *IamError) {
|
||||||
|
userName := values.Get("UserName")
|
||||||
|
accessKeyId := values.Get("AccessKeyId")
|
||||||
|
status := values.Get("Status")
|
||||||
|
|
||||||
|
if userName == "" {
|
||||||
|
return resp, &IamError{Code: iam.ErrCodeInvalidInputException, Error: fmt.Errorf("UserName is required")}
|
||||||
|
}
|
||||||
|
if accessKeyId == "" {
|
||||||
|
return resp, &IamError{Code: iam.ErrCodeInvalidInputException, Error: fmt.Errorf("AccessKeyId is required")}
|
||||||
|
}
|
||||||
|
if err := validateAccessKeyStatus(status); err != nil {
|
||||||
|
return resp, &IamError{Code: iam.ErrCodeInvalidInputException, Error: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ident := range s3cfg.Identities {
|
||||||
|
if ident.Name != userName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, cred := range ident.Credentials {
|
||||||
|
if cred.AccessKey == accessKeyId {
|
||||||
|
cred.Status = status
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp, &IamError{Code: iam.ErrCodeNoSuchEntityException, Error: fmt.Errorf("the access key with id %s for user %s cannot be found", accessKeyId, userName)}
|
||||||
|
}
|
||||||
|
return resp, &IamError{Code: iam.ErrCodeNoSuchEntityException, Error: fmt.Errorf(USER_DOES_NOT_EXIST, userName)}
|
||||||
|
}
|
||||||
|
|
||||||
func (iama *IamApiServer) DeleteAccessKey(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp DeleteAccessKeyResponse) {
|
func (iama *IamApiServer) DeleteAccessKey(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp DeleteAccessKeyResponse) {
|
||||||
userName := values.Get("UserName")
|
userName := values.Get("UserName")
|
||||||
accessKeyId := values.Get("AccessKeyId")
|
accessKeyId := values.Get("AccessKeyId")
|
||||||
@@ -475,6 +525,13 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
|
|||||||
case "DeleteAccessKey":
|
case "DeleteAccessKey":
|
||||||
iama.handleImplicitUsername(r, values)
|
iama.handleImplicitUsername(r, values)
|
||||||
response = iama.DeleteAccessKey(s3cfg, values)
|
response = iama.DeleteAccessKey(s3cfg, values)
|
||||||
|
case "UpdateAccessKey":
|
||||||
|
iama.handleImplicitUsername(r, values)
|
||||||
|
response, iamError = iama.UpdateAccessKey(s3cfg, values)
|
||||||
|
if iamError != nil {
|
||||||
|
writeIamErrorResponse(w, r, iamError)
|
||||||
|
return
|
||||||
|
}
|
||||||
case "CreatePolicy":
|
case "CreatePolicy":
|
||||||
response, iamError = iama.CreatePolicy(s3cfg, values)
|
response, iamError = iama.CreatePolicy(s3cfg, values)
|
||||||
if iamError != nil {
|
if iamError != nil {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type (
|
|||||||
GetUserResponse = iamlib.GetUserResponse
|
GetUserResponse = iamlib.GetUserResponse
|
||||||
UpdateUserResponse = iamlib.UpdateUserResponse
|
UpdateUserResponse = iamlib.UpdateUserResponse
|
||||||
CreateAccessKeyResponse = iamlib.CreateAccessKeyResponse
|
CreateAccessKeyResponse = iamlib.CreateAccessKeyResponse
|
||||||
|
UpdateAccessKeyResponse = iamlib.UpdateAccessKeyResponse
|
||||||
PutUserPolicyResponse = iamlib.PutUserPolicyResponse
|
PutUserPolicyResponse = iamlib.PutUserPolicyResponse
|
||||||
DeleteUserPolicyResponse = iamlib.DeleteUserPolicyResponse
|
DeleteUserPolicyResponse = iamlib.DeleteUserPolicyResponse
|
||||||
GetUserPolicyResponse = iamlib.GetUserPolicyResponse
|
GetUserPolicyResponse = iamlib.GetUserPolicyResponse
|
||||||
|
|||||||
@@ -84,6 +84,58 @@ func TestListAccessKeys(t *testing.T) {
|
|||||||
assert.Equal(t, http.StatusOK, response.Code)
|
assert.Equal(t, http.StatusOK, response.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateAccessKey(t *testing.T) {
|
||||||
|
svc := iam.New(session.New())
|
||||||
|
|
||||||
|
createReq, _ := svc.CreateAccessKeyRequest(&iam.CreateAccessKeyInput{UserName: aws.String("Test")})
|
||||||
|
_ = createReq.Build()
|
||||||
|
createOut := CreateAccessKeyResponse{}
|
||||||
|
response, err := executeRequest(createReq.HTTPRequest, createOut)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, http.StatusOK, response.Code)
|
||||||
|
|
||||||
|
var createResp CreateAccessKeyResponse
|
||||||
|
err = xml.Unmarshal(response.Body.Bytes(), &createResp)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
accessKeyId := createResp.CreateAccessKeyResult.AccessKey.AccessKeyId
|
||||||
|
if accessKeyId == nil {
|
||||||
|
t.Fatalf("expected access key id to be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
updateReq, _ := svc.UpdateAccessKeyRequest(&iam.UpdateAccessKeyInput{
|
||||||
|
UserName: aws.String("Test"),
|
||||||
|
AccessKeyId: accessKeyId,
|
||||||
|
Status: aws.String("Inactive"),
|
||||||
|
})
|
||||||
|
_ = updateReq.Build()
|
||||||
|
updateOut := UpdateAccessKeyResponse{}
|
||||||
|
response, err = executeRequest(updateReq.HTTPRequest, updateOut)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, http.StatusOK, response.Code)
|
||||||
|
|
||||||
|
listReq, _ := svc.ListAccessKeysRequest(&iam.ListAccessKeysInput{UserName: aws.String("Test")})
|
||||||
|
_ = listReq.Build()
|
||||||
|
listOut := ListAccessKeysResponse{}
|
||||||
|
response, err = executeRequest(listReq.HTTPRequest, listOut)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, http.StatusOK, response.Code)
|
||||||
|
|
||||||
|
var listResp ListAccessKeysResponse
|
||||||
|
err = xml.Unmarshal(response.Body.Bytes(), &listResp)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
found := false
|
||||||
|
for _, key := range listResp.ListAccessKeysResult.AccessKeyMetadata {
|
||||||
|
if key.AccessKeyId != nil && *key.AccessKeyId == *accessKeyId {
|
||||||
|
found = true
|
||||||
|
if assert.NotNil(t, key.Status) {
|
||||||
|
assert.Equal(t, "Inactive", *key.Status)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.True(t, found)
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetUser(t *testing.T) {
|
func TestGetUser(t *testing.T) {
|
||||||
userName := aws.String("Test")
|
userName := aws.String("Test")
|
||||||
params := &iam.GetUserInput{UserName: userName}
|
params := &iam.GetUserInput{UserName: userName}
|
||||||
|
|||||||
Reference in New Issue
Block a user