Browse Source
Refactor: Extract common IAM logic into shared weed/iam package (#7747)
Refactor: Extract common IAM logic into shared weed/iam package (#7747)
This resolves GitHub issue #7747 by extracting duplicated IAM code into a shared package that both the embedded S3 IAM and standalone IAM use. New shared package (weed/iam/): - constants.go: Common constants (charsets, action strings, error messages) - helpers.go: Shared helper functions (Hash, GenerateRandomString, GenerateAccessKeyId, GenerateSecretAccessKey, StringSlicesEqual, MapToStatementAction, MapToIdentitiesAction, MaskAccessKey) - responses.go: Common IAM response structs (CommonResponse, ListUsersResponse, CreateUserResponse, etc.) - helpers_test.go: Unit tests for shared helpers Updated files: - weed/s3api/s3api_embedded_iam.go: Use type aliases and function wrappers to the shared package, removing ~200 lines of duplicated code - weed/iamapi/iamapi_management_handlers.go: Use shared package for constants and helper functions, removing ~100 lines of duplicated code - weed/iamapi/iamapi_response.go: Re-export types from shared package for backwards compatibility Benefits: - Single source of truth for IAM constants and helpers - Easier maintenance - changes only need to be made in one place - Reduced risk of inconsistencies between embedded and standalone IAM - Better test coverage through shared test suitepull/7750/head
7 changed files with 509 additions and 430 deletions
-
32weed/iam/constants.go
-
126weed/iam/helpers.go
-
135weed/iam/helpers_test.go
-
140weed/iam/responses.go
-
125weed/iamapi/iamapi_management_handlers.go
-
129weed/iamapi/iamapi_response.go
-
252weed/s3api/s3api_embedded_iam.go
@ -0,0 +1,32 @@ |
|||||
|
package iam |
||||
|
|
||||
|
// Character sets for credential generation
|
||||
|
const ( |
||||
|
CharsetUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" |
||||
|
Charset = CharsetUpper + "abcdefghijklmnopqrstuvwxyz/" |
||||
|
) |
||||
|
|
||||
|
// Policy document version
|
||||
|
const PolicyDocumentVersion = "2012-10-17" |
||||
|
|
||||
|
// Error message templates
|
||||
|
const UserDoesNotExist = "the user with name %s cannot be found." |
||||
|
|
||||
|
// Statement action constants - these map to IAM policy actions
|
||||
|
const ( |
||||
|
StatementActionAdmin = "*" |
||||
|
StatementActionWrite = "Put*" |
||||
|
StatementActionWriteAcp = "PutBucketAcl" |
||||
|
StatementActionRead = "Get*" |
||||
|
StatementActionReadAcp = "GetBucketAcl" |
||||
|
StatementActionList = "List*" |
||||
|
StatementActionTagging = "Tagging*" |
||||
|
StatementActionDelete = "DeleteBucket*" |
||||
|
) |
||||
|
|
||||
|
// Access key lengths
|
||||
|
const ( |
||||
|
AccessKeyIdLength = 21 |
||||
|
SecretAccessKeyLength = 42 |
||||
|
) |
||||
|
|
||||
@ -0,0 +1,126 @@ |
|||||
|
package iam |
||||
|
|
||||
|
import ( |
||||
|
"crypto/rand" |
||||
|
"crypto/sha1" |
||||
|
"fmt" |
||||
|
"math/big" |
||||
|
"sort" |
||||
|
|
||||
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" |
||||
|
) |
||||
|
|
||||
|
// Hash computes a SHA1 hash of the input string.
|
||||
|
func Hash(s *string) string { |
||||
|
h := sha1.New() |
||||
|
h.Write([]byte(*s)) |
||||
|
return fmt.Sprintf("%x", h.Sum(nil)) |
||||
|
} |
||||
|
|
||||
|
// GenerateRandomString generates a cryptographically secure random string.
|
||||
|
// Uses crypto/rand for security-sensitive credential generation.
|
||||
|
func GenerateRandomString(length int, charset string) (string, error) { |
||||
|
if length <= 0 { |
||||
|
return "", fmt.Errorf("length must be positive, got %d", length) |
||||
|
} |
||||
|
if charset == "" { |
||||
|
return "", fmt.Errorf("charset must not be empty") |
||||
|
} |
||||
|
b := make([]byte, length) |
||||
|
for i := range b { |
||||
|
n, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset)))) |
||||
|
if err != nil { |
||||
|
return "", fmt.Errorf("failed to generate random index: %w", err) |
||||
|
} |
||||
|
b[i] = charset[n.Int64()] |
||||
|
} |
||||
|
return string(b), nil |
||||
|
} |
||||
|
|
||||
|
// GenerateAccessKeyId generates a new access key ID.
|
||||
|
func GenerateAccessKeyId() (string, error) { |
||||
|
return GenerateRandomString(AccessKeyIdLength, CharsetUpper) |
||||
|
} |
||||
|
|
||||
|
// GenerateSecretAccessKey generates a new secret access key.
|
||||
|
func GenerateSecretAccessKey() (string, error) { |
||||
|
return GenerateRandomString(SecretAccessKeyLength, Charset) |
||||
|
} |
||||
|
|
||||
|
// StringSlicesEqual compares two string slices for equality, ignoring order.
|
||||
|
// This is used instead of reflect.DeepEqual to avoid order-dependent comparisons.
|
||||
|
func StringSlicesEqual(a, b []string) bool { |
||||
|
if len(a) != len(b) { |
||||
|
return false |
||||
|
} |
||||
|
// Make copies to avoid modifying the originals
|
||||
|
aCopy := make([]string, len(a)) |
||||
|
bCopy := make([]string, len(b)) |
||||
|
copy(aCopy, a) |
||||
|
copy(bCopy, b) |
||||
|
sort.Strings(aCopy) |
||||
|
sort.Strings(bCopy) |
||||
|
for i := range aCopy { |
||||
|
if aCopy[i] != bCopy[i] { |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
// MapToStatementAction converts a policy statement action to an S3 action constant.
|
||||
|
func MapToStatementAction(action string) string { |
||||
|
switch action { |
||||
|
case StatementActionAdmin: |
||||
|
return s3_constants.ACTION_ADMIN |
||||
|
case StatementActionWrite: |
||||
|
return s3_constants.ACTION_WRITE |
||||
|
case StatementActionWriteAcp: |
||||
|
return s3_constants.ACTION_WRITE_ACP |
||||
|
case StatementActionRead: |
||||
|
return s3_constants.ACTION_READ |
||||
|
case StatementActionReadAcp: |
||||
|
return s3_constants.ACTION_READ_ACP |
||||
|
case StatementActionList: |
||||
|
return s3_constants.ACTION_LIST |
||||
|
case StatementActionTagging: |
||||
|
return s3_constants.ACTION_TAGGING |
||||
|
case StatementActionDelete: |
||||
|
return s3_constants.ACTION_DELETE_BUCKET |
||||
|
default: |
||||
|
return "" |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// MapToIdentitiesAction converts an S3 action constant to a policy statement action.
|
||||
|
func MapToIdentitiesAction(action string) string { |
||||
|
switch action { |
||||
|
case s3_constants.ACTION_ADMIN: |
||||
|
return StatementActionAdmin |
||||
|
case s3_constants.ACTION_WRITE: |
||||
|
return StatementActionWrite |
||||
|
case s3_constants.ACTION_WRITE_ACP: |
||||
|
return StatementActionWriteAcp |
||||
|
case s3_constants.ACTION_READ: |
||||
|
return StatementActionRead |
||||
|
case s3_constants.ACTION_READ_ACP: |
||||
|
return StatementActionReadAcp |
||||
|
case s3_constants.ACTION_LIST: |
||||
|
return StatementActionList |
||||
|
case s3_constants.ACTION_TAGGING: |
||||
|
return StatementActionTagging |
||||
|
case s3_constants.ACTION_DELETE_BUCKET: |
||||
|
return StatementActionDelete |
||||
|
default: |
||||
|
return "" |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// MaskAccessKey masks an access key for logging, showing only the first 4 characters.
|
||||
|
func MaskAccessKey(accessKeyId string) string { |
||||
|
if len(accessKeyId) > 4 { |
||||
|
return accessKeyId[:4] + "***" |
||||
|
} |
||||
|
return accessKeyId |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,135 @@ |
|||||
|
package iam |
||||
|
|
||||
|
import ( |
||||
|
"testing" |
||||
|
|
||||
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" |
||||
|
"github.com/stretchr/testify/assert" |
||||
|
) |
||||
|
|
||||
|
func TestHash(t *testing.T) { |
||||
|
input := "test" |
||||
|
result := Hash(&input) |
||||
|
assert.NotEmpty(t, result) |
||||
|
assert.Len(t, result, 40) // SHA1 hex is 40 chars
|
||||
|
|
||||
|
// Same input should produce same hash
|
||||
|
result2 := Hash(&input) |
||||
|
assert.Equal(t, result, result2) |
||||
|
|
||||
|
// Different input should produce different hash
|
||||
|
different := "different" |
||||
|
result3 := Hash(&different) |
||||
|
assert.NotEqual(t, result, result3) |
||||
|
} |
||||
|
|
||||
|
func TestGenerateRandomString(t *testing.T) { |
||||
|
// Valid generation
|
||||
|
result, err := GenerateRandomString(10, CharsetUpper) |
||||
|
assert.NoError(t, err) |
||||
|
assert.Len(t, result, 10) |
||||
|
|
||||
|
// Different calls should produce different results (with high probability)
|
||||
|
result2, err := GenerateRandomString(10, CharsetUpper) |
||||
|
assert.NoError(t, err) |
||||
|
assert.NotEqual(t, result, result2) |
||||
|
|
||||
|
// Invalid length
|
||||
|
_, err = GenerateRandomString(0, CharsetUpper) |
||||
|
assert.Error(t, err) |
||||
|
|
||||
|
_, err = GenerateRandomString(-1, CharsetUpper) |
||||
|
assert.Error(t, err) |
||||
|
|
||||
|
// Empty charset
|
||||
|
_, err = GenerateRandomString(10, "") |
||||
|
assert.Error(t, err) |
||||
|
} |
||||
|
|
||||
|
func TestGenerateAccessKeyId(t *testing.T) { |
||||
|
keyId, err := GenerateAccessKeyId() |
||||
|
assert.NoError(t, err) |
||||
|
assert.Len(t, keyId, AccessKeyIdLength) |
||||
|
} |
||||
|
|
||||
|
func TestGenerateSecretAccessKey(t *testing.T) { |
||||
|
secretKey, err := GenerateSecretAccessKey() |
||||
|
assert.NoError(t, err) |
||||
|
assert.Len(t, secretKey, SecretAccessKeyLength) |
||||
|
} |
||||
|
|
||||
|
func TestStringSlicesEqual(t *testing.T) { |
||||
|
tests := []struct { |
||||
|
a []string |
||||
|
b []string |
||||
|
expected bool |
||||
|
}{ |
||||
|
{[]string{"a", "b", "c"}, []string{"a", "b", "c"}, true}, |
||||
|
{[]string{"c", "b", "a"}, []string{"a", "b", "c"}, true}, // Order independent
|
||||
|
{[]string{"a", "b"}, []string{"a", "b", "c"}, false}, |
||||
|
{[]string{}, []string{}, true}, |
||||
|
{nil, nil, true}, |
||||
|
{[]string{"a"}, []string{"b"}, false}, |
||||
|
} |
||||
|
|
||||
|
for _, test := range tests { |
||||
|
result := StringSlicesEqual(test.a, test.b) |
||||
|
assert.Equal(t, test.expected, result) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestMapToStatementAction(t *testing.T) { |
||||
|
tests := []struct { |
||||
|
input string |
||||
|
expected string |
||||
|
}{ |
||||
|
{StatementActionAdmin, s3_constants.ACTION_ADMIN}, |
||||
|
{StatementActionWrite, s3_constants.ACTION_WRITE}, |
||||
|
{StatementActionRead, s3_constants.ACTION_READ}, |
||||
|
{StatementActionList, s3_constants.ACTION_LIST}, |
||||
|
{StatementActionDelete, s3_constants.ACTION_DELETE_BUCKET}, |
||||
|
{"unknown", ""}, |
||||
|
} |
||||
|
|
||||
|
for _, test := range tests { |
||||
|
result := MapToStatementAction(test.input) |
||||
|
assert.Equal(t, test.expected, result) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestMapToIdentitiesAction(t *testing.T) { |
||||
|
tests := []struct { |
||||
|
input string |
||||
|
expected string |
||||
|
}{ |
||||
|
{s3_constants.ACTION_ADMIN, StatementActionAdmin}, |
||||
|
{s3_constants.ACTION_WRITE, StatementActionWrite}, |
||||
|
{s3_constants.ACTION_READ, StatementActionRead}, |
||||
|
{s3_constants.ACTION_LIST, StatementActionList}, |
||||
|
{s3_constants.ACTION_DELETE_BUCKET, StatementActionDelete}, |
||||
|
{"unknown", ""}, |
||||
|
} |
||||
|
|
||||
|
for _, test := range tests { |
||||
|
result := MapToIdentitiesAction(test.input) |
||||
|
assert.Equal(t, test.expected, result) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestMaskAccessKey(t *testing.T) { |
||||
|
tests := []struct { |
||||
|
input string |
||||
|
expected string |
||||
|
}{ |
||||
|
{"AKIAIOSFODNN7EXAMPLE", "AKIA***"}, |
||||
|
{"AKIA", "AKIA"}, |
||||
|
{"AKI", "AKI"}, |
||||
|
{"", ""}, |
||||
|
} |
||||
|
|
||||
|
for _, test := range tests { |
||||
|
result := MaskAccessKey(test.input) |
||||
|
assert.Equal(t, test.expected, result) |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,140 @@ |
|||||
|
package iam |
||||
|
|
||||
|
import ( |
||||
|
"encoding/xml" |
||||
|
"fmt" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/aws/aws-sdk-go/service/iam" |
||||
|
) |
||||
|
|
||||
|
// CommonResponse is embedded in all IAM response types to provide RequestId.
|
||||
|
type CommonResponse struct { |
||||
|
ResponseMetadata struct { |
||||
|
RequestId string `xml:"RequestId"` |
||||
|
} `xml:"ResponseMetadata"` |
||||
|
} |
||||
|
|
||||
|
// SetRequestId sets a unique request ID based on current timestamp.
|
||||
|
func (r *CommonResponse) SetRequestId() { |
||||
|
r.ResponseMetadata.RequestId = fmt.Sprintf("%d", time.Now().UnixNano()) |
||||
|
} |
||||
|
|
||||
|
// ListUsersResponse is the response for ListUsers action.
|
||||
|
type ListUsersResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ ListUsersResponse"` |
||||
|
ListUsersResult struct { |
||||
|
Users []*iam.User `xml:"Users>member"` |
||||
|
IsTruncated bool `xml:"IsTruncated"` |
||||
|
} `xml:"ListUsersResult"` |
||||
|
} |
||||
|
|
||||
|
// ListAccessKeysResponse is the response for ListAccessKeys action.
|
||||
|
type ListAccessKeysResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ ListAccessKeysResponse"` |
||||
|
ListAccessKeysResult struct { |
||||
|
AccessKeyMetadata []*iam.AccessKeyMetadata `xml:"AccessKeyMetadata>member"` |
||||
|
IsTruncated bool `xml:"IsTruncated"` |
||||
|
} `xml:"ListAccessKeysResult"` |
||||
|
} |
||||
|
|
||||
|
// DeleteAccessKeyResponse is the response for DeleteAccessKey action.
|
||||
|
type DeleteAccessKeyResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ DeleteAccessKeyResponse"` |
||||
|
} |
||||
|
|
||||
|
// CreatePolicyResponse is the response for CreatePolicy action.
|
||||
|
type CreatePolicyResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ CreatePolicyResponse"` |
||||
|
CreatePolicyResult struct { |
||||
|
Policy iam.Policy `xml:"Policy"` |
||||
|
} `xml:"CreatePolicyResult"` |
||||
|
} |
||||
|
|
||||
|
// CreateUserResponse is the response for CreateUser action.
|
||||
|
type CreateUserResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ CreateUserResponse"` |
||||
|
CreateUserResult struct { |
||||
|
User iam.User `xml:"User"` |
||||
|
} `xml:"CreateUserResult"` |
||||
|
} |
||||
|
|
||||
|
// DeleteUserResponse is the response for DeleteUser action.
|
||||
|
type DeleteUserResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ DeleteUserResponse"` |
||||
|
} |
||||
|
|
||||
|
// GetUserResponse is the response for GetUser action.
|
||||
|
type GetUserResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ GetUserResponse"` |
||||
|
GetUserResult struct { |
||||
|
User iam.User `xml:"User"` |
||||
|
} `xml:"GetUserResult"` |
||||
|
} |
||||
|
|
||||
|
// UpdateUserResponse is the response for UpdateUser action.
|
||||
|
type UpdateUserResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ UpdateUserResponse"` |
||||
|
} |
||||
|
|
||||
|
// CreateAccessKeyResponse is the response for CreateAccessKey action.
|
||||
|
type CreateAccessKeyResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ CreateAccessKeyResponse"` |
||||
|
CreateAccessKeyResult struct { |
||||
|
AccessKey iam.AccessKey `xml:"AccessKey"` |
||||
|
} `xml:"CreateAccessKeyResult"` |
||||
|
} |
||||
|
|
||||
|
// PutUserPolicyResponse is the response for PutUserPolicy action.
|
||||
|
type PutUserPolicyResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ PutUserPolicyResponse"` |
||||
|
} |
||||
|
|
||||
|
// DeleteUserPolicyResponse is the response for DeleteUserPolicy action.
|
||||
|
type DeleteUserPolicyResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ DeleteUserPolicyResponse"` |
||||
|
} |
||||
|
|
||||
|
// GetUserPolicyResponse is the response for GetUserPolicy action.
|
||||
|
type GetUserPolicyResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ GetUserPolicyResponse"` |
||||
|
GetUserPolicyResult struct { |
||||
|
UserName string `xml:"UserName"` |
||||
|
PolicyName string `xml:"PolicyName"` |
||||
|
PolicyDocument string `xml:"PolicyDocument"` |
||||
|
} `xml:"GetUserPolicyResult"` |
||||
|
} |
||||
|
|
||||
|
// ErrorResponse is the IAM error response format.
|
||||
|
type ErrorResponse struct { |
||||
|
CommonResponse |
||||
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ ErrorResponse"` |
||||
|
Error struct { |
||||
|
iam.ErrorDetails |
||||
|
Type string `xml:"Type"` |
||||
|
} `xml:"Error"` |
||||
|
} |
||||
|
|
||||
|
// Error represents an IAM API error with code and underlying error.
|
||||
|
type Error struct { |
||||
|
Code string |
||||
|
Error error |
||||
|
} |
||||
|
|
||||
|
// Policies stores IAM policies (used for managed policy storage).
|
||||
|
type Policies struct { |
||||
|
Policies map[string]interface{} `json:"policies"` |
||||
|
} |
||||
|
|
||||
@ -1,113 +1,26 @@ |
|||||
package iamapi |
package iamapi |
||||
|
|
||||
import ( |
|
||||
"encoding/xml" |
|
||||
"fmt" |
|
||||
"time" |
|
||||
|
// This file re-exports IAM response types from the shared weed/iam package
|
||||
|
// for backwards compatibility with existing code.
|
||||
|
|
||||
"github.com/aws/aws-sdk-go/service/iam" |
|
||||
|
import ( |
||||
|
iamlib "github.com/seaweedfs/seaweedfs/weed/iam" |
||||
) |
) |
||||
|
|
||||
type CommonResponse struct { |
|
||||
ResponseMetadata struct { |
|
||||
RequestId string `xml:"RequestId"` |
|
||||
} `xml:"ResponseMetadata"` |
|
||||
} |
|
||||
|
|
||||
type ListUsersResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ ListUsersResponse"` |
|
||||
ListUsersResult struct { |
|
||||
Users []*iam.User `xml:"Users>member"` |
|
||||
IsTruncated bool `xml:"IsTruncated"` |
|
||||
} `xml:"ListUsersResult"` |
|
||||
} |
|
||||
|
|
||||
type ListAccessKeysResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ ListAccessKeysResponse"` |
|
||||
ListAccessKeysResult struct { |
|
||||
AccessKeyMetadata []*iam.AccessKeyMetadata `xml:"AccessKeyMetadata>member"` |
|
||||
IsTruncated bool `xml:"IsTruncated"` |
|
||||
} `xml:"ListAccessKeysResult"` |
|
||||
} |
|
||||
|
|
||||
type DeleteAccessKeyResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ DeleteAccessKeyResponse"` |
|
||||
} |
|
||||
|
|
||||
type CreatePolicyResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ CreatePolicyResponse"` |
|
||||
CreatePolicyResult struct { |
|
||||
Policy iam.Policy `xml:"Policy"` |
|
||||
} `xml:"CreatePolicyResult"` |
|
||||
} |
|
||||
|
|
||||
type CreateUserResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ CreateUserResponse"` |
|
||||
CreateUserResult struct { |
|
||||
User iam.User `xml:"User"` |
|
||||
} `xml:"CreateUserResult"` |
|
||||
} |
|
||||
|
|
||||
type DeleteUserResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ DeleteUserResponse"` |
|
||||
} |
|
||||
|
|
||||
type GetUserResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ GetUserResponse"` |
|
||||
GetUserResult struct { |
|
||||
User iam.User `xml:"User"` |
|
||||
} `xml:"GetUserResult"` |
|
||||
} |
|
||||
|
|
||||
type UpdateUserResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ UpdateUserResponse"` |
|
||||
} |
|
||||
|
|
||||
type CreateAccessKeyResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ CreateAccessKeyResponse"` |
|
||||
CreateAccessKeyResult struct { |
|
||||
AccessKey iam.AccessKey `xml:"AccessKey"` |
|
||||
} `xml:"CreateAccessKeyResult"` |
|
||||
} |
|
||||
|
|
||||
type PutUserPolicyResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ PutUserPolicyResponse"` |
|
||||
} |
|
||||
|
|
||||
type DeleteUserPolicyResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ DeleteUserPolicyResponse"` |
|
||||
} |
|
||||
|
|
||||
type GetUserPolicyResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ GetUserPolicyResponse"` |
|
||||
GetUserPolicyResult struct { |
|
||||
UserName string `xml:"UserName"` |
|
||||
PolicyName string `xml:"PolicyName"` |
|
||||
PolicyDocument string `xml:"PolicyDocument"` |
|
||||
} `xml:"GetUserPolicyResult"` |
|
||||
} |
|
||||
|
|
||||
type ErrorResponse struct { |
|
||||
CommonResponse |
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ ErrorResponse"` |
|
||||
Error struct { |
|
||||
iam.ErrorDetails |
|
||||
Type string `xml:"Type"` |
|
||||
} `xml:"Error"` |
|
||||
} |
|
||||
|
|
||||
func (r *CommonResponse) SetRequestId() { |
|
||||
r.ResponseMetadata.RequestId = fmt.Sprintf("%d", time.Now().UnixNano()) |
|
||||
} |
|
||||
|
// Type aliases for IAM response types from shared package
|
||||
|
type ( |
||||
|
CommonResponse = iamlib.CommonResponse |
||||
|
ListUsersResponse = iamlib.ListUsersResponse |
||||
|
ListAccessKeysResponse = iamlib.ListAccessKeysResponse |
||||
|
DeleteAccessKeyResponse = iamlib.DeleteAccessKeyResponse |
||||
|
CreatePolicyResponse = iamlib.CreatePolicyResponse |
||||
|
CreateUserResponse = iamlib.CreateUserResponse |
||||
|
DeleteUserResponse = iamlib.DeleteUserResponse |
||||
|
GetUserResponse = iamlib.GetUserResponse |
||||
|
UpdateUserResponse = iamlib.UpdateUserResponse |
||||
|
CreateAccessKeyResponse = iamlib.CreateAccessKeyResponse |
||||
|
PutUserPolicyResponse = iamlib.PutUserPolicyResponse |
||||
|
DeleteUserPolicyResponse = iamlib.DeleteUserPolicyResponse |
||||
|
GetUserPolicyResponse = iamlib.GetUserPolicyResponse |
||||
|
ErrorResponse = iamlib.ErrorResponse |
||||
|
) |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue