Skip to content

Commit 213bd2f

Browse files
authored
Merge pull request #85 from strongdm/add-support-for-generic-authorize
Add support for generic policy iteration when authorizing a request
2 parents 7486e7d + f06dc9d commit 213bd2f

33 files changed

+634
-217
lines changed
Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
# This workflow will build a golang project
2-
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
3-
4-
name: Go
1+
name: Verify
52

63
on:
74
push:
@@ -14,15 +11,21 @@ jobs:
1411
- uses: actions/checkout@v4
1512

1613
- name: Set up Go
17-
uses: actions/setup-go@v4
14+
uses: actions/setup-go@v5
1815
with:
19-
go-version: "1.22"
16+
go-version-file: 'go.mod'
2017

2118
- name: Build
2219
run: go build -v ./...
2320

2421
- name: Test
25-
run: go test ./...
22+
run: go test -coverprofile=coverage.out ./...
23+
24+
- name: Lint
25+
uses: golangci/golangci-lint-action@v7
2626

2727
- name: Fuzz
2828
run: mkdir -p testdata && go test -fuzz=FuzzParse -fuzztime 60s && go test -fuzz=FuzzTokenize -fuzztime 60s
29+
30+
- name: Coverage check
31+
run: go tool cover -func=coverage.out | sed 's/%$//' | awk '{ if ($3 < 100.0) { printf "Insufficient code coverage for %s\n", $0; failed=1 } } END { exit failed }'

.github/workflows/golangci-lint.yml

Lines changed: 0 additions & 22 deletions
This file was deleted.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.idea/
22
tmp/
33
.DS_Store
4+
coverage.out

README.md

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -139,21 +139,31 @@ If you're looking to integrate Cedar into a production system, please be sure th
139139
- `x/exp` - code in this directory is not subject to the semantic versioning constraints of the rest of the module and breaking changes may be made at any time.
140140
- Variadics may be added to functions that do not have them to expand the arguments of a function or method.
141141
- Concrete types may be replaced with compatible interfaces to expand the variety of arguments a function or method can take.
142+
- Backwards compatibility is maintained for all Go minor versions released within 6 months of a release of cedar-go.
142143

143144
## Change log
144145

145-
### New features in 1.2.0
146+
### 1.2.0
147+
#### New Features
146148
- Support for the .isEmpty() operator.
149+
- A new top-level Authorize() function, which allows authorization against a generic policy iterator (`AuthorizationPolicySet`) instead of requiring a PolicySet. Like the EntityGetter interface does for entities, using a generic iterator enables policy to be retrieved from external sources or for policy to be selected dynamically by the iterator implementation without having to clone an entire PolicySet.
150+
- batch.Authorize() likewise now also accepts an `AuthorizationPolicySet`.
151+
- First class iterator support for EntityUIDSet, Record, Set, and PolicySet container types.
147152

148-
### New features in 1.1.0
153+
#### Upgrading from 1.1.0
154+
- cedar-go now requires Go 1.23
155+
156+
### 1.1.0
157+
#### New features
149158
- Support for entity tags via the .getTag() and .hasTag() operators.
150159

151-
### New features in 1.0.0
160+
### 1.0.0
161+
### New features
152162
- AST builder methods for Cedar datetime and duration literals and their extension methods have been added
153163
- AST builder methods for adding extension function calls with uninterpreted strings
154164
- Small improvement in evaluation runtime performance for large, shallow entity graphs.
155165

156-
### Upgrading from 0.4.x to 1.0.0
166+
#### Upgrading from 0.4.x to 1.0.0
157167

158168
- The `Parents` field on `types.Entity` has been changed to an immutable set type with an interface similar to `types.Set`
159169
- The `UnsafeDecimal()` constructor for the `types.Decimal` type has been removed and replaced with the following safe constructors, which return error on overflow:
@@ -174,12 +184,13 @@ If you're looking to integrate Cedar into a production system, please be sure th
174184
- `PolicySet.Delete()` has been renamed to `PolicySet.Remove()`
175185
- `types.Set()` now takes variadic arguments of type `types.Value` instead of a single `[]types.Value` argument
176186

177-
### New features in 0.4.0
187+
### 0.4.0
188+
#### New features
178189

179190
- `types.Set` is now implemented as a hash set, turning `Set.Contains()` into an O(1) operation, on average. This mitigates a worst case quadratic runtime for the evaluation of the `containsAny()` operator.
180191
- For convenience, public types, constructors, and constants from the `types` package are now exported via the `cedar` package as well.
181192

