diff options
| author | chrislu <chris.lu@gmail.com> | 2025-12-14 16:08:56 -0800 |
|---|---|---|
| committer | chrislu <chris.lu@gmail.com> | 2025-12-14 16:08:56 -0800 |
| commit | f734b2d4bf154b372d382283a8ef09fe1c808154 (patch) | |
| tree | 85d2e06d14257051a3e57da1d6ee773a401113fe /weed/iam/helpers.go | |
| parent | f41925b60bd066048217a6de23185a6e6cfb75a7 (diff) | |
| download | seaweedfs-f734b2d4bf154b372d382283a8ef09fe1c808154.tar.xz seaweedfs-f734b2d4bf154b372d382283a8ef09fe1c808154.zip | |
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 suite
Diffstat (limited to 'weed/iam/helpers.go')
| -rw-r--r-- | weed/iam/helpers.go | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/weed/iam/helpers.go b/weed/iam/helpers.go new file mode 100644 index 000000000..02b5fe5b4 --- /dev/null +++ b/weed/iam/helpers.go @@ -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 +} + |
