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 22:06:59 +02:00
|
|
|
|
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
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 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() {
|
2024-09-01 17:06:21 +02:00
|
|
|
case KindEqual:
|
2024-09-02 02:24:13 +02:00
|
|
|
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
|
|
|
}
|
2024-09-01 17:06:21 +02:00
|
|
|
case KindGreaterThan:
|
2024-09-02 02:24:13 +02:00
|
|
|
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
|
|
|
}
|
2024-09-02 02:24:13 +02:00
|
|
|
return isOrdered(s.value, one, gt)
|
2024-08-19 23:16:36 +02:00
|
|
|
}
|
2024-09-01 17:06:21 +02:00
|
|
|
case KindGreaterThanOrEqual:
|
2024-09-02 02:24:13 +02:00
|
|
|
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
|
|
|
}
|
2024-09-02 02:24:13 +02:00
|
|
|
return isOrdered(s.value, one, gte)
|
2024-08-19 23:16:36 +02:00
|
|
|
}
|
2024-09-01 17:06:21 +02:00
|
|
|
case KindLessThan:
|
2024-09-02 02:24:13 +02:00
|
|
|
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
|
|
|
}
|
2024-09-02 02:24:13 +02:00
|
|
|
return isOrdered(s.value, one, lt)
|
2024-08-19 23:16:36 +02:00
|
|
|
}
|
2024-09-01 17:06:21 +02:00
|
|
|
case KindLessThanOrEqual:
|
2024-09-02 02:24:13 +02:00
|
|
|
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
|
|
|
}
|
2024-09-02 02:24:13 +02:00
|
|
|
return isOrdered(s.value, one, lte)
|
2024-08-19 23:16:36 +02:00
|
|
|
}
|
2024-09-01 17:06:21 +02:00
|
|
|
case KindNot:
|
2024-09-02 02:24:13 +02:00
|
|
|
if s, ok := statement.(negation); ok {
|
|
|
|
|
return !matchStatement(s.statement, node)
|
2024-08-19 23:16:36 +02:00
|
|
|
}
|
2024-09-01 17:06:21 +02:00
|
|
|
case KindAnd:
|
2024-09-02 02:24:13 +02:00
|
|
|
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
|
|
|
}
|
2024-09-01 17:06:21 +02:00
|
|
|
case KindOr:
|
2024-09-02 02:24:13 +02:00
|
|
|
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
|
|
|
}
|
2024-09-02 02:24:13 +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
|
|
|
}
|
2024-09-01 17:06:21 +02:00
|
|
|
case KindLike:
|
2024-09-02 02:24:13 +02:00
|
|
|
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
|
|
|
|
|
}
|
2024-09-18 11:24:37 +02:00
|
|
|
return s.pattern.Match(v)
|
2024-08-21 08:13:44 +02:00
|
|
|
}
|
2024-09-01 17:06:21 +02:00
|
|
|
case KindAll:
|
2024-09-02 02:24:13 +02:00
|
|
|
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 {
|
2024-09-04 15:58:08 +02:00
|
|
|
ok := matchStatement(s.statement, n)
|
2024-08-21 08:44:17 +02:00
|
|
|
if !ok {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
2024-09-01 17:06:21 +02:00
|
|
|
case KindAny:
|
2024-09-02 02:24:13 +02:00
|
|
|
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 }
|