Files
ucan/capability/policy/match.go

164 lines
3.9 KiB
Go
Raw Normal View History

2024-08-19 23:16:36 +02:00
package policy
import (
"cmp"
"fmt"
"github.com/ipld/go-ipld-prime"
2024-08-20 22:27:56 +02:00
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/must"
2024-08-30 23:11:51 +02:00
"github.com/ucan-wg/go-ucan/v1/capability/policy/selector"
2024-08-19 23:16:36 +02:00
)
// Match determines if the IPLD node matches the policy document.
2024-08-20 22:27:56 +02:00
func Match(policy Policy, node ipld.Node) bool {
2024-08-19 23:16:36 +02:00
for _, stmt := range policy {
2024-08-20 22:27:56 +02:00
ok := matchStatement(stmt, node)
if !ok {
2024-08-21 08:44:17 +02:00
return false
2024-08-19 23:16:36 +02:00
}
}
2024-08-20 22:27:56 +02:00
return true
2024-08-19 23:16:36 +02:00
}
2024-08-20 22:27:56 +02:00
func matchStatement(statement Statement, node ipld.Node) bool {
2024-08-19 23:16:36 +02:00
switch statement.Kind() {
case KindEqual:
2024-08-19 23:16:36 +02:00
if s, ok := statement.(EqualityStatement); ok {
2024-08-20 15:55:04 +02:00
one, _, err := selector.Select(s.Selector(), node)
if err != nil || one == nil {
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
2024-08-20 22:27:56 +02:00
return datamodel.DeepEqual(s.Value(), one)
2024-08-19 23:16:36 +02:00
}
case KindGreaterThan:
2024-08-19 23:16:36 +02:00
if s, ok := statement.(InequalityStatement); ok {
2024-08-20 15:55:04 +02:00
one, _, err := selector.Select(s.Selector(), node)
if err != nil || one == nil {
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
2024-08-20 15:55:04 +02:00
return isOrdered(s.Value(), one, gt)
2024-08-19 23:16:36 +02:00
}
case KindGreaterThanOrEqual:
2024-08-19 23:16:36 +02:00
if s, ok := statement.(InequalityStatement); ok {
2024-08-20 15:55:04 +02:00
one, _, err := selector.Select(s.Selector(), node)
if err != nil || one == nil {
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
2024-08-20 15:55:04 +02:00
return isOrdered(s.Value(), one, gte)
2024-08-19 23:16:36 +02:00
}
case KindLessThan:
2024-08-19 23:16:36 +02:00
if s, ok := statement.(InequalityStatement); ok {
2024-08-20 15:55:04 +02:00
one, _, err := selector.Select(s.Selector(), node)
if err != nil || one == nil {
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
2024-08-20 15:55:04 +02:00
return isOrdered(s.Value(), one, lt)
2024-08-19 23:16:36 +02:00
}
case KindLessThanOrEqual:
2024-08-19 23:16:36 +02:00
if s, ok := statement.(InequalityStatement); ok {
2024-08-20 15:55:04 +02:00
one, _, err := selector.Select(s.Selector(), node)
if err != nil || one == nil {
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
2024-08-20 15:55:04 +02:00
return isOrdered(s.Value(), one, lte)
2024-08-19 23:16:36 +02:00
}
case KindNot:
2024-08-19 23:16:36 +02:00
if s, ok := statement.(NegationStatement); ok {
2024-08-20 22:27:56 +02:00
return !matchStatement(s.Value(), node)
2024-08-19 23:16:36 +02:00
}
case KindAnd:
2024-08-19 23:16:36 +02:00
if s, ok := statement.(ConjunctionStatement); ok {
for _, cs := range s.Value() {
2024-08-20 22:27:56 +02:00
r := matchStatement(cs, node)
2024-08-19 23:16:36 +02:00
if !r {
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
}
2024-08-20 22:27:56 +02:00
return true
2024-08-19 23:16:36 +02:00
}
case KindOr:
2024-08-19 23:16:36 +02:00
if s, ok := statement.(DisjunctionStatement); ok {
2024-08-20 15:55:04 +02:00
if len(s.Value()) == 0 {
2024-08-20 22:27:56 +02:00
return true
2024-08-20 15:55:04 +02:00
}
2024-08-19 23:16:36 +02:00
for _, cs := range s.Value() {
2024-08-20 22:27:56 +02:00
r := matchStatement(cs, node)
2024-08-19 23:16:36 +02:00
if r {
2024-08-20 22:27:56 +02:00
return true
2024-08-19 23:16:36 +02:00
}
}
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
case KindLike:
2024-08-21 08:13:44 +02:00
if s, ok := statement.(WildcardStatement); ok {
one, _, err := selector.Select(s.Selector(), node)
if err != nil || one == nil {
return false
}
v, err := one.AsString()
if err != nil {
return false
}
return s.Value().Match(v)
}
case KindAll:
2024-08-21 08:44:17 +02:00
if s, ok := statement.(QuantifierStatement); ok {
_, many, err := selector.Select(s.Selector(), node)
if err != nil || many == nil {
return false
}
for _, n := range many {
ok := Match(s.Value(), n)
if !ok {
return false
}
}
return true
}
case KindAny:
2024-08-21 08:44:17 +02:00
if s, ok := statement.(QuantifierStatement); ok {
_, many, err := selector.Select(s.Selector(), node)
if err != nil || many == nil {
return false
}
for _, n := range many {
ok := Match(s.Value(), n)
if ok {
return true
}
}
return false
}
2024-08-19 23:16:36 +02:00
}
2024-08-20 22:27:56 +02:00
panic(fmt.Errorf("unimplemented statement kind: %s", statement.Kind()))
2024-08-19 23:16:36 +02:00
}
2024-08-20 22:27:56 +02:00
func isOrdered(expected ipld.Node, actual ipld.Node, satisfies func(order int) bool) bool {
2024-08-20 15:55:04 +02:00
if expected.Kind() == ipld.Kind_Int && actual.Kind() == ipld.Kind_Int {
2024-08-20 22:27:56 +02:00
a := must.Int(actual)
b := must.Int(expected)
return satisfies(cmp.Compare(a, b))
2024-08-19 23:16:36 +02:00
}
2024-08-20 15:55:04 +02:00
if expected.Kind() == ipld.Kind_Float && actual.Kind() == ipld.Kind_Float {
a, err := actual.AsFloat()
2024-08-19 23:16:36 +02:00
if err != nil {
2024-08-20 22:27:56 +02:00
panic(fmt.Errorf("extracting node float: %w", err))
2024-08-19 23:16:36 +02:00
}
2024-08-20 15:55:04 +02:00
b, err := expected.AsFloat()
2024-08-19 23:16:36 +02:00
if err != nil {
2024-08-20 22:27:56 +02:00
panic(fmt.Errorf("extracting selector float: %w", err))
2024-08-19 23:16:36 +02:00
}
2024-08-20 22:27:56 +02:00
return satisfies(cmp.Compare(a, b))
2024-08-19 23:16:36 +02:00
}
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
func gt(order int) bool { return order == 1 }
func gte(order int) bool { return order == 0 || order == 1 }
func lt(order int) bool { return order == -1 }
func lte(order int) bool { return order == 0 || order == -1 }