chore(v1): merge in other changes
This commit is contained in:
@@ -1,54 +1,11 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
separator = "/"
|
||||
)
|
||||
|
||||
// ErrNew indicates that the wrapped error was encountered while creating
|
||||
// a new Command.
|
||||
var ErrNew = errors.New("failed to create Command from elems")
|
||||
|
||||
// ErrParse indicates that the wrapped error was encountered while
|
||||
// attempting to parse a string as a Command.
|
||||
var ErrParse = errors.New("failed to parse Command")
|
||||
|
||||
// ErrorJoin indicates that the wrapped error was encountered while
|
||||
// attempting to join a new segment to a Command.
|
||||
var ErrJoin = errors.New("failed to join segments to Command")
|
||||
|
||||
// ErrRequiresLeadingSlash is returned when a parsing a string that
|
||||
// doesn't start with a [leading slash character].
|
||||
//
|
||||
// [leading slash character]: https://github.com/ucan-wg/spec#segment-structure
|
||||
var ErrRequiresLeadingSlash = parseError("a command requires a leading slash character")
|
||||
|
||||
// ErrDisallowsTrailingSlash is returned when parsing a string that [ends
|
||||
// with a trailing slash character].
|
||||
//
|
||||
// [ends with a trailing slash character]: https://github.com/ucan-wg/spec#segment-structure
|
||||
var ErrDisallowsTrailingSlash = parseError("a command must not include a trailing slash")
|
||||
|
||||
// ErrUCANNamespaceReserved is returned to indicate that a Command's
|
||||
// first segment would contain the [reserved "ucan" namespace].
|
||||
//
|
||||
// [reserved "ucan" namespace]: https://github.com/ucan-wg/spec#ucan-namespace
|
||||
var ErrUCANNamespaceReserved = errors.New("the UCAN namespace is reserved")
|
||||
|
||||
// ErrRequiresLowercase is returned if a Command contains, or would contain,
|
||||
// [uppercase unicode characters].
|
||||
//
|
||||
// [uppercase unicode characters]: https://github.com/ucan-wg/spec#segment-structure
|
||||
var ErrRequiresLowercase = parseError("UCAN path segments must must not contain upper-case characters")
|
||||
|
||||
func parseError(msg string) error {
|
||||
return fmt.Errorf("%w: %s", ErrParse, msg)
|
||||
}
|
||||
const separator = "/"
|
||||
|
||||
var _ fmt.Stringer = (*Command)(nil)
|
||||
|
||||
@@ -66,18 +23,8 @@ type Command struct {
|
||||
// New creates a validated command from the provided list of segment
|
||||
// strings. An error is returned if an invalid Command would be
|
||||
// formed
|
||||
func New(segments ...string) (*Command, error) {
|
||||
return newCommand(ErrNew, segments...)
|
||||
}
|
||||
|
||||
func newCommand(err error, segments ...string) (*Command, error) {
|
||||
if len(segments) > 0 && segments[0] == "ucan" {
|
||||
return nil, fmt.Errorf("%w: %w", err, ErrUCANNamespaceReserved)
|
||||
}
|
||||
|
||||
cmd := Command{segments}
|
||||
|
||||
return &cmd, nil
|
||||
func New(segments ...string) *Command {
|
||||
return &Command{segments: segments}
|
||||
}
|
||||
|
||||
// Parse verifies that the provided string contains the required
|
||||
@@ -100,7 +47,16 @@ func Parse(s string) (*Command, error) {
|
||||
|
||||
// The leading slash will result in the first element from strings.Split
|
||||
// being an empty string which is removed as strings.Join will ignore it.
|
||||
return newCommand(ErrParse, strings.Split(s, "/")[1:]...)
|
||||
return &Command{strings.Split(s, "/")[1:]}, nil
|
||||
}
|
||||
|
||||
// MustParse is the same as Parse, but panic() if the parsing fail.
|
||||
func MustParse(s string) *Command {
|
||||
c, err := Parse(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// [Top] is the most powerful capability.
|
||||
@@ -111,22 +67,19 @@ func Parse(s string) (*Command, error) {
|
||||
//
|
||||
// [Top]: https://github.com/ucan-wg/spec#-aka-top
|
||||
func Top() *Command {
|
||||
cmd, _ := New()
|
||||
|
||||
return cmd
|
||||
return New()
|
||||
}
|
||||
|
||||
// IsValid returns true if the provided string is a valid UCAN command.
|
||||
func IsValid(s string) bool {
|
||||
_, err := Parse(s)
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Join appends segments to the end of this command using the required
|
||||
// segment separator.
|
||||
func (c *Command) Join(segments ...string) (*Command, error) {
|
||||
return newCommand(ErrJoin, append(c.segments, segments...)...)
|
||||
func (c *Command) Join(segments ...string) *Command {
|
||||
return &Command{append(c.segments, segments...)}
|
||||
}
|
||||
|
||||
// Segments returns the ordered segments that comprise the Command as a
|
||||
@@ -138,5 +91,5 @@ func (c *Command) Segments() []string {
|
||||
// String returns the composed representation the command. This is also
|
||||
// the required wire representation (before IPLD encoding occurs.)
|
||||
func (c *Command) String() string {
|
||||
return "/" + strings.Join([]string(c.segments), "/")
|
||||
return "/" + strings.Join(c.segments, "/")
|
||||
}
|
||||
|
||||
21
capability/command/command_errors.go
Normal file
21
capability/command/command_errors.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package command
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ErrRequiresLeadingSlash is returned when a parsing a string that
|
||||
// doesn't start with a [leading slash character].
|
||||
//
|
||||
// [leading slash character]: https://github.com/ucan-wg/spec#segment-structure
|
||||
var ErrRequiresLeadingSlash = fmt.Errorf("a command requires a leading slash character")
|
||||
|
||||
// ErrDisallowsTrailingSlash is returned when parsing a string that [ends
|
||||
// with a trailing slash character].
|
||||
//
|
||||
// [ends with a trailing slash character]: https://github.com/ucan-wg/spec#segment-structure
|
||||
var ErrDisallowsTrailingSlash = fmt.Errorf("a command must not include a trailing slash")
|
||||
|
||||
// ErrRequiresLowercase is returned if a Command contains, or would contain,
|
||||
// [uppercase unicode characters].
|
||||
//
|
||||
// [uppercase unicode characters]: https://github.com/ucan-wg/spec#segment-structure
|
||||
var ErrRequiresLowercase = fmt.Errorf("UCAN path segments must must not contain upper-case characters")
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/v1/capability/command"
|
||||
"github.com/ucan-wg/go-ucan/capability/command"
|
||||
)
|
||||
|
||||
func TestTop(t *testing.T) {
|
||||
@@ -73,7 +73,6 @@ func TestParseCommand(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cmd, err := command.Parse(testcase.inp)
|
||||
require.ErrorIs(t, err, command.ErrParse)
|
||||
require.ErrorIs(t, err, testcase.err)
|
||||
require.Nil(t, cmd)
|
||||
})
|
||||
@@ -134,20 +133,6 @@ func invalidTestcases(t *testing.T) []errorTestcase {
|
||||
},
|
||||
err: command.ErrDisallowsTrailingSlash,
|
||||
},
|
||||
{
|
||||
testcase: testcase{
|
||||
name: "only reserved ucan namespace",
|
||||
inp: "/ucan",
|
||||
},
|
||||
err: command.ErrUCANNamespaceReserved,
|
||||
},
|
||||
{
|
||||
testcase: testcase{
|
||||
name: "reserved ucan namespace prefix",
|
||||
inp: "/ucan/elem0/elem1/elem2",
|
||||
},
|
||||
err: command.ErrUCANNamespaceReserved,
|
||||
},
|
||||
{
|
||||
testcase: testcase{
|
||||
name: "uppercase character are present",
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/ipld/go-ipld-prime/must"
|
||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/v1/capability/policy/selector"
|
||||
"github.com/ucan-wg/go-ucan/capability/policy/selector"
|
||||
)
|
||||
|
||||
func FromIPLD(node datamodel.Node) (Policy, error) {
|
||||
@@ -40,14 +40,14 @@ func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
|
||||
}
|
||||
op := must.String(opNode)
|
||||
|
||||
arg2AsSelector := func() (selector.Selector, error) {
|
||||
arg2AsSelector := func(op string) (selector.Selector, error) {
|
||||
nd, _ := node.LookupByIndex(1)
|
||||
if nd.Kind() != datamodel.Kind_String {
|
||||
return nil, ErrNotAString(path + "1/")
|
||||
return nil, ErrNotAString(combinePath(path, op, 1))
|
||||
}
|
||||
sel, err := selector.Parse(must.String(nd))
|
||||
if err != nil {
|
||||
return nil, ErrInvalidSelector(path+"1/", err)
|
||||
return nil, ErrInvalidSelector(combinePath(path, op, 1), err)
|
||||
}
|
||||
return sel, nil
|
||||
}
|
||||
@@ -57,7 +57,7 @@ func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
|
||||
switch op {
|
||||
case KindNot:
|
||||
arg2, _ := node.LookupByIndex(1)
|
||||
statement, err := statementFromIPLD(path+"1/", arg2)
|
||||
statement, err := statementFromIPLD(combinePath(path, op, 1), arg2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
|
||||
|
||||
case KindAnd, KindOr:
|
||||
arg2, _ := node.LookupByIndex(1)
|
||||
statement, err := statementsFromIPLD(path+"1/", arg2)
|
||||
statement, err := statementsFromIPLD(combinePath(path, op, 1), arg2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -77,7 +77,7 @@ func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
|
||||
case 3:
|
||||
switch op {
|
||||
case KindEqual, KindLessThan, KindLessThanOrEqual, KindGreaterThan, KindGreaterThanOrEqual:
|
||||
sel, err := arg2AsSelector()
|
||||
sel, err := arg2AsSelector(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -85,28 +85,31 @@ func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
|
||||
return equality{kind: op, selector: sel, value: arg3}, nil
|
||||
|
||||
case KindLike:
|
||||
sel, err := arg2AsSelector()
|
||||
sel, err := arg2AsSelector(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pattern, _ := node.LookupByIndex(2)
|
||||
if pattern.Kind() != datamodel.Kind_String {
|
||||
return nil, ErrNotAString(path + "2/")
|
||||
return nil, ErrNotAString(combinePath(path, op, 2))
|
||||
}
|
||||
res, err := Like(sel, must.String(pattern))
|
||||
if err != nil {
|
||||
return nil, ErrInvalidPattern(path+"2/", err)
|
||||
return nil, ErrInvalidPattern(combinePath(path, op, 2), err)
|
||||
}
|
||||
return res, nil
|
||||
|
||||
case KindAll, KindAny:
|
||||
sel, err := arg2AsSelector()
|
||||
sel, err := arg2AsSelector(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statementsNodes, _ := node.LookupByIndex(2)
|
||||
statements, err := statementsFromIPLD(path+"1/", statementsNodes)
|
||||
return quantifier{kind: op, selector: sel, statements: statements}, nil
|
||||
statementsNode, _ := node.LookupByIndex(2)
|
||||
statement, err := statementFromIPLD(combinePath(path, op, 1), statementsNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return quantifier{kind: op, selector: sel, statement: statement}, nil
|
||||
|
||||
default:
|
||||
return nil, ErrUnrecognizedOperator(path, op)
|
||||
@@ -123,7 +126,7 @@ func statementsFromIPLD(path string, node datamodel.Node) ([]Statement, error) {
|
||||
return nil, ErrNotATuple(path)
|
||||
}
|
||||
if node.Length() == 0 {
|
||||
return nil, ErrEmptyList(path)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
res := make([]Statement, node.Length())
|
||||
@@ -243,7 +246,7 @@ func statementToIPLD(statement Statement) (datamodel.Node, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args, err := statementsToIPLD(statement.statements)
|
||||
args, err := statementToIPLD(statement.statement)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -260,3 +263,7 @@ func statementToIPLD(statement Statement) (datamodel.Node, error) {
|
||||
|
||||
return list.Build(), nil
|
||||
}
|
||||
|
||||
func combinePath(prev string, operator string, index int) string {
|
||||
return fmt.Sprintf("%s%d-%s/", prev, index, operator)
|
||||
}
|
||||
|
||||
@@ -35,10 +35,6 @@ func ErrNotATuple(path string) error {
|
||||
return errWithPath{path: path, msg: "not a tuple"}
|
||||
}
|
||||
|
||||
func ErrEmptyList(path string) error {
|
||||
return errWithPath{path: path, msg: "empty list"}
|
||||
}
|
||||
|
||||
func safeStr(str string) string {
|
||||
if len(str) > 10 {
|
||||
return str[:10]
|
||||
|
||||
@@ -11,16 +11,12 @@ import (
|
||||
func TestIpldRoundTrip(t *testing.T) {
|
||||
const illustrativeExample = `
|
||||
[
|
||||
["==", ".status", "draft"],
|
||||
["all", ".reviewer", [
|
||||
["like", ".email", "*@example.com"]]
|
||||
],
|
||||
["any", ".tags", [
|
||||
["or", [
|
||||
["==", ".", "news"],
|
||||
["==", ".", "press"]]
|
||||
]]
|
||||
]
|
||||
["==", ".status", "draft"],
|
||||
["all", ".reviewer", ["like", ".email", "*@example.com"]],
|
||||
["any", ".tags",
|
||||
["or", [
|
||||
["==", ".", "news"],
|
||||
["==", ".", "press"]]]
|
||||
]`
|
||||
|
||||
for _, tc := range []struct {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/ipld/go-ipld-prime/datamodel"
|
||||
"github.com/ipld/go-ipld-prime/must"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/v1/capability/policy/selector"
|
||||
"github.com/ucan-wg/go-ucan/capability/policy/selector"
|
||||
)
|
||||
|
||||
// Match determines if the IPLD node matches the policy document.
|
||||
@@ -110,7 +110,7 @@ func matchStatement(statement Statement, node ipld.Node) bool {
|
||||
return false
|
||||
}
|
||||
for _, n := range many {
|
||||
ok := Match(s.statements, n)
|
||||
ok := matchStatement(s.statement, n)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
@@ -119,12 +119,13 @@ func matchStatement(statement Statement, node ipld.Node) bool {
|
||||
}
|
||||
case KindAny:
|
||||
if s, ok := statement.(quantifier); ok {
|
||||
// FIXME: line below return a single node, not many
|
||||
_, many, err := selector.Select(s.selector, node)
|
||||
if err != nil || many == nil {
|
||||
return false
|
||||
}
|
||||
for _, n := range many {
|
||||
ok := Match(s.statements, n)
|
||||
ok := matchStatement(s.statement, n)
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -6,12 +6,13 @@ import (
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
||||
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/v1/capability/policy/literal"
|
||||
"github.com/ucan-wg/go-ucan/v1/capability/policy/selector"
|
||||
"github.com/ucan-wg/go-ucan/capability/policy/literal"
|
||||
"github.com/ucan-wg/go-ucan/capability/policy/selector"
|
||||
)
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
@@ -400,8 +401,7 @@ func TestMatch(t *testing.T) {
|
||||
pol := Policy{
|
||||
Any(
|
||||
selector.MustParse(".[]"),
|
||||
GreaterThan(selector.MustParse(".value"), literal.Int(10)),
|
||||
LessThan(selector.MustParse(".value"), literal.Int(50)),
|
||||
GreaterThan(selector.MustParse(".value"), literal.Int(60)),
|
||||
),
|
||||
}
|
||||
ok := Match(pol, nd)
|
||||
@@ -418,3 +418,75 @@ func TestMatch(t *testing.T) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestPolicyExamples(t *testing.T) {
|
||||
makeNode := func(data string) ipld.Node {
|
||||
nd, err := ipld.Decode([]byte(data), dagjson.Decode)
|
||||
require.NoError(t, err)
|
||||
return nd
|
||||
}
|
||||
|
||||
evaluate := func(statement string, data ipld.Node) bool {
|
||||
// we need to wrap statement with [] to make them a policy
|
||||
policy := fmt.Sprintf("[%s]", statement)
|
||||
|
||||
pol, err := FromDagJson(policy)
|
||||
require.NoError(t, err)
|
||||
return Match(pol, data)
|
||||
}
|
||||
|
||||
t.Run("And", func(t *testing.T) {
|
||||
data := makeNode(`{ "name": "Katie", "age": 35, "nationalities": ["Canadian", "South African"] }`)
|
||||
|
||||
require.True(t, evaluate(`["and", []]`, data))
|
||||
require.True(t, evaluate(`
|
||||
["and", [
|
||||
["==", ".name", "Katie"],
|
||||
[">=", ".age", 21]
|
||||
]]`, data))
|
||||
require.False(t, evaluate(`
|
||||
["and", [
|
||||
["==", ".name", "Katie"],
|
||||
[">=", ".age", 21],
|
||||
["==", ".nationalities", ["American"]]
|
||||
]]`, data))
|
||||
})
|
||||
|
||||
t.Run("Or", func(t *testing.T) {
|
||||
data := makeNode(`{ "name": "Katie", "age": 35, "nationalities": ["Canadian", "South African"] }`)
|
||||
|
||||
require.True(t, evaluate(`["or", []]`, data))
|
||||
require.True(t, evaluate(`
|
||||
["or", [
|
||||
["==", ".name", "Katie"],
|
||||
[">", ".age", 45]
|
||||
]]
|
||||
`, data))
|
||||
|
||||
})
|
||||
|
||||
t.Run("Not", func(t *testing.T) {
|
||||
data := makeNode(`{ "name": "Katie", "nationalities": ["Canadian", "South African"] }`)
|
||||
|
||||
require.True(t, evaluate(`
|
||||
["not",
|
||||
["and", [
|
||||
["==", ".name", "Katie"],
|
||||
["==", ".nationalities", ["American"]]
|
||||
]]
|
||||
]
|
||||
`, data))
|
||||
})
|
||||
|
||||
t.Run("All", func(t *testing.T) {
|
||||
data := makeNode(`{"a": [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}]}`)
|
||||
|
||||
require.False(t, evaluate(`["all", ".a", [">", ".b", 0]]`, data))
|
||||
})
|
||||
|
||||
t.Run("Any", func(t *testing.T) {
|
||||
data := makeNode(`{"a": [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}]}`)
|
||||
|
||||
require.True(t, evaluate(`["any", ".a", ["==", ".b", 2]]`, data))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/v1/capability/policy/selector"
|
||||
"github.com/ucan-wg/go-ucan/capability/policy/selector"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -40,23 +40,23 @@ func (e equality) Kind() string {
|
||||
}
|
||||
|
||||
func Equal(selector selector.Selector, value ipld.Node) Statement {
|
||||
return equality{KindEqual, selector, value}
|
||||
return equality{kind: KindEqual, selector: selector, value: value}
|
||||
}
|
||||
|
||||
func GreaterThan(selector selector.Selector, value ipld.Node) Statement {
|
||||
return equality{KindGreaterThan, selector, value}
|
||||
return equality{kind: KindGreaterThan, selector: selector, value: value}
|
||||
}
|
||||
|
||||
func GreaterThanOrEqual(selector selector.Selector, value ipld.Node) Statement {
|
||||
return equality{KindGreaterThanOrEqual, selector, value}
|
||||
return equality{kind: KindGreaterThanOrEqual, selector: selector, value: value}
|
||||
}
|
||||
|
||||
func LessThan(selector selector.Selector, value ipld.Node) Statement {
|
||||
return equality{KindLessThan, selector, value}
|
||||
return equality{kind: KindLessThan, selector: selector, value: value}
|
||||
}
|
||||
|
||||
func LessThanOrEqual(selector selector.Selector, value ipld.Node) Statement {
|
||||
return equality{KindLessThanOrEqual, selector, value}
|
||||
return equality{kind: KindLessThanOrEqual, selector: selector, value: value}
|
||||
}
|
||||
|
||||
type negation struct {
|
||||
@@ -68,7 +68,7 @@ func (n negation) Kind() string {
|
||||
}
|
||||
|
||||
func Not(stmt Statement) Statement {
|
||||
return negation{stmt}
|
||||
return negation{statement: stmt}
|
||||
}
|
||||
|
||||
type connective struct {
|
||||
@@ -81,11 +81,11 @@ func (c connective) Kind() string {
|
||||
}
|
||||
|
||||
func And(stmts ...Statement) Statement {
|
||||
return connective{KindAnd, stmts}
|
||||
return connective{kind: KindAnd, statements: stmts}
|
||||
}
|
||||
|
||||
func Or(stmts ...Statement) Statement {
|
||||
return connective{KindOr, stmts}
|
||||
return connective{kind: KindOr, statements: stmts}
|
||||
}
|
||||
|
||||
type wildcard struct {
|
||||
@@ -103,23 +103,23 @@ func Like(selector selector.Selector, pattern string) (Statement, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wildcard{selector, pattern, g}, nil
|
||||
return wildcard{selector: selector, pattern: pattern, glob: g}, nil
|
||||
}
|
||||
|
||||
type quantifier struct {
|
||||
kind string
|
||||
selector selector.Selector
|
||||
statements []Statement
|
||||
kind string
|
||||
selector selector.Selector
|
||||
statement Statement
|
||||
}
|
||||
|
||||
func (n quantifier) Kind() string {
|
||||
return n.kind
|
||||
}
|
||||
|
||||
func All(selector selector.Selector, policy ...Statement) Statement {
|
||||
return quantifier{KindAll, selector, policy}
|
||||
func All(selector selector.Selector, statement Statement) Statement {
|
||||
return quantifier{kind: KindAll, selector: selector, statement: statement}
|
||||
}
|
||||
|
||||
func Any(selector selector.Selector, policy ...Statement) Statement {
|
||||
return quantifier{KindAny, selector, policy}
|
||||
func Any(selector selector.Selector, statement Statement) Statement {
|
||||
return quantifier{kind: KindAny, selector: selector, statement: statement}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/v1/capability/policy/selector"
|
||||
"github.com/ucan-wg/go-ucan/capability/policy/selector"
|
||||
)
|
||||
|
||||
// TestSupported Forms runs tests against the Selector according to the
|
||||
|
||||
87
delegation/view.go
Normal file
87
delegation/view.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package delegation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ipld/go-ipld-prime/datamodel"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/capability/command"
|
||||
"github.com/ucan-wg/go-ucan/capability/policy"
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
)
|
||||
|
||||
type View struct {
|
||||
// Issuer DID (sender)
|
||||
Issuer did.DID
|
||||
// Audience DID (receiver)
|
||||
Audience did.DID
|
||||
// Principal that the chain is about (the Subject)
|
||||
Subject did.DID
|
||||
// The Command to eventually invoke
|
||||
Command *command.Command
|
||||
// The delegation policy
|
||||
Policy policy.Policy
|
||||
// A unique, random nonce
|
||||
Nonce []byte
|
||||
// Arbitrary Metadata
|
||||
Meta map[string]datamodel.Node
|
||||
// "Not before" UTC Unix Timestamp in seconds (valid from), 53-bits integer
|
||||
NotBefore time.Time
|
||||
// The timestamp at which the Invocation becomes invalid
|
||||
Expiration time.Time
|
||||
}
|
||||
|
||||
// ViewFromModel build a decoded view of the raw IPLD data.
|
||||
// This function also serves as validation.
|
||||
func ViewFromModel(m PayloadModel) (*View, error) {
|
||||
var view View
|
||||
var err error
|
||||
|
||||
view.Issuer, err = did.Parse(m.Iss)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse iss: %w", err)
|
||||
}
|
||||
|
||||
view.Audience, err = did.Parse(m.Aud)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse audience: %w", err)
|
||||
}
|
||||
|
||||
if m.Sub != nil {
|
||||
view.Subject, err = did.Parse(*m.Sub)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse subject: %w", err)
|
||||
}
|
||||
} else {
|
||||
view.Subject = did.Undef
|
||||
}
|
||||
|
||||
view.Command, err = command.Parse(m.Cmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse command: %w", err)
|
||||
}
|
||||
|
||||
view.Policy, err = policy.FromIPLD(m.Pol)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse policy: %w", err)
|
||||
}
|
||||
|
||||
if len(m.Nonce) == 0 {
|
||||
return nil, fmt.Errorf("nonce is required")
|
||||
}
|
||||
view.Nonce = m.Nonce
|
||||
|
||||
// TODO: copy?
|
||||
view.Meta = m.Meta.Values
|
||||
|
||||
if m.Nbf != nil {
|
||||
view.NotBefore = time.Unix(*m.Nbf, 0)
|
||||
}
|
||||
|
||||
if m.Exp != nil {
|
||||
view.Expiration = time.Unix(*m.Exp, 0)
|
||||
}
|
||||
|
||||
return &view, nil
|
||||
}
|
||||
2
go.mod
2
go.mod
@@ -1,4 +1,4 @@
|
||||
module github.com/ucan-wg/go-ucan/v1
|
||||
module github.com/ucan-wg/go-ucan
|
||||
|
||||
go 1.21
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ import (
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/ucan-wg/go-ucan/v1/internal/varsig"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/internal/varsig"
|
||||
)
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user