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 |
|||
|
|||
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