Skip to content

Commit

Permalink
Merge pull request #71 from strongdm/idx-470/entity-getter
Browse files Browse the repository at this point in the history
IDX-470: Change PolicySet.IsAuthorized to take an EntityGetter interface in place of an EntityMap
  • Loading branch information
kjamieson-sdm authored Dec 3, 2024
2 parents 9d338bc + 316f277 commit 224fafe
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 11 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,9 @@ Generated documentation for the latest version of the Go implementation can be a
If you're looking to integrate Cedar into a production system, please be sure the read the [security best practices](https://docs.cedarpolicy.com/other/security.html)

## Backward Compatibility Considerations

x/exp - code in this directory is not subject to the semantic version constraints of the rest of the module and breaking changes may be made at any time
- `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.
- Variadics may be added to functions that do not have them to expand the arguments of a function or method.
- Concrete types may be replaced with compatible interfaces to expand the variety of arguments a function or method can take.

## Change log

Expand Down
2 changes: 1 addition & 1 deletion authorize.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const (

// IsAuthorized uses the combination of the PolicySet and Entities to determine
// if the given Request to determine Decision and Diagnostic.
func (p PolicySet) IsAuthorized(entities types.EntityMap, req Request) (Decision, Diagnostic) {
func (p PolicySet) IsAuthorized(entities types.EntityGetter, req Request) (Decision, Diagnostic) {
env := eval.Env{
Entities: entities,
Principal: req.Principal,
Expand Down
14 changes: 7 additions & 7 deletions internal/eval/evalers.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func zeroValue() types.Value {
}

type Env struct {
Entities types.EntityMap
Entities types.EntityGetter
Principal, Action, Resource types.Value
Context types.Value
}
Expand Down Expand Up @@ -754,7 +754,7 @@ func (n *attributeAccessEval) Eval(env Env) (types.Value, error) {
if vv == unspecified {
return zeroValue(), fmt.Errorf("cannot access attribute `%s` of %w", n.attribute, errUnspecifiedEntity)
}
rec, ok := env.Entities[vv]
rec, ok := env.Entities.Get(vv)
if !ok {
return zeroValue(), fmt.Errorf("entity `%v` %w", vv.String(), errEntityNotExist)
}
Expand Down Expand Up @@ -792,7 +792,7 @@ func (n *hasEval) Eval(env Env) (types.Value, error) {
var record types.Record
switch vv := v.(type) {
case types.EntityUID:
if rec, ok := env.Entities[vv]; ok {
if rec, ok := env.Entities.Get(vv); ok {
record = rec.Attributes
}
case types.Record:
Expand Down Expand Up @@ -861,12 +861,12 @@ func entityInOne(env Env, entity types.EntityUID, parent types.EntityUID) bool {
var todo []types.EntityUID
var candidate = entity
for {
if fe, ok := env.Entities[candidate]; ok {
if fe, ok := env.Entities.Get(candidate); ok {
if fe.Parents.Contains(parent) {
return true
}
fe.Parents.Iterate(func(k types.EntityUID) bool {
p, ok := env.Entities[k]
p, ok := env.Entities.Get(k)
if !ok || p.Parents.Len() == 0 || k == entity || known.Contains(k) {
return true
}
Expand All @@ -890,12 +890,12 @@ func entityInSet(env Env, entity types.EntityUID, parents mapset.Container[types
var todo []types.EntityUID
var candidate = entity
for {
if fe, ok := env.Entities[candidate]; ok {
if fe, ok := env.Entities.Get(candidate); ok {
if fe.Parents.Intersects(parents) {
return true
}
fe.Parents.Iterate(func(k types.EntityUID) bool {
p, ok := env.Entities[k]
p, ok := env.Entities.Get(k)
if !ok || p.Parents.Len() == 0 || k == entity || known.Contains(k) {
return true
}
Expand Down
2 changes: 1 addition & 1 deletion internal/eval/partial.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ func (n *partialHasEval) Eval(env Env) (types.Value, error) {
var record types.Record
switch vv := v.(type) {
case types.EntityUID:
if rec, ok := env.Entities[vv]; ok {
if rec, ok := env.Entities.Get(vv); ok {
record = rec.Attributes
}
case types.Record:
Expand Down
12 changes: 12 additions & 0 deletions types/entity_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ import (
"golang.org/x/exp/maps"
)

// An EntityGetter is an interface for retrieving an Entity by EntityUID.
type EntityGetter interface {
Get(uid EntityUID) (Entity, bool)
}

var _ EntityGetter = EntityMap{}

// An EntityMap is a collection of all the entities that are needed to evaluate
// authorization requests. The key is an EntityUID which uniquely identifies
// the Entity (it must be the same as the UID within the Entity itself.)
Expand Down Expand Up @@ -37,3 +44,8 @@ func (e *EntityMap) UnmarshalJSON(b []byte) error {
func (e EntityMap) Clone() EntityMap {
return maps.Clone(e)
}

func (e EntityMap) Get(uid EntityUID) (Entity, bool) {
ent, ok := e[uid]
return ent, ok
}
15 changes: 15 additions & 0 deletions types/entity_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ func TestEntities(t *testing.T) {
testutil.Equals(t, clone, e)
})

t.Run("Get", func(t *testing.T) {
t.Parallel()
ent := types.Entity{
UID: types.NewEntityUID("Type", "id"),
Attributes: types.NewRecord(types.RecordMap{"key": types.Long(42)}),
}
e := types.EntityMap{
ent.UID: ent,
}
got, ok := e.Get(ent.UID)
testutil.Equals(t, ok, true)
testutil.Equals(t, got, ent)
_, ok = e.Get(types.NewEntityUID("Type", "id2"))
testutil.Equals(t, ok, false)
})
}

func TestEntitiesJSON(t *testing.T) {
Expand Down

0 comments on commit 224fafe

Please sign in to comment.