extargs: make the hash more convenient to use by directly returning the invocation.Option

This commit is contained in:
Michael Muré
2025-01-23 14:36:34 +01:00
committed by Michael Muré
parent cc661f3936
commit 41d679dfab
4 changed files with 80 additions and 40 deletions

View File

@@ -15,6 +15,7 @@ import (
"github.com/multiformats/go-multihash"
"github.com/ucan-wg/go-ucan/pkg/args"
"github.com/ucan-wg/go-ucan/pkg/policy"
"github.com/ucan-wg/go-ucan/token/invocation"
)
// HttpArgsKey is the key in the args, used for:
@@ -111,7 +112,9 @@ func (hea *HttpExtArgs) verifyHash() error {
// If that hash is inserted at the HttpArgsKey key in the invocation arguments,
// this increases the security as the UCAN token cannot be used with a different
// HTTP request.
func MakeHttpHash(req *http.Request) ([]byte, error) {
// For convenience, the hash is returned as a read to use invocation argument.
func MakeHttpHash(req *http.Request) (invocation.Option, error) {
// Note: the hash is computed on the full IPLD args, including HttpArgsKey
computedArgs, err := makeHttpArgs(req)
if err != nil {
return nil, err
@@ -132,7 +135,7 @@ func MakeHttpHash(req *http.Request) ([]byte, error) {
return nil, err
}
return sum, nil
return invocation.WithArgument(HttpArgsKey, []byte(sum)), nil
}
func makeHttpArgs(req *http.Request) (*args.Args, error) {

View File

@@ -7,9 +7,12 @@ import (
"github.com/multiformats/go-multihash"
"github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/did/didtest"
"github.com/ucan-wg/go-ucan/pkg/args"
"github.com/ucan-wg/go-ucan/pkg/command"
"github.com/ucan-wg/go-ucan/pkg/policy"
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
"github.com/ucan-wg/go-ucan/token/invocation"
)
func TestHttp(t *testing.T) {
@@ -131,6 +134,9 @@ func TestHttp(t *testing.T) {
}
func TestHttpHash(t *testing.T) {
servicePersona := didtest.PersonaAlice
clientPersona := didtest.PersonaBob
req, err := http.NewRequest(http.MethodGet, "http://example.com/foo", nil)
require.NoError(t, err)
req.Header.Add("User-Agent", "Chrome/51.0.2704.103 Safari/537.36")
@@ -140,40 +146,51 @@ func TestHttpHash(t *testing.T) {
policy.Equal(".http.scheme", literal.String("http")),
)
makeArg := func(data []byte, code uint64) invocation.Option {
mh, err := multihash.Sum(data, code, -1)
require.NoError(t, err)
return invocation.WithArgument(HttpArgsKey, []byte(mh))
}
tests := []struct {
name string
hash []byte
expected bool
name string
argOptions []invocation.Option
expected bool
}{
{
name: "correct hash",
hash: must(MakeHttpHash(req)),
expected: true,
name: "correct hash",
argOptions: []invocation.Option{must(MakeHttpHash(req))},
expected: true,
},
{
name: "non-matching hash",
hash: must(multihash.Sum([]byte{1, 2, 3, 4}, multihash.SHA2_256, -1)),
expected: false,
name: "non-matching hash",
argOptions: []invocation.Option{makeArg([]byte{1, 2, 3, 4}, multihash.SHA2_256)},
expected: false,
},
{
name: "wrong type of hash",
hash: must(multihash.Sum([]byte{1, 2, 3, 4}, multihash.BLAKE3, -1)),
expected: false,
name: "wrong type of hash",
argOptions: []invocation.Option{makeArg([]byte{1, 2, 3, 4}, multihash.BLAKE3)},
expected: false,
},
{
name: "no hash",
hash: nil,
expected: false,
name: "no hash",
argOptions: nil,
expected: true, // having a hash is not enforced
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
invArgs := args.New()
err := invArgs.Add(HttpArgsKey, tc.hash)
inv, err := invocation.New(
clientPersona.DID(),
command.MustParse("/foo"),
servicePersona.DID(),
nil,
tc.argOptions..., // inject hash argument, if any
)
require.NoError(t, err)
ctx := NewHttpExtArgs(pol, invArgs.ReadOnly(), req)
ctx := NewHttpExtArgs(pol, inv.Arguments(), req)
if tc.expected {
require.NoError(t, ctx.Verify())

View File

@@ -16,6 +16,7 @@ import (
"github.com/ucan-wg/go-ucan/pkg/args"
"github.com/ucan-wg/go-ucan/pkg/policy"
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
"github.com/ucan-wg/go-ucan/token/invocation"
)
// JsonRpcArgsKey is the key in the args, used for:
@@ -112,7 +113,9 @@ func (jrea *JsonRpcExtArgs) verifyHash() error {
// If that hash is inserted at the JsonRpcArgsKey key in the invocation arguments,
// this increases the security as the UCAN token cannot be used with a different
// JsonRPC request.
func MakeJsonRpcHash(req *jsonrpc.Request) ([]byte, error) {
// For convenience, the hash is returned as a read to use invocation argument.
func MakeJsonRpcHash(req *jsonrpc.Request) (invocation.Option, error) {
// Note: the hash is computed on the full IPLD args, including JsonRpcArgsKey
computedArgs, err := makeJsonRpcArgs(req)
if err != nil {
return nil, err
@@ -133,7 +136,7 @@ func MakeJsonRpcHash(req *jsonrpc.Request) ([]byte, error) {
return nil, err
}
return sum, nil
return invocation.WithArgument(JsonRpcArgsKey, []byte(sum)), nil
}
func makeJsonRpcArgs(req *jsonrpc.Request) (*args.Args, error) {

View File

@@ -6,9 +6,12 @@ import (
"github.com/INFURA/go-ethlibs/jsonrpc"
"github.com/multiformats/go-multihash"
"github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/did/didtest"
"github.com/ucan-wg/go-ucan/pkg/args"
"github.com/ucan-wg/go-ucan/pkg/command"
"github.com/ucan-wg/go-ucan/pkg/policy"
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
"github.com/ucan-wg/go-ucan/token/invocation"
)
func TestJsonRpc(t *testing.T) {
@@ -112,6 +115,9 @@ func TestJsonRpc(t *testing.T) {
}
func TestJsonRpcHash(t *testing.T) {
servicePersona := didtest.PersonaAlice
clientPersona := didtest.PersonaBob
req := jsonrpc.MustRequest(1839673506133526, "debug_traceCall",
true, false, 1234, "ho_no",
)
@@ -119,40 +125,51 @@ func TestJsonRpcHash(t *testing.T) {
policy.Equal(".jsonrpc.method", literal.String("debug_traceCall")),
)
makeArg := func(data []byte, code uint64) invocation.Option {
mh, err := multihash.Sum(data, code, -1)
require.NoError(t, err)
return invocation.WithArgument(JsonRpcArgsKey, []byte(mh))
}
tests := []struct {
name string
hash []byte
expected bool
name string
argOptions []invocation.Option
expected bool
}{
{
name: "correct hash",
hash: must(MakeJsonRpcHash(req)),
expected: true,
name: "correct hash",
argOptions: []invocation.Option{must(MakeJsonRpcHash(req))},
expected: true,
},
{
name: "non-matching hash",
hash: must(multihash.Sum([]byte{1, 2, 3, 4}, multihash.SHA2_256, -1)),
expected: false,
name: "non-matching hash",
argOptions: []invocation.Option{makeArg([]byte{1, 2, 3, 4}, multihash.SHA2_256)},
expected: false,
},
{
name: "wrong type of hash",
hash: must(multihash.Sum([]byte{1, 2, 3, 4}, multihash.BLAKE3, -1)),
expected: false,
name: "wrong type of hash",
argOptions: []invocation.Option{makeArg([]byte{1, 2, 3, 4}, multihash.BLAKE3)},
expected: false,
},
{
name: "no hash",
hash: nil,
expected: false,
name: "no hash",
argOptions: nil,
expected: true, // having a hash is not enforced
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
invArgs := args.New()
err := invArgs.Add(JsonRpcArgsKey, tc.hash)
inv, err := invocation.New(
clientPersona.DID(),
command.MustParse("/foo"),
servicePersona.DID(),
nil,
tc.argOptions..., // inject hash argument, if any
)
require.NoError(t, err)
ctx := NewJsonRpcExtArgs(pol, invArgs.ReadOnly(), req)
ctx := NewJsonRpcExtArgs(pol, inv.Arguments(), req)
if tc.expected {
require.NoError(t, ctx.Verify())