From 41d679dfab35deb1647ddb02ab95889db3480a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 23 Jan 2025 14:36:34 +0100 Subject: [PATCH] extargs: make the hash more convenient to use by directly returning the invocation.Option --- toolkit/server/extargs/http.go | 7 +++- toolkit/server/extargs/http_test.go | 53 +++++++++++++++++--------- toolkit/server/extargs/jsonrpc.go | 7 +++- toolkit/server/extargs/jsonrpc_test.go | 53 +++++++++++++++++--------- 4 files changed, 80 insertions(+), 40 deletions(-) diff --git a/toolkit/server/extargs/http.go b/toolkit/server/extargs/http.go index 743ec0d..e2062f6 100644 --- a/toolkit/server/extargs/http.go +++ b/toolkit/server/extargs/http.go @@ -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) { diff --git a/toolkit/server/extargs/http_test.go b/toolkit/server/extargs/http_test.go index 401bd3a..ebb20f7 100644 --- a/toolkit/server/extargs/http_test.go +++ b/toolkit/server/extargs/http_test.go @@ -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()) diff --git a/toolkit/server/extargs/jsonrpc.go b/toolkit/server/extargs/jsonrpc.go index 418f844..51e911e 100644 --- a/toolkit/server/extargs/jsonrpc.go +++ b/toolkit/server/extargs/jsonrpc.go @@ -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) { diff --git a/toolkit/server/extargs/jsonrpc_test.go b/toolkit/server/extargs/jsonrpc_test.go index 1346f1d..ceb715d 100644 --- a/toolkit/server/extargs/jsonrpc_test.go +++ b/toolkit/server/extargs/jsonrpc_test.go @@ -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())