File: policy.go

package info (click to toggle)
incus 6.0.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 24,392 kB
  • sloc: sh: 16,313; ansic: 3,121; python: 457; makefile: 337; ruby: 51; sql: 50; lisp: 6
file content (144 lines) | stat: -rw-r--r-- 2,783 bytes parent folder | download | duplicates (3)
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
}