Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC-82: Implement Support for Cedar Tags #11

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions ast/ast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,16 @@ func TestASTByTable(t *testing.T) {
ast.Permit().When(ast.Long(42).Has("key")),
internalast.Permit().When(internalast.Long(42).Has("key")),
},
{
"opGetTag",
ast.Permit().When(ast.Long(42).GetTag(ast.String("key"))),
internalast.Permit().When(internalast.Long(42).GetTag(internalast.String("key"))),
},
{
"opsHasTag",
ast.Permit().When(ast.Long(42).HasTag(ast.String("key"))),
internalast.Permit().When(internalast.Long(42).HasTag(internalast.String("key"))),
},
{
"opIsIpv4",
ast.Permit().When(ast.Long(42).IsIpv4()),
Expand Down
8 changes: 8 additions & 0 deletions ast/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ func (lhs Node) Has(attr types.String) Node {
return wrapNode(lhs.Node.Has(attr))
}

func (lhs Node) GetTag(rhs Node) Node {
return wrapNode(lhs.Node.GetTag(rhs.Node))
}

func (lhs Node) HasTag(rhs Node) Node {
return wrapNode(lhs.Node.HasTag(rhs.Node))
}

// ___ ____ _ _ _
// |_ _| _ \ / \ __| | __| |_ __ ___ ___ ___
// | || |_) / _ \ / _` |/ _` | '__/ _ \/ __/ __|
Expand Down
3 changes: 3 additions & 0 deletions authorize.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ 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.EntityGetter, req Request) (Decision, Diagnostic) {
if entities == nil {
entities = types.EntityMap{}
}
env := eval.Env{
Entities: entities,
Principal: req.Principal,
Expand Down
15 changes: 13 additions & 2 deletions authorize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/cedar-policy/cedar-go"
"github.com/cedar-policy/cedar-go/internal/testutil"
"github.com/cedar-policy/cedar-go/types"
)

//nolint:revive // due to table test function-length
Expand All @@ -15,7 +16,7 @@ func TestIsAuthorized(t *testing.T) {
tests := []struct {
Name string
Policy string
Entities cedar.EntityMap
Entities types.EntityGetter
Principal, Action, Resource cedar.EntityUID
Context cedar.Record
Want cedar.Decision
Expand All @@ -33,6 +34,16 @@ func TestIsAuthorized(t *testing.T) {
Want: true,
DiagErr: 0,
},
{
Name: "nil-entity-map",
Policy: `permit(principal,action,resource);`,
Principal: cuzco,
Action: dropTable,
Resource: cedar.NewEntityUID("table", "whatever"),
Context: cedar.Record{},
Want: true,
DiagErr: 0,
},
{
Name: "simple-forbid",
Policy: `forbid(principal,action,resource);`,
Expand Down Expand Up @@ -784,7 +795,7 @@ func TestIsAuthorized(t *testing.T) {
for _, tt := range tests {
tt := tt
t.Run(tt.Name, func(t *testing.T) {
t.Parallel()
// t.Parallel()
ps, err := cedar.NewPolicySetFromBytes("policy.cedar", []byte(tt.Policy))
testutil.Equals(t, err != nil, tt.ParseErr)
ok, diag := ps.IsAuthorized(tt.Entities, cedar.Request{
Expand Down
Binary file modified corpus-tests.tar.gz
Binary file not shown.
27 changes: 26 additions & 1 deletion corpus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/cedar-policy/cedar-go"
"github.com/cedar-policy/cedar-go/internal/testutil"
"github.com/cedar-policy/cedar-go/types"
"github.com/cedar-policy/cedar-go/x/exp/batch"
)

Expand Down Expand Up @@ -244,11 +245,27 @@ func TestCorpusRelated(t *testing.T) {
tests := []struct {
name string
policy string
entities types.EntityGetter
request cedar.Request
decision cedar.Decision
reasons []cedar.PolicyID
errors []cedar.PolicyID
}{
{
"a9fe7e4b20024dc7818a168c67ce312d6e076b93",
`forbid(
principal,
action in [Action::"action",Action::"action"],
resource
) when {
true && (resource.hasTag("A"))
};`,
types.EntityMap{cedar.NewEntityUID("a", ""): cedar.Entity{Attributes: cedar.NewRecord(cedar.RecordMap{"A": types.False})}},
cedar.Request{Principal: cedar.NewEntityUID("a", ""), Action: cedar.NewEntityUID("Action", "action"), Resource: cedar.NewEntityUID("a", "'")},
cedar.Deny,
nil,
nil,
},
{
"0cb1ad7042508e708f1999284b634ed0f334bc00",
`forbid(
Expand All @@ -258,6 +275,7 @@ func TestCorpusRelated(t *testing.T) {
) when {
(true && (((!870985681610) == principal) == principal)) && principal
};`,
nil,
cedar.Request{Principal: cedar.NewEntityUID("a", "\u0000\u0000"), Action: cedar.NewEntityUID("Action", "action"), Resource: cedar.NewEntityUID("a", "\u0000\u0000")},
cedar.Deny,
nil,
Expand All @@ -273,6 +291,7 @@ func TestCorpusRelated(t *testing.T) {
) when {
(((!870985681610) == principal) == principal)
};`,
nil,
cedar.Request{Principal: cedar.NewEntityUID("a", "\u0000\u0000"), Action: cedar.NewEntityUID("Action", "action"), Resource: cedar.NewEntityUID("a", "\u0000\u0000")},
cedar.Deny,
nil,
Expand All @@ -287,6 +306,7 @@ func TestCorpusRelated(t *testing.T) {
) when {
((!870985681610) == principal)
};`,
nil,
cedar.Request{Principal: cedar.NewEntityUID("a", "\u0000\u0000"), Action: cedar.NewEntityUID("Action", "action"), Resource: cedar.NewEntityUID("a", "\u0000\u0000")},
cedar.Deny,
nil,
Expand All @@ -302,6 +322,7 @@ func TestCorpusRelated(t *testing.T) {
) when {
(!870985681610)
};`,
nil,
cedar.Request{Principal: cedar.NewEntityUID("a", "\u0000\u0000"), Action: cedar.NewEntityUID("Action", "action"), Resource: cedar.NewEntityUID("a", "\u0000\u0000")},
cedar.Deny,
nil,
Expand All @@ -317,6 +338,7 @@ func TestCorpusRelated(t *testing.T) {
) when {
((!42) == principal)
};`,
nil,
cedar.Request{},
cedar.Deny,
nil,
Expand All @@ -332,6 +354,7 @@ func TestCorpusRelated(t *testing.T) {
) when {
(!42 == principal)
};`,
nil,
cedar.Request{},
cedar.Deny,
nil,
Expand All @@ -346,6 +369,7 @@ func TestCorpusRelated(t *testing.T) {
) when {
true && ((if (principal in action) then (ip("")) else (if true then (ip("6b6b:f00::32ff:ffff:6368/00")) else (ip("7265:6c69:706d:6f43:5f74:6f70:7374:6f68")))).isMulticast())
};`,
nil,
cedar.Request{Principal: cedar.NewEntityUID("a", "\u0000\b\u0011\u0000R"), Action: cedar.NewEntityUID("Action", "action"), Resource: cedar.NewEntityUID("a", "\u0000\b\u0011\u0000R")},
cedar.Deny,
nil,
Expand All @@ -360,6 +384,7 @@ func TestCorpusRelated(t *testing.T) {
) when {
true && ip("6b6b:f00::32ff:ffff:6368/00").isMulticast()
};`,
nil,
cedar.Request{},
cedar.Deny,
nil,
Expand All @@ -386,7 +411,7 @@ func TestCorpusRelated(t *testing.T) {
t.Parallel()
policy, err := cedar.NewPolicySetFromBytes("", []byte(tt.policy))
testutil.OK(t, err)
ok, diag := policy.IsAuthorized(cedar.EntityMap{}, tt.request)
ok, diag := policy.IsAuthorized(tt.entities, tt.request)
testutil.Equals(t, ok, tt.decision)
var reasons []cedar.PolicyID
for _, n := range diag.Reasons {
Expand Down
74 changes: 73 additions & 1 deletion internal/eval/evalers.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var errOverflow = fmt.Errorf("integer overflow")
var errUnknownExtensionFunction = fmt.Errorf("function does not exist")
var errArity = fmt.Errorf("wrong number of arguments provided to extension function")
var errAttributeAccess = fmt.Errorf("does not have the attribute")
var errTagAccess = fmt.Errorf("does not have the tag")
var errEntityNotExist = fmt.Errorf("does not exist")
var errUnspecifiedEntity = fmt.Errorf("unspecified entity")

Expand Down Expand Up @@ -804,6 +805,73 @@ func (n *hasEval) Eval(env Env) (types.Value, error) {
return types.Boolean(ok), nil
}

// getTagEval
type getTagEval struct {
lhs, rhs Evaler
}

func newGetTagEval(object, tag Evaler) *getTagEval {
return &getTagEval{lhs: object, rhs: tag}
}

func (n *getTagEval) Eval(env Env) (types.Value, error) {
eid, err := evalEntity(n.lhs, env)
if err != nil {
return zeroValue(), err
}

var unspecified types.EntityUID
if eid == unspecified {
return zeroValue(), fmt.Errorf("cannot access tag `%s` of %w", n.rhs, errUnspecifiedEntity)
}

t, err := evalString(n.rhs, env)
if err != nil {
return zeroValue(), err
}

e, ok := env.Entities.Get(eid)
if !ok {
return zeroValue(), fmt.Errorf("entity `%v` %w", eid.String(), errEntityNotExist)
}

val, ok := e.Tags.Get(t)
if !ok {
return zeroValue(), fmt.Errorf("`%s` %w `%s`", eid.String(), errTagAccess, t)
}

return val, nil
}

// hasTagEval
type hasTagEval struct {
lhs, rhs Evaler
}

func newHasTagEval(object, tag Evaler) *hasTagEval {
return &hasTagEval{lhs: object, rhs: tag}
}

func (n *hasTagEval) Eval(env Env) (types.Value, error) {
eid, err := evalEntity(n.lhs, env)
if err != nil {
return zeroValue(), err
}

t, err := evalString(n.rhs, env)
if err != nil {
return zeroValue(), err
}

e, ok := env.Entities.Get(eid)
if !ok {
adowair marked this conversation as resolved.
Show resolved Hide resolved
return types.False, nil
}

_, ok = e.Tags.Get(t)
return types.Boolean(ok), nil
}

// likeEval
type likeEval struct {
lhs Evaler
Expand Down Expand Up @@ -1139,9 +1207,13 @@ func newExtensionEval(name types.Path, args []Evaler) Evaler {

if i, ok := extensions.ExtMap[name]; ok {
if i.Args != len(args) {
return newErrorEval(fmt.Errorf("%w: %s takes %d parameter(s)", errArity, name, i.Args))
return newErrorEval(fmt.Errorf("%w: %s takes %d parameter(s), but %d provided", errArity, name, i.Args, len(args)))
}
switch {
case name == "hasTag":
return newHasTagEval(args[0], args[1])
case name == "getTag":
return newGetTagEval(args[0], args[1])
case name == "datetime":
return newDatetimeLiteralEval(args[0])
case name == "decimal":
Expand Down
Loading
Loading