182-
### Upgrading from 0.3.x to 0.4.x
193+
#### Upgrading from 0.3.x to 0.4.x
183194

184195
- `types.Set` is now an immutable type which must be constructed via `types.NewSet()`
185196
- To iterate the values, use `Set.Iterate()`, which takes an iterator callback.
@@ -189,29 +200,32 @@ If you're looking to integrate Cedar into a production system, please be sure th
189200
- To iterate the keys and values, use `Record.Iterate()`, which takes an iterator callback.
190201
- All implementations of `types.Value` are now safe to copy shallowly, so `Record.DeepClone()` has been removed.
191202

192-
### New features in 0.3.2
203+
### 0.3.2
204+
#### New features
193205

194206
- An implementation of the `datetime` and `duration` extension types specified in [RFC 80](https://github.com/cedar-policy/rfcs/blob/main/text/0080-datetime-extension.md).
195207
- Note: While these types have been accepted into the language, they have not yet been formally analyzed in the [specification](https://github.com/cedar-policy/cedar-spec/).
196208

197-
### New features in 0.3.1
209+
### 0.3.1
210+
#### New features
198211

199212
- General performance improvements to the evaluator
200213
- An experimental batch evaluator has been added to `x/exp/batch`
201214
- Reserved keywords are now rejected in all appropriate places when parsing Cedar text
202215
- A parsing ambiguity between variables, entity UIDs, and extension functions has been resolved
203216

204-
### Upgrading from 0.2.x to 0.3.x
217+
#### Upgrading from 0.2.x to 0.3.x
205218

206219
- The JSON marshaling of the Position struct now uses canonical lower-case keys for its fields
207220

208-
### New features in 0.2.0
221+
### 0.2.0
222+
#### New features
209223

210224
- A programmatic AST is now available in the `ast` package.
211225
- Policy sets can be marshaled and unmarshaled from JSON.
212226
- Policies can also be marshaled to Cedar text.
213227

214-
### Upgrading from 0.1.x to 0.2.x
228+
#### Upgrading from 0.1.x to 0.2.x
215229

216230
- The Cedar value types have moved from the `cedar` package to the `types` package.
217231
- The PolicyIDs are now `strings`, previously they were numeric.

ast/ast_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,11 @@ func TestASTByTable(t *testing.T) {
337337
ast.Permit().When(ast.Long(42).ContainsAny(ast.Long(43))),
338338
internalast.Permit().When(internalast.Long(42).ContainsAny(internalast.Long(43))),
339339
},
340+
{
341+
"opContainsIsEmpty",
342+
ast.Permit().When(ast.Long(42).IsEmpty()),
343+
internalast.Permit().When(internalast.Long(42).IsEmpty()),
344+
},
340345
{
341346
"opAccess",
342347
ast.Permit().When(ast.Long(42).Access("key")),

ast/policy_test.go

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import (
1212
func TestPolicy_MarshalJSON(t *testing.T) {
1313
t.Parallel()
1414

15-
p := ast.Permit().PrincipalEq(types.NewEntityUID("Foo::Bar", "Baz"))
16-
expected := `{
15+
t.Run("roundtrip", func(t *testing.T) {
16+
p := ast.Permit().PrincipalEq(types.NewEntityUID("Foo::Bar", "Baz"))
17+
expected := `{
1718
"effect": "permit",
1819
"principal": {
1920
"op": "==",
@@ -29,30 +30,45 @@ func TestPolicy_MarshalJSON(t *testing.T) {
2930
"op": "All"
3031
}
3132
}`
32-
testutil.JSONMarshalsTo(t, p, expected)
33+
testutil.JSONMarshalsTo(t, p, expected)
3334

34-
var unmarshaled ast.Policy
35-
err := unmarshaled.UnmarshalJSON([]byte(expected))
36-
testutil.OK(t, err)
37-
testutil.Equals(t, &unmarshaled, p)
35+
var unmarshaled ast.Policy
36+
err := unmarshaled.UnmarshalJSON([]byte(expected))
37+
testutil.OK(t, err)
38+
testutil.Equals(t, &unmarshaled, p)
39+
})
40+
41+
t.Run("unmarshal error", func(t *testing.T) {
42+
var unmarshaled ast.Policy
43+
err := unmarshaled.UnmarshalJSON([]byte("[]"))
44+
testutil.Error(t, err)
45+
})
3846
}
3947

4048
func TestPolicy_MarshalCedar(t *testing.T) {
4149
t.Parallel()
4250

43-
p := ast.Permit().PrincipalEq(types.NewEntityUID("Foo::Bar", "Baz"))
44-
expected := `permit (
51+
t.Run("roundtrip", func(t *testing.T) {
52+
p := ast.Permit().PrincipalEq(types.NewEntityUID("Foo::Bar", "Baz"))
53+
expected := `permit (
4554
principal == Foo::Bar::"Baz",
4655
action,
4756
resource
4857
);`
4958

50-
testutil.Equals(t, string(p.MarshalCedar()), expected)
59+
testutil.Equals(t, string(p.MarshalCedar()), expected)
60+
61+
var unmarshaled ast.Policy
62+
err := unmarshaled.UnmarshalCedar([]byte(expected))
5163

52-
var unmarshaled ast.Policy
53-
err := unmarshaled.UnmarshalCedar([]byte(expected))
64+
p.Position = internalast.Position{Offset: 0, Line: 1, Column: 1}
65+
testutil.OK(t, err)
66+
testutil.Equals(t, &unmarshaled, p)
67+
})
5468

55-
p.Position = internalast.Position{Offset: 0, Line: 1, Column: 1}
56-
testutil.OK(t, err)
57-
testutil.Equals(t, &unmarshaled, p)
69+
t.Run("unmarshal error", func(t *testing.T) {
70+
var unmarshaled ast.Policy
71+
err := unmarshaled.UnmarshalCedar([]byte("this isn't Cedar"))
72+
testutil.Error(t, err)
73+
})
5874
}

authorize.go

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,21 @@
11
package cedar
22

33
import (
4+
"iter"
5+
46
"github.com/cedar-policy/cedar-go/internal/eval"
57
"github.com/cedar-policy/cedar-go/types"
68
)
79

8-
//revive:disable:exported
9-
10-
type Request = types.Request
11-
type Decision = types.Decision
12-
type Diagnostic = types.Diagnostic
13-
type DiagnosticReason = types.DiagnosticReason
14-
type DiagnosticError = types.DiagnosticError
15-
16-
const (
17-
Allow = types.Allow
18-
Deny = types.Deny
19-
)
20-
21-
//revive:enable:exported
10+
// AuthorizationPolicySet is an interface which abstracts an iterable set of policies.
11+
type AuthorizationPolicySet interface {
12+
// All returns an iterator over all the policies in the set
13+
All() iter.Seq2[PolicyID, *Policy]
14+
}
2215

23-
// IsAuthorized uses the combination of the PolicySet and Entities to determine
16+
// Authorize uses the combination of the PolicySet and Entities to determine
2417
// if the given Request to determine Decision and Diagnostic.
25-
func (p PolicySet) IsAuthorized(entities types.EntityGetter, req Request) (Decision, Diagnostic) {
18+
func Authorize(policies AuthorizationPolicySet, entities types.EntityGetter, req Request) (Decision, Diagnostic) {
2619
if entities == nil {
2720
var zero types.EntityMap
2821
entities = zero
@@ -42,7 +35,7 @@ func (p PolicySet) IsAuthorized(entities types.EntityGetter, req Request) (Decis
4235
// - All policy should be run to collect errors
4336
// - For permit, all permits must be run to collect annotations
4437
// - For forbid, forbids must be run to collect annotations
45-
for id, po := range p.policies {
38+
for id, po := range policies.All() {
4639
result, err := po.eval.Eval(env)
4740
if err != nil {
4841
diag.Errors = append(diag.Errors, DiagnosticError{PolicyID: id, Position: po.Position(), Message: err.Error()})

authorize_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,15 @@ func TestIsAuthorized(t *testing.T) {
835835
})
836836
testutil.Equals(t, len(diag.Errors), tt.DiagErr)
837837
testutil.Equals(t, ok, tt.Want)
838+
839+
ok, diag = cedar.Authorize(ps, tt.Entities, cedar.Request{
840+
Principal: tt.Principal,
841+
Action: tt.Action,
842+
Resource: tt.Resource,
843+
Context: tt.Context,
844+
})
845+
testutil.Equals(t, len(diag.Errors), tt.DiagErr)
846+
testutil.Equals(t, ok, tt.Want)
838847
})
839848
}
840849
}

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module github.com/cedar-policy/cedar-go
22

3-
go 1.22
3+
go 1.23.0
44

5-
require golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
5+
require golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
2-
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
1+
golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e h1:Ctm9yurWsg7aWwIpH9Bnap/IdSVxixymIb3MhiMEQQA=
2+
golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=

0 commit comments

Comments
 (0)