diff options
Diffstat (limited to 'weed/s3api/policy_engine/examples.go')
| -rw-r--r-- | weed/s3api/policy_engine/examples.go | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/weed/s3api/policy_engine/examples.go b/weed/s3api/policy_engine/examples.go new file mode 100644 index 000000000..6f14127f3 --- /dev/null +++ b/weed/s3api/policy_engine/examples.go @@ -0,0 +1,463 @@ +//go:build ignore +// +build ignore + +package policy_engine + +import ( + "encoding/json" + "fmt" +) + +// This file contains examples and documentation for the policy engine + +// ExampleIdentityJSON shows the existing identities.json format (unchanged) +var ExampleIdentityJSON = `{ + "identities": [ + { + "name": "user1", + "credentials": [ + { + "accessKey": "AKIAIOSFODNN7EXAMPLE", + "secretKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + } + ], + "actions": [ + "Read:bucket1/*", + "Write:bucket1/*", + "Admin:bucket2" + ] + }, + { + "name": "readonly-user", + "credentials": [ + { + "accessKey": "AKIAI44QH8DHBEXAMPLE", + "secretKey": "je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY" + } + ], + "actions": [ + "Read:bucket1/*", + "List:bucket1" + ] + } + ] +}` + +// ExampleBucketPolicy shows an AWS S3 bucket policy with conditions +var ExampleBucketPolicy = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowGetObjectFromSpecificIP", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::my-bucket/*", + "Condition": { + "IpAddress": { + "aws:SourceIp": "192.168.1.0/24" + } + } + }, + { + "Sid": "AllowPutObjectWithSSL", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:PutObject", + "Resource": "arn:aws:s3:::my-bucket/*", + "Condition": { + "Bool": { + "aws:SecureTransport": "true" + } + } + }, + { + "Sid": "DenyDeleteFromProduction", + "Effect": "Deny", + "Principal": "*", + "Action": "s3:DeleteObject", + "Resource": "arn:aws:s3:::my-bucket/production/*" + } + ] +}` + +// ExampleTimeBasedPolicy shows a policy with time-based conditions +var ExampleTimeBasedPolicy = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowAccessDuringBusinessHours", + "Effect": "Allow", + "Principal": "*", + "Action": ["s3:GetObject", "s3:PutObject"], + "Resource": "arn:aws:s3:::my-bucket/*", + "Condition": { + "DateGreaterThan": { + "aws:RequestTime": "2023-01-01T08:00:00Z" + }, + "DateLessThan": { + "aws:RequestTime": "2023-12-31T18:00:00Z" + } + } + } + ] +}` + +// ExampleIPRestrictedPolicy shows a policy with IP restrictions +var ExampleIPRestrictedPolicy = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowFromOfficeNetwork", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:*", + "Resource": [ + "arn:aws:s3:::my-bucket", + "arn:aws:s3:::my-bucket/*" + ], + "Condition": { + "IpAddress": { + "aws:SourceIp": [ + "203.0.113.0/24", + "198.51.100.0/24" + ] + } + } + }, + { + "Sid": "DenyFromRestrictedIPs", + "Effect": "Deny", + "Principal": "*", + "Action": "*", + "Resource": "*", + "Condition": { + "IpAddress": { + "aws:SourceIp": [ + "192.0.2.0/24" + ] + } + } + } + ] +}` + +// ExamplePublicReadPolicy shows a policy for public read access +var ExamplePublicReadPolicy = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "PublicReadGetObject", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::my-public-bucket/*" + } + ] +}` + +// ExampleCORSPolicy shows a policy with CORS-related conditions +var ExampleCORSPolicy = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowCrossOriginRequests", + "Effect": "Allow", + "Principal": "*", + "Action": ["s3:GetObject", "s3:PutObject"], + "Resource": "arn:aws:s3:::my-bucket/*", + "Condition": { + "StringLike": { + "aws:Referer": [ + "https://example.com/*", + "https://*.example.com/*" + ] + } + } + } + ] +}` + +// ExampleUserAgentPolicy shows a policy with user agent restrictions +var ExampleUserAgentPolicy = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowSpecificUserAgents", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::my-bucket/*", + "Condition": { + "StringLike": { + "aws:UserAgent": [ + "MyApp/*", + "curl/*" + ] + } + } + } + ] +}` + +// ExamplePrefixBasedPolicy shows a policy with prefix-based access +var ExamplePrefixBasedPolicy = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowUserFolderAccess", + "Effect": "Allow", + "Principal": "*", + "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], + "Resource": "arn:aws:s3:::my-bucket/${aws:username}/*", + "Condition": { + "StringEquals": { + "s3:prefix": "${aws:username}/" + } + } + } + ] +}` + +// ExampleMultiStatementPolicy shows a complex policy with multiple statements +var ExampleMultiStatementPolicy = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowListBucket", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:ListBucket", + "Resource": "arn:aws:s3:::my-bucket", + "Condition": { + "StringEquals": { + "s3:prefix": "public/" + } + } + }, + { + "Sid": "AllowGetPublicObjects", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::my-bucket/public/*" + }, + { + "Sid": "AllowAuthenticatedUpload", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:PutObject", + "Resource": "arn:aws:s3:::my-bucket/uploads/*", + "Condition": { + "StringEquals": { + "s3:x-amz-acl": "private" + } + } + }, + { + "Sid": "DenyInsecureConnections", + "Effect": "Deny", + "Principal": "*", + "Action": "s3:*", + "Resource": [ + "arn:aws:s3:::my-bucket", + "arn:aws:s3:::my-bucket/*" + ], + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + } + } + ] +}` + +// GetAllExamples returns all example policies +func GetAllExamples() map[string]string { + return map[string]string{ + "basic-bucket-policy": ExampleBucketPolicy, + "time-based-policy": ExampleTimeBasedPolicy, + "ip-restricted-policy": ExampleIPRestrictedPolicy, + "public-read-policy": ExamplePublicReadPolicy, + "cors-policy": ExampleCORSPolicy, + "user-agent-policy": ExampleUserAgentPolicy, + "prefix-based-policy": ExamplePrefixBasedPolicy, + "multi-statement-policy": ExampleMultiStatementPolicy, + } +} + +// ValidateExamplePolicies validates all example policies +func ValidateExamplePolicies() error { + examples := GetAllExamples() + + for name, policyJSON := range examples { + _, err := ParsePolicy(policyJSON) + if err != nil { + return fmt.Errorf("invalid example policy %s: %v", name, err) + } + } + + return nil +} + +// GetExamplePolicy returns a specific example policy +func GetExamplePolicy(name string) (string, error) { + examples := GetAllExamples() + + policy, exists := examples[name] + if !exists { + return "", fmt.Errorf("example policy %s not found", name) + } + + return policy, nil +} + +// CreateExamplePolicyDocument creates a PolicyDocument from an example +func CreateExamplePolicyDocument(name string) (*PolicyDocument, error) { + policyJSON, err := GetExamplePolicy(name) + if err != nil { + return nil, err + } + + return ParsePolicy(policyJSON) +} + +// PrintExamplePolicyPretty prints an example policy in pretty format +func PrintExamplePolicyPretty(name string) error { + policyJSON, err := GetExamplePolicy(name) + if err != nil { + return err + } + + var policy interface{} + if err := json.Unmarshal([]byte(policyJSON), &policy); err != nil { + return err + } + + prettyJSON, err := json.MarshalIndent(policy, "", " ") + if err != nil { + return err + } + + fmt.Printf("Example Policy: %s\n", name) + fmt.Printf("================\n") + fmt.Println(string(prettyJSON)) + + return nil +} + +// ExampleUsage demonstrates how to use the policy engine +func ExampleUsage() { + // Create a new policy engine + engine := NewPolicyEngine() + + // Set a bucket policy + policyJSON := ExampleBucketPolicy + err := engine.SetBucketPolicy("my-bucket", policyJSON) + if err != nil { + fmt.Printf("Error setting bucket policy: %v\n", err) + return + } + + // Evaluate a policy + args := &PolicyEvaluationArgs{ + Action: "s3:GetObject", + Resource: "arn:aws:s3:::my-bucket/test-object", + Principal: "*", + Conditions: map[string][]string{ + "aws:SourceIp": {"192.168.1.100"}, + }, + } + + result := engine.EvaluatePolicy("my-bucket", args) + + switch result { + case PolicyResultAllow: + fmt.Println("Access allowed") + case PolicyResultDeny: + fmt.Println("Access denied") + case PolicyResultIndeterminate: + fmt.Println("Access indeterminate") + } +} + +// ExampleLegacyIntegration demonstrates backward compatibility +func ExampleLegacyIntegration() { + // Legacy identity actions + legacyActions := []string{ + "Read:bucket1/*", + "Write:bucket1/uploads/*", + "Admin:bucket2", + } + + // Convert to policy + policy, err := ConvertIdentityToPolicy(legacyActions, "bucket1") + if err != nil { + fmt.Printf("Error converting identity to policy: %v\n", err) + return + } + + // Create policy-backed IAM + policyIAM := NewPolicyBackedIAM() + + // Set the converted policy + policyJSON, _ := json.MarshalIndent(policy, "", " ") + err = policyIAM.SetBucketPolicy("bucket1", string(policyJSON)) + if err != nil { + fmt.Printf("Error setting bucket policy: %v\n", err) + return + } + + fmt.Println("Legacy identity successfully converted to AWS S3 policy") +} + +// ExampleConditions demonstrates various condition types +func ExampleConditions() { + examples := map[string]string{ + "StringEquals": `"StringEquals": {"s3:prefix": "documents/"}`, + "StringLike": `"StringLike": {"aws:UserAgent": "MyApp/*"}`, + "NumericEquals": `"NumericEquals": {"s3:max-keys": "10"}`, + "NumericLessThan": `"NumericLessThan": {"s3:max-keys": "1000"}`, + "DateGreaterThan": `"DateGreaterThan": {"aws:RequestTime": "2023-01-01T00:00:00Z"}`, + "DateLessThan": `"DateLessThan": {"aws:RequestTime": "2023-12-31T23:59:59Z"}`, + "IpAddress": `"IpAddress": {"aws:SourceIp": "192.168.1.0/24"}`, + "NotIpAddress": `"NotIpAddress": {"aws:SourceIp": "10.0.0.0/8"}`, + "Bool": `"Bool": {"aws:SecureTransport": "true"}`, + "Null": `"Null": {"s3:x-amz-server-side-encryption": "false"}`, + } + + fmt.Println("Supported Condition Operators:") + fmt.Println("==============================") + + for operator, example := range examples { + fmt.Printf("%s: %s\n", operator, example) + } +} + +// ExampleMigrationStrategy demonstrates migration from legacy to policy-based system +func ExampleMigrationStrategy() { + fmt.Println("Migration Strategy:") + fmt.Println("==================") + fmt.Println("1. Keep existing identities.json unchanged") + fmt.Println("2. Legacy actions are automatically converted to AWS policies internally") + fmt.Println("3. Add bucket policies for advanced features:") + fmt.Println(" - IP restrictions") + fmt.Println(" - Time-based access") + fmt.Println(" - SSL-only access") + fmt.Println(" - User agent restrictions") + fmt.Println("4. Policy evaluation precedence:") + fmt.Println(" - Explicit Deny (highest priority)") + fmt.Println(" - Explicit Allow") + fmt.Println(" - Default Deny (lowest priority)") +} + +// PrintAllExamples prints all example policies +func PrintAllExamples() { + examples := GetAllExamples() + + for name := range examples { + fmt.Printf("\n") + PrintExamplePolicyPretty(name) + fmt.Printf("\n") + } +} |
