Files
ucan/pkg/policy/ipld.go

275 lines
6.6 KiB
Go
Raw Normal View History

2024-09-01 20:27:54 +02:00
package policy
import (
"fmt"
2024-09-02 02:25:34 +02:00
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/codec/dagjson"
2024-09-01 20:27:54 +02:00
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/must"
"github.com/ipld/go-ipld-prime/node/basicnode"
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
2024-09-24 11:40:28 -04:00
"github.com/ucan-wg/go-ucan/pkg/policy/selector"
2024-09-01 20:27:54 +02:00
)
2024-09-02 02:25:34 +02:00
func FromIPLD(node datamodel.Node) (Policy, error) {
if err := limits.ValidateIntegerBoundsIPLD(node); err != nil {
return nil, fmt.Errorf("policy contains integer values outside safe bounds: %w", err)
}
2024-09-01 20:27:54 +02:00
return statementsFromIPLD("/", node)
}
2024-09-02 02:25:34 +02:00
func FromDagJson(json string) (Policy, error) {
nodes, err := ipld.Decode([]byte(json), dagjson.Decode)
if err != nil {
return nil, err
}
return FromIPLD(nodes)
}
2024-09-01 20:27:54 +02:00
func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
// sanity checks
if node.Kind() != datamodel.Kind_List {
return nil, ErrNotATuple(path)
}
if node.Length() != 2 && node.Length() != 3 {
return nil, ErrUnrecognizedShape(path)
}
// extract operator
opNode, _ := node.LookupByIndex(0)
if opNode.Kind() != datamodel.Kind_String {
return nil, ErrNotAString(path)
}
op := must.String(opNode)
2024-09-04 19:08:21 +02:00
arg2AsSelector := func(op string) (selector.Selector, error) {
2024-09-01 20:27:54 +02:00
nd, _ := node.LookupByIndex(1)
if nd.Kind() != datamodel.Kind_String {
2024-09-04 19:08:21 +02:00
return nil, ErrNotAString(combinePath(path, op, 1))
2024-09-01 20:27:54 +02:00
}
sel, err := selector.Parse(must.String(nd))
if err != nil {
2024-09-04 19:08:21 +02:00
return nil, ErrInvalidSelector(combinePath(path, op, 1), err)
2024-09-01 20:27:54 +02:00
}
return sel, nil
}
switch node.Length() {
case 2:
switch op {
case KindNot:
arg2, _ := node.LookupByIndex(1)
2024-09-04 19:08:21 +02:00
statement, err := statementFromIPLD(combinePath(path, op, 1), arg2)
2024-09-01 20:27:54 +02:00
if err != nil {
return nil, err
}
2024-10-14 20:09:21 +02:00
return negation{statement: statement}, nil
2024-09-01 20:27:54 +02:00
case KindAnd, KindOr:
arg2, _ := node.LookupByIndex(1)
2024-09-04 19:08:21 +02:00
statement, err := statementsFromIPLD(combinePath(path, op, 1), arg2)
2024-09-01 20:27:54 +02:00
if err != nil {
return nil, err
}
return connective{kind: op, statements: statement}, nil
default:
return nil, ErrUnrecognizedOperator(path, op)
}
case 3:
switch op {
case KindEqual, KindNotEqual, KindLessThan, KindLessThanOrEqual, KindGreaterThan, KindGreaterThanOrEqual:
2024-09-04 19:08:21 +02:00
sel, err := arg2AsSelector(op)
2024-09-01 20:27:54 +02:00
if err != nil {
return nil, err
}
arg3, _ := node.LookupByIndex(2)
return equality{kind: op, selector: sel, value: arg3}, nil
case KindLike:
2024-09-04 19:08:21 +02:00
sel, err := arg2AsSelector(op)
2024-09-01 20:27:54 +02:00
if err != nil {
return nil, err
}
pattern, _ := node.LookupByIndex(2)
if pattern.Kind() != datamodel.Kind_String {
2024-09-04 19:08:21 +02:00
return nil, ErrNotAString(combinePath(path, op, 2))
2024-09-01 20:27:54 +02:00
}
2024-10-14 20:09:21 +02:00
g, err := parseGlob(must.String(pattern))
2024-09-01 20:27:54 +02:00
if err != nil {
2024-09-04 19:08:21 +02:00
return nil, ErrInvalidPattern(combinePath(path, op, 2), err)
2024-09-01 20:27:54 +02:00
}
2024-10-14 20:09:21 +02:00
return wildcard{selector: sel, pattern: g}, nil
2024-09-01 20:27:54 +02:00
case KindAll, KindAny:
2024-09-04 19:08:21 +02:00
sel, err := arg2AsSelector(op)
2024-09-01 20:27:54 +02:00
if err != nil {
return nil, err
}
statementsNode, _ := node.LookupByIndex(2)
2024-09-04 19:08:21 +02:00
statement, err := statementFromIPLD(combinePath(path, op, 1), statementsNode)
if err != nil {
return nil, err
}
return quantifier{kind: op, selector: sel, statement: statement}, nil
2024-09-01 20:27:54 +02:00
default:
return nil, ErrUnrecognizedOperator(path, op)
}
default:
return nil, ErrUnrecognizedShape(path)
}
}
func statementsFromIPLD(path string, node datamodel.Node) ([]Statement, error) {
// sanity checks
if node.Kind() != datamodel.Kind_List {
return nil, ErrNotATuple(path)
}
if node.Length() == 0 {
2024-09-04 19:08:21 +02:00
return nil, nil
2024-09-01 20:27:54 +02:00
}
res := make([]Statement, node.Length())
for i := int64(0); i < node.Length(); i++ {
nd, _ := node.LookupByIndex(i)
statement, err := statementFromIPLD(fmt.Sprintf("%s%d/", path, i), nd)
if err != nil {
return nil, err
}
res[i] = statement
}
return res, nil
}
func (p Policy) ToIPLD() (datamodel.Node, error) {
return statementsToIPLD(p)
}
func statementsToIPLD(statements []Statement) (datamodel.Node, error) {
list := basicnode.Prototype.List.NewBuilder()
// can't error, we have the right builder.
listBuilder, _ := list.BeginList(int64(len(statements)))
for _, argStatement := range statements {
node, err := statementToIPLD(argStatement)
if err != nil {
return nil, err
}
err = listBuilder.AssembleValue().AssignNode(node)
if err != nil {
return nil, err
}
}
err := listBuilder.Finish()
if err != nil {
return nil, err
}
return list.Build(), nil
}
func statementToIPLD(statement Statement) (datamodel.Node, error) {
list := basicnode.Prototype.List.NewBuilder()
length := int64(3)
switch statement.(type) {
case negation, connective:
length = 2
}
// can't error, we have the right builder.
listBuilder, _ := list.BeginList(length)
switch statement := statement.(type) {
case equality:
err := listBuilder.AssembleValue().AssignString(statement.kind)
if err != nil {
return nil, err
}
err = listBuilder.AssembleValue().AssignString(statement.selector.String())
if err != nil {
return nil, err
}
err = listBuilder.AssembleValue().AssignNode(statement.value)
if err != nil {
return nil, err
}
case negation:
err := listBuilder.AssembleValue().AssignString(statement.Kind())
if err != nil {
return nil, err
}
node, err := statementToIPLD(statement.statement)
if err != nil {
return nil, err
}
err = listBuilder.AssembleValue().AssignNode(node)
if err != nil {
return nil, err
}
case connective:
err := listBuilder.AssembleValue().AssignString(statement.kind)
if err != nil {
return nil, err
}
args, err := statementsToIPLD(statement.statements)
if err != nil {
return nil, err
}
err = listBuilder.AssembleValue().AssignNode(args)
if err != nil {
return nil, err
}
case wildcard:
err := listBuilder.AssembleValue().AssignString(statement.Kind())
if err != nil {
return nil, err
}
err = listBuilder.AssembleValue().AssignString(statement.selector.String())
if err != nil {
return nil, err
}
2024-09-17 14:15:36 +02:00
err = listBuilder.AssembleValue().AssignString(string(statement.pattern))
2024-09-01 20:27:54 +02:00
if err != nil {
return nil, err
}
case quantifier:
err := listBuilder.AssembleValue().AssignString(statement.kind)
if err != nil {
return nil, err
}
err = listBuilder.AssembleValue().AssignString(statement.selector.String())
if err != nil {
return nil, err
}
args, err := statementToIPLD(statement.statement)
2024-09-01 20:27:54 +02:00
if err != nil {
return nil, err
}
err = listBuilder.AssembleValue().AssignNode(args)
if err != nil {
return nil, err
}
}
err := listBuilder.Finish()
if err != nil {
return nil, err
}
return list.Build(), nil
}
2024-09-04 19:08:21 +02:00
func combinePath(prev string, operator string, index int) string {
return fmt.Sprintf("%s%d-%s/", prev, index, operator)
}