Files
ucan/pkg/policy/match.go

202 lines
4.6 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-09-24 11:40:28 -04:00
"github.com/ucan-wg/go-ucan/pkg/policy/selector"
2024-08-19 23:16:36 +02:00
)
func (p Policy) Filter(sel selector.Selector) Policy {
2024-09-24 19:36:01 +02:00
return p.FilterWithMatcher(sel, selector.SegmentEquals)
}
// FilterWithMatcher extracts a subset of the policy related to the specified selector,
// by matching each segment using the given selector.SegmentMatcher.
func (p Policy) FilterWithMatcher(sel selector.Selector, matcher selector.SegmentMatcher) Policy {
var filtered Policy
for _, stmt := range p {
2024-09-24 19:36:01 +02:00
if stmt.Selector().Matches(sel, matcher) {
filtered = append(filtered, stmt)
}
}
return filtered
}
// Match determines if the IPLD node matches the policy document.
func (p Policy) Match(node datamodel.Node) bool {
for _, stmt := range p {
ok := matchStatement(stmt, node)
if !ok {
return false
}
}
2024-09-24 11:36:31 +02:00
return true
}
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:
if s, ok := statement.(equality); ok {
2024-09-16 13:00:13 +02:00
one, many, err := selector.Select(s.selector, node)
if err != nil {
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
2024-09-16 13:00:13 +02:00
if one != nil {
return datamodel.DeepEqual(s.value, one)
}
if many != nil {
for _, n := range many {
if eq := datamodel.DeepEqual(s.value, n); eq {
return true
}
}
}
return false
2024-08-19 23:16:36 +02:00
}
case KindGreaterThan:
if s, ok := statement.(equality); ok {
one, _, err := selector.Select(s.selector, node)
2024-08-20 15:55:04 +02:00
if err != nil || one == nil {
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
return isOrdered(s.value, one, gt)
2024-08-19 23:16:36 +02:00
}
case KindGreaterThanOrEqual:
if s, ok := statement.(equality); ok {
one, _, err := selector.Select(s.selector, node)
2024-08-20 15:55:04 +02:00
if err != nil || one == nil {
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
return isOrdered(s.value, one, gte)
2024-08-19 23:16:36 +02:00
}
case KindLessThan:
if s, ok := statement.(equality); ok {
one, _, err := selector.Select(s.selector, node)
2024-08-20 15:55:04 +02:00
if err != nil || one == nil {
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
return isOrdered(s.value, one, lt)
2024-08-19 23:16:36 +02:00
}
case KindLessThanOrEqual:
if s, ok := statement.(equality); ok {
one, _, err := selector.Select(s.selector, node)
2024-08-20 15:55:04 +02:00
if err != nil || one == nil {
2024-08-20 22:27:56 +02:00
return false
2024-08-19 23:16:36 +02:00
}
return isOrdered(s.value, one, lte)
2024-08-19 23:16:36 +02:00
}
case KindNot:
if s, ok := statement.(negation); ok {
return !matchStatement(s.statement, node)
2024-08-19 23:16:36 +02:00
}
case KindAnd:
if s, ok := statement.(connective); ok {
for _, cs := range s.statements {
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:
if s, ok := statement.(connective); ok {
if len(s.statements) == 0 {
2024-08-20 22:27:56 +02:00
return true
2024-08-20 15:55:04 +02:00
}
for _, cs := range s.statements {
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:
if s, ok := statement.(wildcard); ok {
one, _, err := selector.Select(s.selector, node)
2024-08-21 08:13:44 +02:00
if err != nil || one == nil {
return false
}
v, err := one.AsString()
if err != nil {
return false
}
return s.pattern.Match(v)
2024-08-21 08:13:44 +02:00
}
case KindAll:
if s, ok := statement.(quantifier); ok {
_, many, err := selector.Select(s.selector, node)
2024-08-21 08:44:17 +02:00
if err != nil || many == nil {
return false
}
for _, n := range many {
ok := matchStatement(s.statement, n)
2024-08-21 08:44:17 +02:00
if !ok {
return false
}
}
return true
}
case KindAny:
if s, ok := statement.(quantifier); ok {
2024-09-16 13:00:13 +02:00
one, many, err := selector.Select(s.selector, node)
if err != nil {
2024-08-21 08:44:17 +02:00
return false
}
2024-09-16 13:00:13 +02:00
if one != nil {
ok := matchStatement(s.statement, one)
2024-08-21 08:44:17 +02:00
if ok {
return true
}
}
2024-09-16 13:00:13 +02:00
if many != nil {
for _, n := range many {
ok := matchStatement(s.statement, n)
if ok {
return true
}
}
}
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
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 }