Compare commits
4 Commits
feat/deleg
...
v1-policy-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfd5f00618 | ||
|
|
d66b8e40ec | ||
|
|
18820f5e9d | ||
|
|
ddaa67ed7d |
@@ -116,11 +116,3 @@ func Parse(str string) (DID, error) {
|
||||
buf = append(buf, suffix...)
|
||||
return DID{str: string(buf), code: DIDCore}, nil
|
||||
}
|
||||
|
||||
func MustParse(str string) DID {
|
||||
did, err := Parse(str)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return did
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package did
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseDIDKey(t *testing.T) {
|
||||
@@ -17,18 +15,6 @@ func TestParseDIDKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustParseDIDKey(t *testing.T) {
|
||||
str := "did:key:z6Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z"
|
||||
require.NotPanics(t, func() {
|
||||
d := MustParse(str)
|
||||
require.Equal(t, str, d.String())
|
||||
})
|
||||
str = "did:key:z7Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z"
|
||||
require.Panics(t, func() {
|
||||
MustParse(str)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDecodeDIDKey(t *testing.T) {
|
||||
str := "did:key:z6Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z"
|
||||
d0, err := Parse(str)
|
||||
|
||||
@@ -11,14 +11,32 @@ import (
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/selector"
|
||||
)
|
||||
|
||||
func (p Policy) Filter(sel selector.Selector) Policy {
|
||||
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 {
|
||||
if stmt.Selector().Matches(sel, matcher) {
|
||||
filtered = append(filtered, stmt)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
// Match determines if the IPLD node matches the policy document.
|
||||
func Match(policy Policy, node ipld.Node) bool {
|
||||
for _, stmt := range policy {
|
||||
func (p Policy) Match(node datamodel.Node) bool {
|
||||
for _, stmt := range p {
|
||||
ok := matchStatement(stmt, node)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
@@ -9,6 +11,7 @@ import (
|
||||
"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/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||
@@ -24,15 +27,15 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{Equal(selector.MustParse("."), literal.String("test"))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{Equal(selector.MustParse("."), literal.String("test2"))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
|
||||
pol = Policy{Equal(selector.MustParse("."), literal.Int(138))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
@@ -43,15 +46,15 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{Equal(selector.MustParse("."), literal.Int(138))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{Equal(selector.MustParse("."), literal.Int(1138))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
|
||||
pol = Policy{Equal(selector.MustParse("."), literal.String("138"))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
@@ -62,15 +65,15 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{Equal(selector.MustParse("."), literal.Float(1.138))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{Equal(selector.MustParse("."), literal.Float(11.38))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
|
||||
pol = Policy{Equal(selector.MustParse("."), literal.String("138"))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
@@ -84,15 +87,15 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{Equal(selector.MustParse("."), literal.Link(l0))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{Equal(selector.MustParse("."), literal.Link(l1))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
|
||||
pol = Policy{Equal(selector.MustParse("."), literal.String("bafybeif4owy5gno5lwnixqm52rwqfodklf76hsetxdhffuxnplvijskzqq"))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
@@ -106,19 +109,19 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{Equal(selector.MustParse(".foo"), literal.String("bar"))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{Equal(selector.MustParse(".[\"foo\"]"), literal.String("bar"))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{Equal(selector.MustParse(".foo"), literal.String("baz"))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
|
||||
pol = Policy{Equal(selector.MustParse(".foobar"), literal.String("bar"))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
@@ -131,11 +134,11 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{Equal(selector.MustParse(".[0]"), literal.String("foo"))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{Equal(selector.MustParse(".[1]"), literal.String("foo"))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
})
|
||||
})
|
||||
@@ -148,7 +151,7 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{GreaterThan(selector.MustParse("."), literal.Int(1))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
})
|
||||
|
||||
@@ -159,11 +162,11 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{GreaterThanOrEqual(selector.MustParse("."), literal.Int(1))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{GreaterThanOrEqual(selector.MustParse("."), literal.Int(138))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
})
|
||||
|
||||
@@ -174,7 +177,7 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{GreaterThan(selector.MustParse("."), literal.Float(1))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
})
|
||||
|
||||
@@ -185,11 +188,11 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{GreaterThanOrEqual(selector.MustParse("."), literal.Float(1))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{GreaterThanOrEqual(selector.MustParse("."), literal.Float(1.38))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
})
|
||||
|
||||
@@ -200,7 +203,7 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{LessThan(selector.MustParse("."), literal.Int(1138))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
})
|
||||
|
||||
@@ -211,11 +214,11 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{LessThanOrEqual(selector.MustParse("."), literal.Int(1138))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{LessThanOrEqual(selector.MustParse("."), literal.Int(138))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
})
|
||||
})
|
||||
@@ -227,11 +230,11 @@ func TestMatch(t *testing.T) {
|
||||
nd := nb.Build()
|
||||
|
||||
pol := Policy{Not(Equal(selector.MustParse("."), literal.Bool(true)))}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{Not(Equal(selector.MustParse("."), literal.Bool(false)))}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
@@ -247,7 +250,7 @@ func TestMatch(t *testing.T) {
|
||||
LessThan(selector.MustParse("."), literal.Int(1138)),
|
||||
),
|
||||
}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{
|
||||
@@ -256,11 +259,11 @@ func TestMatch(t *testing.T) {
|
||||
Equal(selector.MustParse("."), literal.Int(1138)),
|
||||
),
|
||||
}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
|
||||
pol = Policy{And()}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
})
|
||||
|
||||
@@ -276,7 +279,7 @@ func TestMatch(t *testing.T) {
|
||||
LessThan(selector.MustParse("."), literal.Int(1138)),
|
||||
),
|
||||
}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{
|
||||
@@ -285,11 +288,11 @@ func TestMatch(t *testing.T) {
|
||||
Equal(selector.MustParse("."), literal.Int(1138)),
|
||||
),
|
||||
}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
|
||||
pol = Policy{Or()}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
})
|
||||
|
||||
@@ -313,7 +316,7 @@ func TestMatch(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
pol := Policy{statement}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
})
|
||||
}(s)
|
||||
@@ -337,7 +340,7 @@ func TestMatch(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
pol := Policy{statement}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
})
|
||||
}(s)
|
||||
@@ -373,7 +376,7 @@ func TestMatch(t *testing.T) {
|
||||
GreaterThan(selector.MustParse(".value"), literal.Int(2)),
|
||||
),
|
||||
}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{
|
||||
@@ -382,7 +385,7 @@ func TestMatch(t *testing.T) {
|
||||
GreaterThan(selector.MustParse(".value"), literal.Int(20)),
|
||||
),
|
||||
}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
@@ -404,7 +407,7 @@ func TestMatch(t *testing.T) {
|
||||
GreaterThan(selector.MustParse(".value"), literal.Int(60)),
|
||||
),
|
||||
}
|
||||
ok := Match(pol, nd)
|
||||
ok := pol.Match(nd)
|
||||
require.True(t, ok)
|
||||
|
||||
pol = Policy{
|
||||
@@ -413,7 +416,7 @@ func TestMatch(t *testing.T) {
|
||||
GreaterThan(selector.MustParse(".value"), literal.Int(100)),
|
||||
),
|
||||
}
|
||||
ok = Match(pol, nd)
|
||||
ok = pol.Match(nd)
|
||||
require.False(t, ok)
|
||||
})
|
||||
})
|
||||
@@ -432,7 +435,7 @@ func TestPolicyExamples(t *testing.T) {
|
||||
|
||||
pol, err := FromDagJson(policy)
|
||||
require.NoError(t, err)
|
||||
return Match(pol, data)
|
||||
return pol.Match(data)
|
||||
}
|
||||
|
||||
t.Run("And", func(t *testing.T) {
|
||||
@@ -536,6 +539,104 @@ func FuzzMatch(f *testing.F) {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
Match(policy, dataNode)
|
||||
policy.Match(dataNode)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPolicyFilter(t *testing.T) {
|
||||
sel1 := selector.Selector{selector.NewFieldSegment("http")}
|
||||
sel2 := selector.Selector{selector.NewFieldSegment("jsonrpc")}
|
||||
|
||||
stmt1 := Equal(sel1, basicnode.NewString("value1"))
|
||||
stmt2 := Equal(sel2, basicnode.NewString("value2"))
|
||||
|
||||
p := Policy{stmt1, stmt2}
|
||||
|
||||
filtered := p.Filter(sel1)
|
||||
assert.Len(t, filtered, 1)
|
||||
assert.Equal(t, stmt1, filtered[0])
|
||||
|
||||
filtered = p.Filter(sel2)
|
||||
assert.Len(t, filtered, 1)
|
||||
assert.Equal(t, stmt2, filtered[0])
|
||||
|
||||
sel3 := selector.Selector{selector.NewFieldSegment("nonexistent")}
|
||||
filtered = p.Filter(sel3)
|
||||
assert.Len(t, filtered, 0)
|
||||
|
||||
stmt1 = Equal(
|
||||
selector.Selector{selector.NewFieldSegment(".http.host")},
|
||||
basicnode.NewString("mainnet.infura.io"),
|
||||
)
|
||||
stmt2, err := Like(
|
||||
selector.Selector{selector.NewFieldSegment(".jsonrpc.method")},
|
||||
"eth_*",
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
p = Policy{stmt1, stmt2}
|
||||
filtered = p.FilterWithMatcher(selector.Selector{selector.NewFieldSegment(".http")}, selector.SegmentStartsWith)
|
||||
assert.Len(t, filtered, 1)
|
||||
assert.Equal(t, stmt1, filtered[0])
|
||||
}
|
||||
|
||||
func FuzzPolicyFilter(f *testing.F) {
|
||||
f.Add([]byte(`{"selector": [{"field": "http"}], "value": "value1"}`))
|
||||
f.Add([]byte(`{"selector": [{"field": "jsonrpc"}], "value": "value2"}`))
|
||||
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
var input struct {
|
||||
Selector []struct {
|
||||
Field string `json:"field"` // because selector.segment is not public
|
||||
} `json:"selector"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &input); err != nil {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
var sel selector.Selector
|
||||
for _, seg := range input.Selector {
|
||||
sel = append(sel, selector.NewFieldSegment(seg.Field))
|
||||
}
|
||||
stmt := Equal(sel, basicnode.NewString(input.Value))
|
||||
|
||||
// create a policy and filter it based on the fuzzy input selector
|
||||
p := Policy{stmt}
|
||||
filtered := p.Filter(sel)
|
||||
|
||||
// verify that the filtered policy contains the statement
|
||||
if len(filtered) != 1 || !reflect.DeepEqual(filtered[0], stmt) {
|
||||
t.Errorf("filtered policy does not contain the expected statement")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkPolicyFilter(b *testing.B) {
|
||||
sel1 := selector.Selector{selector.NewFieldSegment("http")}
|
||||
sel2 := selector.Selector{selector.NewFieldSegment("jsonrpc")}
|
||||
|
||||
stmt1 := Equal(sel1, basicnode.NewString("value1"))
|
||||
stmt2 := Equal(sel2, basicnode.NewString("value2"))
|
||||
|
||||
p := Policy{stmt1, stmt2}
|
||||
|
||||
b.Run("Filter by sel1", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p.Filter(sel1)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("Filter by sel2", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p.Filter(sel2)
|
||||
}
|
||||
})
|
||||
|
||||
sel3 := selector.Selector{selector.NewFieldSegment("nonexistent")}
|
||||
b.Run("Filter by sel3", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p.Filter(sel3)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ type Policy []Statement
|
||||
|
||||
type Statement interface {
|
||||
Kind() string
|
||||
Selector() selector.Selector
|
||||
}
|
||||
|
||||
type equality struct {
|
||||
@@ -38,6 +39,10 @@ func (e equality) Kind() string {
|
||||
return e.kind
|
||||
}
|
||||
|
||||
func (e equality) Selector() selector.Selector {
|
||||
return e.selector
|
||||
}
|
||||
|
||||
func Equal(selector selector.Selector, value ipld.Node) Statement {
|
||||
return equality{kind: KindEqual, selector: selector, value: value}
|
||||
}
|
||||
@@ -66,6 +71,10 @@ func (n negation) Kind() string {
|
||||
return KindNot
|
||||
}
|
||||
|
||||
func (n negation) Selector() selector.Selector {
|
||||
return n.statement.Selector()
|
||||
}
|
||||
|
||||
func Not(stmt Statement) Statement {
|
||||
return negation{statement: stmt}
|
||||
}
|
||||
@@ -79,6 +88,15 @@ func (c connective) Kind() string {
|
||||
return c.kind
|
||||
}
|
||||
|
||||
func (c connective) Selector() selector.Selector {
|
||||
// assuming the first statement's selector is representative
|
||||
if len(c.statements) > 0 {
|
||||
return c.statements[0].Selector()
|
||||
}
|
||||
|
||||
return selector.Selector{}
|
||||
}
|
||||
|
||||
func And(stmts ...Statement) Statement {
|
||||
return connective{kind: KindAnd, statements: stmts}
|
||||
}
|
||||
@@ -96,6 +114,10 @@ func (n wildcard) Kind() string {
|
||||
return KindLike
|
||||
}
|
||||
|
||||
func (n wildcard) Selector() selector.Selector {
|
||||
return n.selector
|
||||
}
|
||||
|
||||
func Like(selector selector.Selector, pattern string) (Statement, error) {
|
||||
g, err := parseGlob(pattern)
|
||||
if err != nil {
|
||||
@@ -115,6 +137,10 @@ func (n quantifier) Kind() string {
|
||||
return n.kind
|
||||
}
|
||||
|
||||
func (n quantifier) Selector() selector.Selector {
|
||||
return n.selector
|
||||
}
|
||||
|
||||
func All(selector selector.Selector, statement Statement) Statement {
|
||||
return quantifier{kind: KindAll, selector: selector, statement: statement}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,21 @@ func (s Selector) String() string {
|
||||
return res.String()
|
||||
}
|
||||
|
||||
// Matches checks if the selector matches another selector.
|
||||
func (s Selector) Matches(other Selector, matcher SegmentMatcher) bool {
|
||||
if len(s) != len(other) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, seg := range s {
|
||||
if !matcher(seg, other[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
var Identity = segment{".", true, false, false, nil, "", 0}
|
||||
|
||||
var (
|
||||
@@ -41,6 +56,48 @@ type segment struct {
|
||||
index int
|
||||
}
|
||||
|
||||
// NewFieldSegment creates a new segment for a field.
|
||||
func NewFieldSegment(field string) segment {
|
||||
return segment{
|
||||
str: fmt.Sprintf(".%s", field),
|
||||
field: field,
|
||||
}
|
||||
}
|
||||
|
||||
// NewIndexSegment creates a new segment for an index.
|
||||
func NewIndexSegment(index int) segment {
|
||||
return segment{
|
||||
str: fmt.Sprintf("[%d]", index),
|
||||
index: index,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSliceSegment creates a new segment for a slice.
|
||||
func NewSliceSegment(slice []int) segment {
|
||||
return segment{
|
||||
str: fmt.Sprintf("[%d:%d]", slice[0], slice[1]),
|
||||
slice: slice,
|
||||
}
|
||||
}
|
||||
|
||||
// NewIteratorSegment creates a new segment for an iterator.
|
||||
func NewIteratorSegment() segment {
|
||||
return segment{
|
||||
str: "*",
|
||||
iterator: true,
|
||||
}
|
||||
}
|
||||
|
||||
type SegmentMatcher func(s1, s2 segment) bool
|
||||
|
||||
var SegmentEquals SegmentMatcher = func(s1, s2 segment) bool {
|
||||
return s1.str == s2.str
|
||||
}
|
||||
|
||||
var SegmentStartsWith SegmentMatcher = func(s1, s2 segment) bool {
|
||||
return strings.HasPrefix(s1.str, s2.str)
|
||||
}
|
||||
|
||||
// String returns the segment's string representation.
|
||||
func (s segment) String() string {
|
||||
return s.str
|
||||
|
||||
@@ -49,10 +49,6 @@ type Token struct {
|
||||
}
|
||||
|
||||
// New creates a validated Token from the provided parameters and options.
|
||||
//
|
||||
// When creating a delegated token, the Issuer's (iss) DID is assembed
|
||||
// using the public key associated with the private key sent as the first
|
||||
// parameter.
|
||||
func New(privKey crypto.PrivKey, aud did.DID, cmd command.Command, pol policy.Policy, opts ...Option) (*Token, error) {
|
||||
iss, err := did.FromPrivKey(privKey)
|
||||
if err != nil {
|
||||
@@ -90,12 +86,6 @@ func New(privKey crypto.PrivKey, aud did.DID, cmd command.Command, pol policy.Po
|
||||
return tkn, nil
|
||||
}
|
||||
|
||||
// Root creates a validated UCAN delegation Token from the provided
|
||||
// parameters and options.
|
||||
//
|
||||
// When creating a root token, both the Issuer's (iss) and Subject's
|
||||
// (sub) DIDs are assembled from the public key associated with the
|
||||
// private key passed as the first argument.
|
||||
func Root(privKey crypto.PrivKey, aud did.DID, cmd command.Command, pol policy.Policy, opts ...Option) (*Token, error) {
|
||||
sub, err := did.FromPrivKey(privKey)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,313 +0,0 @@
|
||||
package delegation_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
"github.com/ucan-wg/go-ucan/tokens/delegation"
|
||||
"github.com/ucan-wg/go-ucan/tokens/internal/envelope"
|
||||
)
|
||||
|
||||
// The following example shows how to create a delegation.Token with
|
||||
// distinct DIDs for issuer (iss), audience (aud) and subject (sub).
|
||||
func ExampleNew() {
|
||||
issuerPrivKey := examplePrivKey(issuerPrivKeyCfg)
|
||||
audienceDID := exampleDID(AudienceDID)
|
||||
command := exampleCommand(subJectCmd)
|
||||
policy := examplePolicy(subjectPol)
|
||||
subjectDID := exampleDID(subjectDID)
|
||||
|
||||
// Don't do this in your code - a nonce should be a cryptographically
|
||||
// strong random slice of bytes to ensure the integrity of your private
|
||||
// key. For this example, a fixed nonce is required to obtain the fixed
|
||||
// printed output (below). If unsure of what value to supply for the
|
||||
// nonce, don't pass the WithNonce option and one will be generated
|
||||
// when the token is created.
|
||||
nonce := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b}
|
||||
|
||||
tkn, err := delegation.New(
|
||||
issuerPrivKey,
|
||||
audienceDID,
|
||||
command,
|
||||
policy,
|
||||
delegation.WithSubject(subjectDID),
|
||||
delegation.WithNonce(nonce),
|
||||
)
|
||||
printThenPanicOnErr(err)
|
||||
data, id, err := tkn.ToSealed(issuerPrivKey)
|
||||
printThenPanicOnErr(err)
|
||||
|
||||
printCIDAndSealed(id, data)
|
||||
|
||||
//Output:
|
||||
//CID (base58BTC): zdpuAw26pFuvZa2Z9YAtpZZnWN6VmnRFr7Z8LVY5c7RVWoxGY
|
||||
//DAG-CBOR (base64) out: glhAmnAkgfjAx4SA5pzJmtaHRJtTGNpF1y6oqb4yhGoM2H2EUGbBYT4rVDjMKBgCjhdGHjipm00L8iR5SsQh3sIEBaJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cPZjaXNzeDhkaWQ6a2V5Ono2TWtwem4ybjNaR1QyVmFxTUdTUUMzdHptelY0VFM5UzcxaUZzRFhFMVdub05IMmNwb2yDg2I9PWcuc3RhdHVzZWRyYWZ0g2NhbGxpLnJldmlld2Vyg2RsaWtlZi5lbWFpbG0qQGV4YW1wbGUuY29tg2NhbnllLnRhZ3OCYm9ygoNiPT1hLmRuZXdzg2I9PWEuZXByZXNzY3N1Yng4ZGlkOmtleTp6Nk1rdEExdUJkQ3BxNHVKQnFFOWpqTWlMeXhaQmc5YTZ4Z1BQS0pqTXFzczZaYzJkbWV0YaBlbm9uY2VMAAECAwQFBgcICQoL
|
||||
//Converted to DAG-JSON out:
|
||||
//[
|
||||
// {
|
||||
// "/": {
|
||||
// "bytes": "mnAkgfjAx4SA5pzJmtaHRJtTGNpF1y6oqb4yhGoM2H2EUGbBYT4rVDjMKBgCjhdGHjipm00L8iR5SsQh3sIEBQ"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "h": {
|
||||
// "/": {
|
||||
// "bytes": "NO0BcQ"
|
||||
// }
|
||||
// },
|
||||
// "ucan/dlg@1.0.0-rc.1": {
|
||||
// "aud": "did:key:z6Mkq5YmbJcTrPExNDi26imrTCpKhepjBFBSHqrBDN2ArPkv",
|
||||
// "cmd": "/foo/bar",
|
||||
// "exp": null,
|
||||
// "iss": "did:key:z6Mkpzn2n3ZGT2VaqMGSQC3tzmzV4TS9S71iFsDXE1WnoNH2",
|
||||
// "meta": {},
|
||||
// "nonce": {
|
||||
// "/": {
|
||||
// "bytes": "AAECAwQFBgcICQoL"
|
||||
// }
|
||||
// },
|
||||
// "pol": [
|
||||
// [
|
||||
// "==",
|
||||
// ".status",
|
||||
// "draft"
|
||||
// ],
|
||||
// [
|
||||
// "all",
|
||||
// ".reviewer",
|
||||
// [
|
||||
// "like",
|
||||
// ".email",
|
||||
// "*@example.com"
|
||||
// ]
|
||||
// ],
|
||||
// [
|
||||
// "any",
|
||||
// ".tags",
|
||||
// [
|
||||
// "or",
|
||||
// [
|
||||
// [
|
||||
// "==",
|
||||
// ".",
|
||||
// "news"
|
||||
// ],
|
||||
// [
|
||||
// "==",
|
||||
// ".",
|
||||
// "press"
|
||||
// ]
|
||||
// ]
|
||||
// ]
|
||||
// ]
|
||||
// ],
|
||||
// "sub": "did:key:z6MktA1uBdCpq4uJBqE9jjMiLyxZBg9a6xgPPKJjMqss6Zc2"
|
||||
// }
|
||||
// }
|
||||
//]
|
||||
}
|
||||
|
||||
// The following example shows how to create a UCAN root delegation.Token
|
||||
// - a delegation.Token with the subject (sub) set to the value of issuer
|
||||
// (iss).
|
||||
func ExampleRoot() {
|
||||
issuerPrivKey := examplePrivKey(issuerPrivKeyCfg)
|
||||
audienceDID := exampleDID(AudienceDID)
|
||||
command := exampleCommand(subJectCmd)
|
||||
policy := examplePolicy(subjectPol)
|
||||
|
||||
// Don't do this in your code - a nonce should be a cryptographically
|
||||
// strong random slice of bytes to ensure the integrity of your private
|
||||
// key. For this example, a fixed nonce is required to obtain the fixed
|
||||
// printed output (below). If unsure of what value to supply for the
|
||||
// nonce, don't pass the WithNonce option and one will be generated
|
||||
// when the token is created.
|
||||
nonce := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b}
|
||||
|
||||
tkn, err := delegation.Root(
|
||||
issuerPrivKey,
|
||||
audienceDID,
|
||||
command,
|
||||
policy,
|
||||
delegation.WithNonce(nonce),
|
||||
)
|
||||
printThenPanicOnErr(err)
|
||||
data, id, err := tkn.ToSealed(issuerPrivKey)
|
||||
printThenPanicOnErr(err)
|
||||
|
||||
printCIDAndSealed(id, data)
|
||||
|
||||
//Output:
|
||||
//CID (base58BTC): zdpuAnbsR3e6DK8hBk5WA7KwbHYN6CKY4a3Bv1GNehvFYShQ8
|
||||
//DAG-CBOR (base64) out: glhA67ASBczF/wlIP0ESENn+4ZNQKukjcTNz+fo7K2tYa6OUm0rWICDJJkDWm7lJeQt+KvSA+Y4ctHTQbAr3Lr7mDqJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cPZjaXNzeDhkaWQ6a2V5Ono2TWtwem4ybjNaR1QyVmFxTUdTUUMzdHptelY0VFM5UzcxaUZzRFhFMVdub05IMmNwb2yDg2I9PWcuc3RhdHVzZWRyYWZ0g2NhbGxpLnJldmlld2Vyg2RsaWtlZi5lbWFpbG0qQGV4YW1wbGUuY29tg2NhbnllLnRhZ3OCYm9ygoNiPT1hLmRuZXdzg2I9PWEuZXByZXNzY3N1Yng4ZGlkOmtleTp6Nk1rcHpuMm4zWkdUMlZhcU1HU1FDM3R6bXpWNFRTOVM3MWlGc0RYRTFXbm9OSDJkbWV0YaBlbm9uY2VMAAECAwQFBgcICQoL
|
||||
//Converted to DAG-JSON out:
|
||||
//[
|
||||
// {
|
||||
// "/": {
|
||||
// "bytes": "67ASBczF/wlIP0ESENn+4ZNQKukjcTNz+fo7K2tYa6OUm0rWICDJJkDWm7lJeQt+KvSA+Y4ctHTQbAr3Lr7mDg"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "h": {
|
||||
// "/": {
|
||||
// "bytes": "NO0BcQ"
|
||||
// }
|
||||
// },
|
||||
// "ucan/dlg@1.0.0-rc.1": {
|
||||
// "aud": "did:key:z6Mkq5YmbJcTrPExNDi26imrTCpKhepjBFBSHqrBDN2ArPkv",
|
||||
// "cmd": "/foo/bar",
|
||||
// "exp": null,
|
||||
// "iss": "did:key:z6Mkpzn2n3ZGT2VaqMGSQC3tzmzV4TS9S71iFsDXE1WnoNH2",
|
||||
// "meta": {},
|
||||
// "nonce": {
|
||||
// "/": {
|
||||
// "bytes": "AAECAwQFBgcICQoL"
|
||||
// }
|
||||
// },
|
||||
// "pol": [
|
||||
// [
|
||||
// "==",
|
||||
// ".status",
|
||||
// "draft"
|
||||
// ],
|
||||
// [
|
||||
// "all",
|
||||
// ".reviewer",
|
||||
// [
|
||||
// "like",
|
||||
// ".email",
|
||||
// "*@example.com"
|
||||
// ]
|
||||
// ],
|
||||
// [
|
||||
// "any",
|
||||
// ".tags",
|
||||
// [
|
||||
// "or",
|
||||
// [
|
||||
// [
|
||||
// "==",
|
||||
// ".",
|
||||
// "news"
|
||||
// ],
|
||||
// [
|
||||
// "==",
|
||||
// ".",
|
||||
// "press"
|
||||
// ]
|
||||
// ]
|
||||
// ]
|
||||
// ]
|
||||
// ],
|
||||
// "sub": "did:key:z6Mkpzn2n3ZGT2VaqMGSQC3tzmzV4TS9S71iFsDXE1WnoNH2"
|
||||
// }
|
||||
// }
|
||||
//]
|
||||
}
|
||||
|
||||
// The following example demonstrates how to get a delegation.Token from
|
||||
// a DAG-CBOR []byte.
|
||||
func ExampleToken_FromSealed() {
|
||||
cborBytes := exampleCBORData()
|
||||
fmt.Println("DAG-CBOR (base64) in:", base64.StdEncoding.EncodeToString(cborBytes))
|
||||
|
||||
tkn, err := delegation.FromSealed(cborBytes)
|
||||
printThenPanicOnErr(err)
|
||||
|
||||
fmt.Println("CID (base58BTC):", envelope.CIDToBase58BTC(tkn.CID()))
|
||||
fmt.Println("Issuer (iss):", tkn.Issuer().String())
|
||||
fmt.Println("Audience (aud):", tkn.Audience().String())
|
||||
fmt.Println("Subject (sub):", tkn.Subject().String())
|
||||
fmt.Println("Command (cmd):", tkn.Command().String())
|
||||
fmt.Println("Policy (pol): TODO")
|
||||
fmt.Println("Nonce (nonce):", hex.EncodeToString(tkn.Nonce()))
|
||||
fmt.Println("Meta (meta): TODO")
|
||||
fmt.Println("NotBefore (nbf):", tkn.NotBefore())
|
||||
fmt.Println("Expiration (exp):", tkn.Expiration())
|
||||
|
||||
//Output:
|
||||
//DAG-CBOR (base64) in: glhAmnAkgfjAx4SA5pzJmtaHRJtTGNpF1y6oqb4yhGoM2H2EUGbBYT4rVDjMKBgCjhdGHjipm00L8iR5SsQh3sIEBaJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cPZjaXNzeDhkaWQ6a2V5Ono2TWtwem4ybjNaR1QyVmFxTUdTUUMzdHptelY0VFM5UzcxaUZzRFhFMVdub05IMmNwb2yDg2I9PWcuc3RhdHVzZWRyYWZ0g2NhbGxpLnJldmlld2Vyg2RsaWtlZi5lbWFpbG0qQGV4YW1wbGUuY29tg2NhbnllLnRhZ3OCYm9ygoNiPT1hLmRuZXdzg2I9PWEuZXByZXNzY3N1Yng4ZGlkOmtleTp6Nk1rdEExdUJkQ3BxNHVKQnFFOWpqTWlMeXhaQmc5YTZ4Z1BQS0pqTXFzczZaYzJkbWV0YaBlbm9uY2VMAAECAwQFBgcICQoL
|
||||
//CID (base58BTC): zdpuAw26pFuvZa2Z9YAtpZZnWN6VmnRFr7Z8LVY5c7RVWoxGY
|
||||
//Issuer (iss): did:key:z6Mkpzn2n3ZGT2VaqMGSQC3tzmzV4TS9S71iFsDXE1WnoNH2
|
||||
//Audience (aud): did:key:z6Mkq5YmbJcTrPExNDi26imrTCpKhepjBFBSHqrBDN2ArPkv
|
||||
//Subject (sub): did:key:z6MktA1uBdCpq4uJBqE9jjMiLyxZBg9a6xgPPKJjMqss6Zc2
|
||||
//Command (cmd): /foo/bar
|
||||
//Policy (pol): TODO
|
||||
//Nonce (nonce): 000102030405060708090a0b
|
||||
//Meta (meta): TODO
|
||||
//NotBefore (nbf): <nil>
|
||||
//Expiration (exp): <nil>
|
||||
}
|
||||
|
||||
func exampleCBORData() []byte {
|
||||
data, err := base64.StdEncoding.DecodeString("glhAmnAkgfjAx4SA5pzJmtaHRJtTGNpF1y6oqb4yhGoM2H2EUGbBYT4rVDjMKBgCjhdGHjipm00L8iR5SsQh3sIEBaJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cPZjaXNzeDhkaWQ6a2V5Ono2TWtwem4ybjNaR1QyVmFxTUdTUUMzdHptelY0VFM5UzcxaUZzRFhFMVdub05IMmNwb2yDg2I9PWcuc3RhdHVzZWRyYWZ0g2NhbGxpLnJldmlld2Vyg2RsaWtlZi5lbWFpbG0qQGV4YW1wbGUuY29tg2NhbnllLnRhZ3OCYm9ygoNiPT1hLmRuZXdzg2I9PWEuZXByZXNzY3N1Yng4ZGlkOmtleTp6Nk1rdEExdUJkQ3BxNHVKQnFFOWpqTWlMeXhaQmc5YTZ4Z1BQS0pqTXFzczZaYzJkbWV0YaBlbm9uY2VMAAECAwQFBgcICQoL")
|
||||
printThenPanicOnErr(err)
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func exampleDID(didStr string) did.DID {
|
||||
id, err := did.Parse(didStr)
|
||||
printThenPanicOnErr(err)
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func exampleCommand(cmdStr string) command.Command {
|
||||
cmd, err := command.Parse(cmdStr)
|
||||
printThenPanicOnErr(err)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func examplePolicy(policyJSON string) policy.Policy {
|
||||
pol, err := policy.FromDagJson(policyJSON)
|
||||
printThenPanicOnErr(err)
|
||||
|
||||
return pol
|
||||
}
|
||||
|
||||
func examplePrivKey(privKeyCfg string) crypto.PrivKey {
|
||||
privKeyMar, err := crypto.ConfigDecodeKey(privKeyCfg)
|
||||
printThenPanicOnErr(err)
|
||||
|
||||
privKey, err := crypto.UnmarshalPrivateKey(privKeyMar)
|
||||
printThenPanicOnErr(err)
|
||||
|
||||
return privKey
|
||||
}
|
||||
|
||||
func printCIDAndSealed(id cid.Cid, data []byte) {
|
||||
fmt.Println("CID (base58BTC):", envelope.CIDToBase58BTC(id))
|
||||
fmt.Println("DAG-CBOR (base64) out:", base64.StdEncoding.EncodeToString(data))
|
||||
fmt.Println("Converted to DAG-JSON out:")
|
||||
|
||||
node, err := ipld.Decode(data, dagcbor.Decode)
|
||||
printThenPanicOnErr(err)
|
||||
|
||||
rawJSON, err := ipld.Encode(node, dagjson.Encode)
|
||||
printThenPanicOnErr(err)
|
||||
|
||||
prettyJSON := &bytes.Buffer{}
|
||||
err = json.Indent(prettyJSON, rawJSON, "", "\t")
|
||||
printThenPanicOnErr(err)
|
||||
|
||||
fmt.Println(prettyJSON.String())
|
||||
}
|
||||
|
||||
func printThenPanicOnErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user