* chore: remove unreachable dead code across the codebase Remove ~50,000 lines of unreachable code identified by static analysis. Major removals: - weed/filer/redis_lua: entire unused Redis Lua filer store implementation - weed/wdclient/net2, resource_pool: unused connection/resource pool packages - weed/plugin/worker/lifecycle: unused lifecycle plugin worker - weed/s3api: unused S3 policy templates, presigned URL IAM, streaming copy, multipart IAM, key rotation, and various SSE helper functions - weed/mq/kafka: unused partition mapping, compression, schema, and protocol functions - weed/mq/offset: unused SQL storage and migration code - weed/worker: unused registry, task, and monitoring functions - weed/query: unused SQL engine, parquet scanner, and type functions - weed/shell: unused EC proportional rebalance functions - weed/storage/erasure_coding/distribution: unused distribution analysis functions - Individual unreachable functions removed from 150+ files across admin, credential, filer, iam, kms, mount, mq, operation, pb, s3api, server, shell, storage, topology, and util packages * fix(s3): reset shared memory store in IAM test to prevent flaky failure TestLoadIAMManagerFromConfig_EmptyConfigWithFallbackKey was flaky because the MemoryStore credential backend is a singleton registered via init(). Earlier tests that create anonymous identities pollute the shared store, causing LookupAnonymous() to unexpectedly return true. Fix by calling Reset() on the memory store before the test runs. * style: run gofmt on changed files * fix: restore KMS functions used by integration tests * fix(plugin): prevent panic on send to closed worker session channel The Plugin.sendToWorker method could panic with "send on closed channel" when a worker disconnected while a message was being sent. The race was between streamSession.close() closing the outgoing channel and sendToWorker writing to it concurrently. Add a done channel to streamSession that is closed before the outgoing channel, and check it in sendToWorker's select to safely detect closed sessions without panicking.
177 lines
6.6 KiB
Go
177 lines
6.6 KiB
Go
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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// fineGrainedActionMap maps S3 IAM action names to internal S3 action constants.
|
|
// Supports both prefixed (e.g., "s3:DeleteObject") and unprefixed (e.g., "DeleteObject") formats.
|
|
// Populated in init() to avoid duplication of prefixed/unprefixed variants.
|
|
var fineGrainedActionMap = map[string]string{
|
|
// Coarse-grained actions (populated statically)
|
|
StatementActionAdmin: s3_constants.ACTION_ADMIN,
|
|
StatementActionWrite: s3_constants.ACTION_WRITE,
|
|
StatementActionWriteAcp: s3_constants.ACTION_WRITE_ACP,
|
|
StatementActionRead: s3_constants.ACTION_READ,
|
|
StatementActionReadAcp: s3_constants.ACTION_READ_ACP,
|
|
StatementActionList: s3_constants.ACTION_LIST,
|
|
StatementActionTagging: s3_constants.ACTION_TAGGING,
|
|
StatementActionDelete: s3_constants.ACTION_DELETE_BUCKET,
|
|
}
|
|
|
|
// baseS3ActionMap defines the base S3 actions that will be populated with both
|
|
// prefixed (s3:Action) and unprefixed (Action) variants in init().
|
|
var baseS3ActionMap = map[string]string{
|
|
// Object operations
|
|
"DeleteObject": s3_constants.ACTION_WRITE,
|
|
"PutObject": s3_constants.ACTION_WRITE,
|
|
"GetObject": s3_constants.ACTION_READ,
|
|
"DeleteObjectVersion": s3_constants.ACTION_WRITE,
|
|
"GetObjectVersion": s3_constants.ACTION_READ,
|
|
// Tagging operations
|
|
"GetObjectTagging": s3_constants.ACTION_TAGGING,
|
|
"GetObjectVersionTagging": s3_constants.ACTION_TAGGING,
|
|
"PutObjectTagging": s3_constants.ACTION_TAGGING,
|
|
"DeleteObjectTagging": s3_constants.ACTION_TAGGING,
|
|
"GetBucketTagging": s3_constants.ACTION_TAGGING,
|
|
"PutBucketTagging": s3_constants.ACTION_TAGGING,
|
|
"DeleteBucketTagging": s3_constants.ACTION_TAGGING,
|
|
// ACL operations
|
|
"PutObjectAcl": s3_constants.ACTION_WRITE_ACP,
|
|
"GetObjectAcl": s3_constants.ACTION_READ_ACP,
|
|
"GetObjectVersionAcl": s3_constants.ACTION_READ_ACP,
|
|
"PutBucketAcl": s3_constants.ACTION_WRITE_ACP,
|
|
"GetBucketAcl": s3_constants.ACTION_READ_ACP,
|
|
// Bucket operations
|
|
"DeleteBucket": s3_constants.ACTION_DELETE_BUCKET,
|
|
"DeleteBucketPolicy": s3_constants.ACTION_ADMIN,
|
|
"ListBucket": s3_constants.ACTION_LIST,
|
|
"ListBucketVersions": s3_constants.ACTION_LIST,
|
|
"ListAllMyBuckets": s3_constants.ACTION_LIST,
|
|
"GetBucketLocation": s3_constants.ACTION_READ,
|
|
"GetBucketVersioning": s3_constants.ACTION_READ,
|
|
"PutBucketVersioning": s3_constants.ACTION_WRITE,
|
|
"GetBucketCors": s3_constants.ACTION_READ,
|
|
"PutBucketCors": s3_constants.ACTION_WRITE,
|
|
"DeleteBucketCors": s3_constants.ACTION_WRITE,
|
|
"GetBucketNotification": s3_constants.ACTION_READ,
|
|
"PutBucketNotification": s3_constants.ACTION_WRITE,
|
|
"GetBucketObjectLockConfiguration": s3_constants.ACTION_READ,
|
|
"PutBucketObjectLockConfiguration": s3_constants.ACTION_WRITE,
|
|
// Multipart upload operations
|
|
"CreateMultipartUpload": s3_constants.ACTION_WRITE,
|
|
"UploadPart": s3_constants.ACTION_WRITE,
|
|
"CompleteMultipartUpload": s3_constants.ACTION_WRITE,
|
|
"AbortMultipartUpload": s3_constants.ACTION_WRITE,
|
|
"ListMultipartUploads": s3_constants.ACTION_WRITE,
|
|
"ListParts": s3_constants.ACTION_WRITE,
|
|
// Retention and legal hold operations
|
|
"GetObjectRetention": s3_constants.ACTION_READ,
|
|
"PutObjectRetention": s3_constants.ACTION_WRITE,
|
|
"GetObjectLegalHold": s3_constants.ACTION_READ,
|
|
"PutObjectLegalHold": s3_constants.ACTION_WRITE,
|
|
"BypassGovernanceRetention": s3_constants.ACTION_WRITE,
|
|
}
|
|
|
|
func init() {
|
|
// Populate both prefixed and unprefixed variants for all base S3 actions.
|
|
// This avoids duplication and makes it easy to add new actions in one place.
|
|
for action, constant := range baseS3ActionMap {
|
|
fineGrainedActionMap[action] = constant // unprefixed: "DeleteObject"
|
|
fineGrainedActionMap["s3:"+action] = constant // prefixed: "s3:DeleteObject"
|
|
}
|
|
}
|
|
|
|
// MapToStatementAction converts a policy statement action to an S3 action constant.
|
|
// It handles both coarse-grained statement actions (e.g., "Put*", "Get*") and
|
|
// fine-grained S3 actions (e.g., "s3:DeleteObject", "s3:PutObject") via exact lookup.
|
|
func MapToStatementAction(action string) string {
|
|
if val, ok := fineGrainedActionMap[action]; ok {
|
|
return val
|
|
}
|
|
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 ""
|
|
}
|
|
}
|