forked from cedar-policy/cedar-go
-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathpolicy_set.go
More file actions
156 lines (137 loc) · 4.83 KB
/
policy_set.go
File metadata and controls
156 lines (137 loc) · 4.83 KB
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
145
146
147
148
149
150
151
152
153
154
155
156
// Package cedar provides an implementation of the Cedar language authorizer.
package cedar
import (
"bytes"
"encoding/json"
"fmt"
"iter"
"maps"
"slices"
internaljson "github.com/cedar-policy/cedar-go/internal/json"
"github.com/cedar-policy/cedar-go/types"
"github.com/cedar-policy/cedar-go/x/exp/ast"
)
//revive:disable-next-line:exported
type PolicyID = types.PolicyID
// PolicyMap is a map of policy IDs to policy
type PolicyMap map[PolicyID]*Policy
// All returns an iterator over the policy IDs and policies in the PolicyMap.
func (p PolicyMap) All() iter.Seq2[PolicyID, *Policy] {
return maps.All(p)
}
// PolicySet is a set of named policies against which a request can be authorized.
type PolicySet struct {
// policies are stored internally so we can handle performance, concurrency bookkeeping however we want
policies PolicyMap
}
// NewPolicySet creates a new, empty PolicySet
func NewPolicySet() *PolicySet {
return &PolicySet{policies: PolicyMap{}}
}
// NewPolicySetFromBytes will create a PolicySet from the given text document with the given file name used in Position
// data. If there is an error parsing the document, it will be returned.
//
// NewPolicySetFromBytes assigns default PolicyIDs to the policies contained in fileName in the format "policy<n>" where
// <n> is incremented for each new policy found in the file.
func NewPolicySetFromBytes(fileName string, document []byte) (*PolicySet, error) {
policySlice, err := NewPolicyListFromBytes(fileName, document)
if err != nil {
return &PolicySet{}, err
}
policyMap := make(PolicyMap, len(policySlice))
for i, p := range policySlice {
policyID := PolicyID(fmt.Sprintf("policy%d", i))
policyMap[policyID] = p
}
return &PolicySet{policies: policyMap}, nil
}
// Get returns the Policy with the given ID. If a policy with the given ID
// does not exist, nil is returned.
func (p *PolicySet) Get(policyID PolicyID) *Policy {
return p.policies[policyID]
}
// Add inserts or updates a policy with the given ID. Returns true if a policy
// with the given ID did not already exist in the set.
func (p *PolicySet) Add(policyID PolicyID, policy *Policy) bool {
_, exists := p.policies[policyID]
p.policies[policyID] = policy
return !exists
}
// Remove removes a policy from the PolicySet. Returns true if a policy with
// the given ID already existed in the set.
func (p *PolicySet) Remove(policyID PolicyID) bool {
_, exists := p.policies[policyID]
delete(p.policies, policyID)
return exists
}
// Map returns a new PolicyMap instance of the policies in the PolicySet.
//
// Deprecated: use the iterator returned by All() like so: maps.Collect(ps.All())
func (p *PolicySet) Map() PolicyMap {
return maps.Clone(p.policies)
}
// MarshalCedar emits a concatenated Cedar representation of a PolicySet. The policy names are stripped, but policies
// are emitted in lexicographical order by ID.
func (p *PolicySet) MarshalCedar() []byte {
ids := make([]PolicyID, 0, len(p.policies))
for k := range p.policies {
ids = append(ids, k)
}
slices.Sort(ids)
var buf bytes.Buffer
i := 0
for _, id := range ids {
policy := p.policies[id]
buf.Write(policy.MarshalCedar())
if i < len(p.policies)-1 {
buf.WriteString("\n\n")
}
i++
}
return buf.Bytes()
}
// MarshalJSON encodes a PolicySet in the JSON format specified by the [Cedar documentation].
//
// [Cedar documentation]: https://docs.cedarpolicy.com/policies/json-format.html
func (p *PolicySet) MarshalJSON() ([]byte, error) {
jsonPolicySet := internaljson.PolicySetJSON{
StaticPolicies: make(internaljson.PolicySet, len(p.policies)),
}
for k, v := range p.policies {
jsonPolicySet.StaticPolicies[string(k)] = (*internaljson.Policy)(v.ast)
}
return json.Marshal(jsonPolicySet)
}
// UnmarshalJSON parses and compiles a PolicySet in the JSON format specified by the [Cedar documentation].
//
// [Cedar documentation]: https://docs.cedarpolicy.com/policies/json-format.html
func (p *PolicySet) UnmarshalJSON(b []byte) error {
var jsonPolicySet internaljson.PolicySetJSON
if err := json.Unmarshal(b, &jsonPolicySet); err != nil {
return err
}
*p = PolicySet{
policies: make(PolicyMap, len(jsonPolicySet.StaticPolicies)),
}
for k, v := range jsonPolicySet.StaticPolicies {
p.policies[PolicyID(k)] = newPolicy((*ast.Policy)(v))
}
return nil
}
// IsAuthorized uses the combination of the PolicySet and Entities to determine
// if the given Request to determine Decision and Diagnostic.
//
// Deprecated: Use the Authorize() function instead
func (p *PolicySet) IsAuthorized(entities types.EntityGetter, req Request) (Decision, Diagnostic) {
return Authorize(p, entities, req)
}
// All returns an iterator over the (PolicyID, *Policy) tuples in the PolicySet
func (p *PolicySet) All() iter.Seq2[PolicyID, *Policy] {
return func(yield func(PolicyID, *Policy) bool) {
for k, v := range p.policies {
if !yield(k, v) {
break
}
}
}
}