1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
package s3
import (
"encoding/json"
"errors"
"fmt"
"sort"
)
const (
roleAdmin = "admin"
roleReadOnly = "read-only"
)
// Policy defines the S3 policy.
type Policy struct {
Version string
Statement []PolicyStatement
}
// PolicyStatement defines the S3 policy statement.
type PolicyStatement struct {
Effect string
Action []string
Resource []string
}
// BucketPolicy generates an S3 bucket policy for role.
func BucketPolicy(bucketName string, roleName string) (json.RawMessage, error) {
switch roleName {
case roleAdmin:
return fmt.Appendf(nil, `{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::%s/*"
]
}]
}`, bucketName), nil
case roleReadOnly:
return fmt.Appendf(nil, `{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": [
"arn:aws:s3:::%s/*"
]
}]
}`, bucketName), nil
}
return nil, errors.New("Invalid key role")
}
// BucketPolicyRole compares the given bucket policy with the predefined bucket policies
// and returns the role name of the matching policy.
func BucketPolicyRole(bucketName string, jsonPolicy string) (string, error) {
var policy Policy
err := json.Unmarshal([]byte(jsonPolicy), &policy)
if err != nil {
return "", err
}
predefinedRoles := []string{roleAdmin, roleReadOnly}
for _, role := range predefinedRoles {
var rolePolicy Policy
jsonRolePolicy, err := BucketPolicy(bucketName, role)
if err != nil {
return "", err
}
err = json.Unmarshal([]byte(jsonRolePolicy), &rolePolicy)
if err != nil {
return "", err
}
matches := comparePolicy(policy, rolePolicy)
if matches {
return role, nil
}
}
return "", errors.New("Policy does not match any role")
}
// comparePolicy checks whether two policies are equal.
func comparePolicy(policyA Policy, policyB Policy) bool {
if policyA.Version != policyB.Version {
return false
}
if len(policyA.Statement) != len(policyB.Statement) {
return false
}
for i := range policyA.Statement {
psA := policyA.Statement[i]
psB := policyB.Statement[i]
if psA.Effect != psB.Effect {
return false
}
if len(psA.Action) != len(psB.Action) {
return false
}
if len(psA.Resource) != len(psB.Resource) {
return false
}
sort.Strings(psA.Action)
sort.Strings(psB.Action)
for j := range psA.Action {
if psA.Action[j] != psB.Action[j] {
return false
}
}
sort.Strings(psB.Resource)
sort.Strings(psB.Resource)
for j := range psA.Resource {
if psA.Resource[j] != psB.Resource[j] {
return false
}
}
}
return true
}
|