s3api: fix static IAM policy enforcement after reload (#8532)
* s3api: honor attached IAM policies over legacy actions * s3api: hydrate IAM policy docs during config reload * s3api: use policy-aware auth when listing buckets * credential: propagate context through filer_etc policy reads * credential: make legacy policy deletes durable * s3api: exercise managed policy runtime loader * s3api: allow static IAM users without session tokens * iam: deny unmatched attached policies under default allow * iam: load embedded policy files from filer store * s3api: require session tokens for IAM presigning * s3api: sync runtime policies into zero-config IAM * credential: respect context in policy file loads * credential: serialize legacy policy deletes * iam: align filer policy store naming * s3api: use authenticated principals for presigning * iam: deep copy policy conditions * s3api: require request creation in policy tests * filer: keep ReadInsideFiler as the context-aware API * iam: harden filer policy store writes * credential: strengthen legacy policy serialization test * credential: forward runtime policy loaders through wrapper * s3api: harden runtime policy merging * iam: require typed already-exists errors
This commit is contained in:
@@ -13,16 +13,15 @@ import (
|
||||
"github.com/seaweedfs/seaweedfs/weed/iam/sts"
|
||||
"github.com/seaweedfs/seaweedfs/weed/iam/utils"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestS3IAMMiddleware tests the basic S3 IAM middleware functionality
|
||||
func TestS3IAMMiddleware(t *testing.T) {
|
||||
// Create IAM manager
|
||||
iamManager := integration.NewIAMManager()
|
||||
func newTestS3IAMManagerWithDefaultEffect(t *testing.T, defaultEffect string) *integration.IAMManager {
|
||||
t.Helper()
|
||||
|
||||
// Initialize with test configuration
|
||||
iamManager := integration.NewIAMManager()
|
||||
config := &integration.IAMConfig{
|
||||
STS: &sts.STSConfig{
|
||||
TokenDuration: sts.FlexibleDuration{Duration: time.Hour},
|
||||
@@ -31,7 +30,7 @@ func TestS3IAMMiddleware(t *testing.T) {
|
||||
SigningKey: []byte("test-signing-key-32-characters-long"),
|
||||
},
|
||||
Policy: &policy.PolicyEngineConfig{
|
||||
DefaultEffect: "Deny",
|
||||
DefaultEffect: defaultEffect,
|
||||
StoreType: "memory",
|
||||
},
|
||||
Roles: &integration.RoleStoreConfig{
|
||||
@@ -40,10 +39,22 @@ func TestS3IAMMiddleware(t *testing.T) {
|
||||
}
|
||||
|
||||
err := iamManager.Initialize(config, func() string {
|
||||
return "localhost:8888" // Mock filer address for testing
|
||||
return "localhost:8888"
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return iamManager
|
||||
}
|
||||
|
||||
func newTestS3IAMManager(t *testing.T) *integration.IAMManager {
|
||||
t.Helper()
|
||||
return newTestS3IAMManagerWithDefaultEffect(t, "Deny")
|
||||
}
|
||||
|
||||
// TestS3IAMMiddleware tests the basic S3 IAM middleware functionality
|
||||
func TestS3IAMMiddleware(t *testing.T) {
|
||||
iamManager := newTestS3IAMManager(t)
|
||||
|
||||
// Create S3 IAM integration
|
||||
s3IAMIntegration := NewS3IAMIntegration(iamManager, "localhost:8888")
|
||||
|
||||
@@ -52,6 +63,74 @@ func TestS3IAMMiddleware(t *testing.T) {
|
||||
assert.True(t, s3IAMIntegration.enabled)
|
||||
}
|
||||
|
||||
func TestS3IAMMiddlewareStaticV4ManagedPolicies(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
iamManager := newTestS3IAMManager(t)
|
||||
|
||||
allowPolicy := &policy.PolicyDocument{
|
||||
Version: "2012-10-17",
|
||||
Statement: []policy.Statement{
|
||||
{
|
||||
Effect: "Allow",
|
||||
Action: policy.StringList{"s3:PutObject", "s3:ListBucket"},
|
||||
Resource: policy.StringList{"arn:aws:s3:::cli-allowed-bucket", "arn:aws:s3:::cli-allowed-bucket/*"},
|
||||
},
|
||||
},
|
||||
}
|
||||
require.NoError(t, iamManager.CreatePolicy(ctx, "localhost:8888", "cli-bucket-access-policy", allowPolicy))
|
||||
|
||||
s3IAMIntegration := NewS3IAMIntegration(iamManager, "localhost:8888")
|
||||
identity := &IAMIdentity{
|
||||
Name: "cli-test-user",
|
||||
Principal: "arn:aws:iam::000000000000:user/cli-test-user",
|
||||
PolicyNames: []string{"cli-bucket-access-policy"},
|
||||
}
|
||||
|
||||
putReq := httptest.NewRequest(http.MethodPut, "http://example.com/cli-allowed-bucket/test-file.txt", http.NoBody)
|
||||
putErrCode := s3IAMIntegration.AuthorizeAction(ctx, identity, s3_constants.ACTION_WRITE, "cli-allowed-bucket", "test-file.txt", putReq)
|
||||
assert.Equal(t, s3err.ErrNone, putErrCode)
|
||||
|
||||
listReq := httptest.NewRequest(http.MethodGet, "http://example.com/cli-allowed-bucket/", http.NoBody)
|
||||
listErrCode := s3IAMIntegration.AuthorizeAction(ctx, identity, s3_constants.ACTION_LIST, "cli-allowed-bucket", "", listReq)
|
||||
assert.Equal(t, s3err.ErrNone, listErrCode)
|
||||
}
|
||||
|
||||
func TestS3IAMMiddlewareAttachedPoliciesRestrictDefaultAllow(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
iamManager := newTestS3IAMManagerWithDefaultEffect(t, "Allow")
|
||||
|
||||
allowPolicy := &policy.PolicyDocument{
|
||||
Version: "2012-10-17",
|
||||
Statement: []policy.Statement{
|
||||
{
|
||||
Effect: "Allow",
|
||||
Action: policy.StringList{"s3:PutObject", "s3:ListBucket"},
|
||||
Resource: policy.StringList{"arn:aws:s3:::cli-allowed-bucket", "arn:aws:s3:::cli-allowed-bucket/*"},
|
||||
},
|
||||
},
|
||||
}
|
||||
require.NoError(t, iamManager.CreatePolicy(ctx, "localhost:8888", "cli-bucket-access-policy", allowPolicy))
|
||||
|
||||
s3IAMIntegration := NewS3IAMIntegration(iamManager, "localhost:8888")
|
||||
identity := &IAMIdentity{
|
||||
Name: "cli-test-user",
|
||||
Principal: "arn:aws:iam::000000000000:user/cli-test-user",
|
||||
PolicyNames: []string{"cli-bucket-access-policy"},
|
||||
}
|
||||
|
||||
allowedReq := httptest.NewRequest(http.MethodPut, "http://example.com/cli-allowed-bucket/test-file.txt", http.NoBody)
|
||||
allowedErrCode := s3IAMIntegration.AuthorizeAction(ctx, identity, s3_constants.ACTION_WRITE, "cli-allowed-bucket", "test-file.txt", allowedReq)
|
||||
assert.Equal(t, s3err.ErrNone, allowedErrCode)
|
||||
|
||||
forbiddenReq := httptest.NewRequest(http.MethodPut, "http://example.com/cli-forbidden-bucket/forbidden-file.txt", http.NoBody)
|
||||
forbiddenErrCode := s3IAMIntegration.AuthorizeAction(ctx, identity, s3_constants.ACTION_WRITE, "cli-forbidden-bucket", "forbidden-file.txt", forbiddenReq)
|
||||
assert.Equal(t, s3err.ErrAccessDenied, forbiddenErrCode)
|
||||
|
||||
forbiddenListReq := httptest.NewRequest(http.MethodGet, "http://example.com/cli-forbidden-bucket/", http.NoBody)
|
||||
forbiddenListErrCode := s3IAMIntegration.AuthorizeAction(ctx, identity, s3_constants.ACTION_LIST, "cli-forbidden-bucket", "", forbiddenListReq)
|
||||
assert.Equal(t, s3err.ErrAccessDenied, forbiddenListErrCode)
|
||||
}
|
||||
|
||||
// TestS3IAMMiddlewareJWTAuth tests JWT authentication
|
||||
func TestS3IAMMiddlewareJWTAuth(t *testing.T) {
|
||||
// Skip for now since it requires full setup
|
||||
|
||||
Reference in New Issue
Block a user