You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							463 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							463 lines
						
					
					
						
							11 KiB
						
					
					
				| //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") | |
| 	} | |
| }
 |