Browse Source

format

pull/7160/head
chrislu 1 month ago
parent
commit
8af7596ab1
  1. 4
      weed/iam/oidc/mock_provider.go
  2. 20
      weed/iam/policy/policy_engine.go
  3. 12
      weed/s3api/s3_multipart_iam.go
  4. 18
      weed/s3api/s3_policy_templates.go
  5. 116
      weed/s3api/s3_policy_templates_test.go

4
weed/iam/oidc/mock_provider.go

@ -145,10 +145,10 @@ func (m *MockOIDCProvider) SetupDefaultTestData() {
"groups": []string{"developers"},
},
}
// Add multiple token variants for compatibility
m.AddTestToken("valid_token", defaultClaims)
m.AddTestToken("valid-oidc-token", defaultClaims) // For integration tests
m.AddTestToken("valid-oidc-token", defaultClaims) // For integration tests
m.AddTestToken("valid_test_token", defaultClaims) // For STS tests
// Add default test users

20
weed/iam/policy/policy_engine.go

@ -429,7 +429,7 @@ func ValidatePolicyDocument(policy *PolicyDocument) error {
return ValidatePolicyDocumentWithType(policy, "resource")
}
// ValidateTrustPolicyDocument validates a trust policy document structure
// ValidateTrustPolicyDocument validates a trust policy document structure
func ValidateTrustPolicyDocument(policy *PolicyDocument) error {
return ValidatePolicyDocumentWithType(policy, "trust")
}
@ -439,21 +439,21 @@ func ValidatePolicyDocumentWithType(policy *PolicyDocument, policyType string) e
if policy == nil {
return fmt.Errorf("policy document cannot be nil")
}
if policy.Version == "" {
return fmt.Errorf("version is required")
}
if len(policy.Statement) == 0 {
return fmt.Errorf("at least one statement is required")
}
for i, statement := range policy.Statement {
if err := validateStatementWithType(&statement, policyType); err != nil {
return fmt.Errorf("statement %d is invalid: %w", i, err)
}
}
return nil
}
@ -467,11 +467,11 @@ func validateStatementWithType(statement *Statement, policyType string) error {
if statement.Effect != "Allow" && statement.Effect != "Deny" {
return fmt.Errorf("invalid effect: %s (must be Allow or Deny)", statement.Effect)
}
if len(statement.Action) == 0 {
return fmt.Errorf("at least one action is required")
}
// Trust policies don't require Resource field, but resource policies do
if policyType == "resource" {
if len(statement.Resource) == 0 {
@ -482,21 +482,21 @@ func validateStatementWithType(statement *Statement, policyType string) error {
if statement.Principal == nil {
return fmt.Errorf("trust policy statement must have Principal field")
}
// Trust policies typically have specific actions
validTrustActions := map[string]bool{
"sts:AssumeRole": true,
"sts:AssumeRoleWithWebIdentity": true,
"sts:AssumeRoleWithCredentials": true,
}
for _, action := range statement.Action {
if !validTrustActions[action] {
return fmt.Errorf("invalid action for trust policy: %s", action)
}
}
}
return nil
}

12
weed/s3api/s3_multipart_iam.go

@ -51,12 +51,12 @@ type MultipartUploadPolicy struct {
type MultipartOperation string
const (
MultipartOpInitiate MultipartOperation = "initiate"
MultipartOpUploadPart MultipartOperation = "upload_part"
MultipartOpComplete MultipartOperation = "complete"
MultipartOpAbort MultipartOperation = "abort"
MultipartOpList MultipartOperation = "list"
MultipartOpListParts MultipartOperation = "list_parts"
MultipartOpInitiate MultipartOperation = "initiate"
MultipartOpUploadPart MultipartOperation = "upload_part"
MultipartOpComplete MultipartOperation = "complete"
MultipartOpAbort MultipartOperation = "abort"
MultipartOpList MultipartOperation = "list"
MultipartOpListParts MultipartOperation = "list_parts"
)
// ValidateMultipartOperationWithIAM validates multipart operations using IAM policies

18
weed/s3api/s3_policy_templates.go

@ -222,11 +222,11 @@ func (t *S3PolicyTemplates) GetTimeBasedAccessPolicy(startHour, endHour int) *po
},
Condition: map[string]map[string]interface{}{
"DateGreaterThan": map[string]interface{}{
"aws:CurrentTime": time.Now().Format("2006-01-02") + "T" +
"aws:CurrentTime": time.Now().Format("2006-01-02") + "T" +
formatHour(startHour) + ":00:00Z",
},
"DateLessThan": map[string]interface{}{
"aws:CurrentTime": time.Now().Format("2006-01-02") + "T" +
"aws:CurrentTime": time.Now().Format("2006-01-02") + "T" +
formatHour(endHour) + ":00:00Z",
},
},
@ -297,7 +297,7 @@ func (t *S3PolicyTemplates) GetPresignedURLPolicy(bucketName string) *policy.Pol
// GetTemporaryAccessPolicy returns a policy for temporary access with expiration
func (t *S3PolicyTemplates) GetTemporaryAccessPolicy(bucketName string, expirationHours int) *policy.PolicyDocument {
expirationTime := time.Now().Add(time.Duration(expirationHours) * time.Hour)
return &policy.PolicyDocument{
Version: "2012-10-17",
Statement: []policy.Statement{
@ -416,11 +416,11 @@ func formatHour(hour int) string {
// PolicyTemplateDefinition represents metadata about a policy template
type PolicyTemplateDefinition struct {
Name string `json:"name"`
Description string `json:"description"`
Category string `json:"category"`
UseCase string `json:"use_case"`
Parameters []PolicyTemplateParam `json:"parameters,omitempty"`
Name string `json:"name"`
Description string `json:"description"`
Category string `json:"category"`
UseCase string `json:"use_case"`
Parameters []PolicyTemplateParam `json:"parameters,omitempty"`
Policy *policy.PolicyDocument `json:"policy"`
}
@ -447,7 +447,7 @@ func (t *S3PolicyTemplates) GetAllPolicyTemplates() []PolicyTemplateDefinition {
{
Name: "S3WriteOnlyAccess",
Description: "Provides write-only access to all S3 buckets and objects",
Category: "Basic Access",
Category: "Basic Access",
UseCase: "Data ingestion services, backup applications",
Policy: t.GetS3WriteOnlyPolicy(),
},

116
weed/s3api/s3_policy_templates_test.go

@ -13,11 +13,11 @@ func TestS3PolicyTemplates(t *testing.T) {
t.Run("S3ReadOnlyPolicy", func(t *testing.T) {
policy := templates.GetS3ReadOnlyPolicy()
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 1)
stmt := policy.Statement[0]
assert.Equal(t, "Allow", stmt.Effect)
assert.Equal(t, "S3ReadOnlyAccess", stmt.Sid)
@ -25,18 +25,18 @@ func TestS3PolicyTemplates(t *testing.T) {
assert.Contains(t, stmt.Action, "s3:ListBucket")
assert.NotContains(t, stmt.Action, "s3:PutObject")
assert.NotContains(t, stmt.Action, "s3:DeleteObject")
assert.Contains(t, stmt.Resource, "arn:seaweed:s3:::*")
assert.Contains(t, stmt.Resource, "arn:seaweed:s3:::*/*")
})
t.Run("S3WriteOnlyPolicy", func(t *testing.T) {
policy := templates.GetS3WriteOnlyPolicy()
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 1)
stmt := policy.Statement[0]
assert.Equal(t, "Allow", stmt.Effect)
assert.Equal(t, "S3WriteOnlyAccess", stmt.Sid)
@ -44,23 +44,23 @@ func TestS3PolicyTemplates(t *testing.T) {
assert.Contains(t, stmt.Action, "s3:CreateMultipartUpload")
assert.NotContains(t, stmt.Action, "s3:GetObject")
assert.NotContains(t, stmt.Action, "s3:DeleteObject")
assert.Contains(t, stmt.Resource, "arn:seaweed:s3:::*")
assert.Contains(t, stmt.Resource, "arn:seaweed:s3:::*/*")
})
t.Run("S3AdminPolicy", func(t *testing.T) {
policy := templates.GetS3AdminPolicy()
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 1)
stmt := policy.Statement[0]
assert.Equal(t, "Allow", stmt.Effect)
assert.Equal(t, "S3FullAccess", stmt.Sid)
assert.Contains(t, stmt.Action, "s3:*")
assert.Contains(t, stmt.Resource, "arn:seaweed:s3:::*")
assert.Contains(t, stmt.Resource, "arn:seaweed:s3:::*/*")
})
@ -72,18 +72,18 @@ func TestBucketSpecificPolicies(t *testing.T) {
t.Run("BucketSpecificReadPolicy", func(t *testing.T) {
policy := templates.GetBucketSpecificReadPolicy(bucketName)
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 1)
stmt := policy.Statement[0]
assert.Equal(t, "Allow", stmt.Effect)
assert.Equal(t, "BucketSpecificReadAccess", stmt.Sid)
assert.Contains(t, stmt.Action, "s3:GetObject")
assert.Contains(t, stmt.Action, "s3:ListBucket")
assert.NotContains(t, stmt.Action, "s3:PutObject")
expectedBucketArn := "arn:seaweed:s3:::" + bucketName
expectedObjectArn := "arn:seaweed:s3:::" + bucketName + "/*"
assert.Contains(t, stmt.Resource, expectedBucketArn)
@ -92,18 +92,18 @@ func TestBucketSpecificPolicies(t *testing.T) {
t.Run("BucketSpecificWritePolicy", func(t *testing.T) {
policy := templates.GetBucketSpecificWritePolicy(bucketName)
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 1)
stmt := policy.Statement[0]
assert.Equal(t, "Allow", stmt.Effect)
assert.Equal(t, "BucketSpecificWriteAccess", stmt.Sid)
assert.Contains(t, stmt.Action, "s3:PutObject")
assert.Contains(t, stmt.Action, "s3:CreateMultipartUpload")
assert.NotContains(t, stmt.Action, "s3:GetObject")
expectedBucketArn := "arn:seaweed:s3:::" + bucketName
expectedObjectArn := "arn:seaweed:s3:::" + bucketName + "/*"
assert.Contains(t, stmt.Resource, expectedBucketArn)
@ -117,7 +117,7 @@ func TestPathBasedAccessPolicy(t *testing.T) {
pathPrefix := "user123/documents"
policy := templates.GetPathBasedAccessPolicy(bucketName, pathPrefix)
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 2)
@ -137,7 +137,7 @@ func TestPathBasedAccessPolicy(t *testing.T) {
assert.Contains(t, objectStmt.Action, "s3:GetObject")
assert.Contains(t, objectStmt.Action, "s3:PutObject")
assert.Contains(t, objectStmt.Action, "s3:DeleteObject")
expectedObjectArn := "arn:seaweed:s3:::" + bucketName + "/" + pathPrefix + "/*"
assert.Contains(t, objectStmt.Resource, expectedObjectArn)
}
@ -147,22 +147,22 @@ func TestIPRestrictedPolicy(t *testing.T) {
allowedCIDRs := []string{"192.168.1.0/24", "10.0.0.0/8"}
policy := templates.GetIPRestrictedPolicy(allowedCIDRs)
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 1)
stmt := policy.Statement[0]
assert.Equal(t, "Allow", stmt.Effect)
assert.Equal(t, "IPRestrictedS3Access", stmt.Sid)
assert.Contains(t, stmt.Action, "s3:*")
assert.NotNil(t, stmt.Condition)
// Check IP condition structure
condition := stmt.Condition
ipAddress, exists := condition["IpAddress"]
assert.True(t, exists)
sourceIp, exists := ipAddress["aws:SourceIp"]
assert.True(t, exists)
assert.Equal(t, allowedCIDRs, sourceIp)
@ -170,15 +170,15 @@ func TestIPRestrictedPolicy(t *testing.T) {
func TestTimeBasedAccessPolicy(t *testing.T) {
templates := NewS3PolicyTemplates()
startHour := 9 // 9 AM
endHour := 17 // 5 PM
startHour := 9 // 9 AM
endHour := 17 // 5 PM
policy := templates.GetTimeBasedAccessPolicy(startHour, endHour)
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 1)
stmt := policy.Statement[0]
assert.Equal(t, "Allow", stmt.Effect)
assert.Equal(t, "TimeBasedS3Access", stmt.Sid)
@ -186,7 +186,7 @@ func TestTimeBasedAccessPolicy(t *testing.T) {
assert.Contains(t, stmt.Action, "s3:PutObject")
assert.Contains(t, stmt.Action, "s3:ListBucket")
assert.NotNil(t, stmt.Condition)
// Check time condition structure
condition := stmt.Condition
_, hasGreater := condition["DateGreaterThan"]
@ -200,7 +200,7 @@ func TestMultipartUploadPolicyTemplate(t *testing.T) {
bucketName := "large-files"
policy := templates.GetMultipartUploadPolicy(bucketName)
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 2)
@ -215,7 +215,7 @@ func TestMultipartUploadPolicyTemplate(t *testing.T) {
assert.Contains(t, multipartStmt.Action, "s3:AbortMultipartUpload")
assert.Contains(t, multipartStmt.Action, "s3:ListMultipartUploads")
assert.Contains(t, multipartStmt.Action, "s3:ListParts")
expectedObjectArn := "arn:seaweed:s3:::" + bucketName + "/*"
assert.Contains(t, multipartStmt.Resource, expectedObjectArn)
@ -224,7 +224,7 @@ func TestMultipartUploadPolicyTemplate(t *testing.T) {
assert.Equal(t, "Allow", listStmt.Effect)
assert.Equal(t, "ListBucketForMultipart", listStmt.Sid)
assert.Contains(t, listStmt.Action, "s3:ListBucket")
expectedBucketArn := "arn:seaweed:s3:::" + bucketName
assert.Contains(t, listStmt.Resource, expectedBucketArn)
}
@ -234,26 +234,26 @@ func TestPresignedURLPolicy(t *testing.T) {
bucketName := "shared-files"
policy := templates.GetPresignedURLPolicy(bucketName)
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 1)
stmt := policy.Statement[0]
assert.Equal(t, "Allow", stmt.Effect)
assert.Equal(t, "PresignedURLAccess", stmt.Sid)
assert.Contains(t, stmt.Action, "s3:GetObject")
assert.Contains(t, stmt.Action, "s3:PutObject")
assert.NotNil(t, stmt.Condition)
expectedObjectArn := "arn:seaweed:s3:::" + bucketName + "/*"
assert.Contains(t, stmt.Resource, expectedObjectArn)
// Check signature version condition
condition := stmt.Condition
stringEquals, exists := condition["StringEquals"]
assert.True(t, exists)
signatureVersion, exists := stringEquals["s3:x-amz-signature-version"]
assert.True(t, exists)
assert.Equal(t, "AWS4-HMAC-SHA256", signatureVersion)
@ -265,11 +265,11 @@ func TestTemporaryAccessPolicy(t *testing.T) {
expirationHours := 24
policy := templates.GetTemporaryAccessPolicy(bucketName, expirationHours)
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 1)
stmt := policy.Statement[0]
assert.Equal(t, "Allow", stmt.Effect)
assert.Equal(t, "TemporaryS3Access", stmt.Sid)
@ -277,12 +277,12 @@ func TestTemporaryAccessPolicy(t *testing.T) {
assert.Contains(t, stmt.Action, "s3:PutObject")
assert.Contains(t, stmt.Action, "s3:ListBucket")
assert.NotNil(t, stmt.Condition)
// Check expiration condition
condition := stmt.Condition
dateLessThan, exists := condition["DateLessThan"]
assert.True(t, exists)
currentTime, exists := dateLessThan["aws:CurrentTime"]
assert.True(t, exists)
assert.IsType(t, "", currentTime) // Should be a string timestamp
@ -294,7 +294,7 @@ func TestContentTypeRestrictedPolicy(t *testing.T) {
allowedTypes := []string{"image/jpeg", "image/png", "video/mp4"}
policy := templates.GetContentTypeRestrictedPolicy(bucketName, allowedTypes)
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 2)
@ -306,12 +306,12 @@ func TestContentTypeRestrictedPolicy(t *testing.T) {
assert.Contains(t, uploadStmt.Action, "s3:PutObject")
assert.Contains(t, uploadStmt.Action, "s3:CreateMultipartUpload")
assert.NotNil(t, uploadStmt.Condition)
// Check content type condition
condition := uploadStmt.Condition
stringEquals, exists := condition["StringEquals"]
assert.True(t, exists)
contentType, exists := stringEquals["s3:content-type"]
assert.True(t, exists)
assert.Equal(t, allowedTypes, contentType)
@ -329,7 +329,7 @@ func TestDenyDeletePolicy(t *testing.T) {
templates := NewS3PolicyTemplates()
policy := templates.GetDenyDeletePolicy()
require.NotNil(t, policy)
assert.Equal(t, "2012-10-17", policy.Version)
assert.Len(t, policy.Statement, 2)
@ -358,9 +358,9 @@ func TestPolicyTemplateMetadata(t *testing.T) {
t.Run("GetAllPolicyTemplates", func(t *testing.T) {
allTemplates := templates.GetAllPolicyTemplates()
assert.Greater(t, len(allTemplates), 10) // Should have many templates
// Check that each template has required fields
for _, template := range allTemplates {
assert.NotEmpty(t, template.Name)
@ -378,7 +378,7 @@ func TestPolicyTemplateMetadata(t *testing.T) {
require.NotNil(t, template)
assert.Equal(t, "S3ReadOnlyAccess", template.Name)
assert.Equal(t, "Basic Access", template.Category)
// Test non-existing template
nonExistent := templates.GetPolicyTemplateByName("NonExistentTemplate")
assert.Nil(t, nonExistent)
@ -387,11 +387,11 @@ func TestPolicyTemplateMetadata(t *testing.T) {
t.Run("GetPolicyTemplatesByCategory", func(t *testing.T) {
basicAccessTemplates := templates.GetPolicyTemplatesByCategory("Basic Access")
assert.GreaterOrEqual(t, len(basicAccessTemplates), 2)
for _, template := range basicAccessTemplates {
assert.Equal(t, "Basic Access", template.Category)
}
// Test non-existing category
emptyCategory := templates.GetPolicyTemplatesByCategory("NonExistentCategory")
assert.Empty(t, emptyCategory)
@ -399,7 +399,7 @@ func TestPolicyTemplateMetadata(t *testing.T) {
t.Run("PolicyTemplateParameters", func(t *testing.T) {
allTemplates := templates.GetAllPolicyTemplates()
// Find a template with parameters (like BucketSpecificRead)
var templateWithParams *PolicyTemplateDefinition
for _, template := range allTemplates {
@ -408,10 +408,10 @@ func TestPolicyTemplateMetadata(t *testing.T) {
break
}
}
require.NotNil(t, templateWithParams)
assert.Greater(t, len(templateWithParams.Parameters), 0)
param := templateWithParams.Parameters[0]
assert.Equal(t, "bucketName", param.Name)
assert.Equal(t, "string", param.Type)
@ -445,17 +445,17 @@ func TestFormatHourHelper(t *testing.T) {
func TestPolicyTemplateCategories(t *testing.T) {
templates := NewS3PolicyTemplates()
allTemplates := templates.GetAllPolicyTemplates()
// Extract all categories
categoryMap := make(map[string]int)
for _, template := range allTemplates {
categoryMap[template.Category]++
}
// Expected categories
expectedCategories := []string{
"Basic Access",
"Administrative",
"Administrative",
"Bucket-Specific",
"Path-Restricted",
"Security",
@ -464,7 +464,7 @@ func TestPolicyTemplateCategories(t *testing.T) {
"Content Control",
"Data Protection",
}
for _, expectedCategory := range expectedCategories {
count, exists := categoryMap[expectedCategory]
assert.True(t, exists, "Category %s should exist", expectedCategory)
@ -475,23 +475,23 @@ func TestPolicyTemplateCategories(t *testing.T) {
func TestPolicyValidation(t *testing.T) {
templates := NewS3PolicyTemplates()
allTemplates := templates.GetAllPolicyTemplates()
// Test that all policies have valid structure
for _, template := range allTemplates {
t.Run("Policy_"+template.Name, func(t *testing.T) {
policy := template.Policy
// Basic validation
assert.Equal(t, "2012-10-17", policy.Version)
assert.Greater(t, len(policy.Statement), 0)
// Validate each statement
for i, stmt := range policy.Statement {
assert.NotEmpty(t, stmt.Effect, "Statement %d should have effect", i)
assert.Contains(t, []string{"Allow", "Deny"}, stmt.Effect, "Statement %d effect should be Allow or Deny", i)
assert.Greater(t, len(stmt.Action), 0, "Statement %d should have actions", i)
assert.Greater(t, len(stmt.Resource), 0, "Statement %d should have resources", i)
// Check resource format
for _, resource := range stmt.Resource {
if resource != "*" {

Loading…
Cancel
Save