fix(ucanCtx): update test

This commit is contained in:
rarquevaux
2025-01-17 11:38:21 +01:00
committed by Michael Muré
parent 9e062b0cc7
commit cd9ee535ad

View File

@@ -4,14 +4,16 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"net/http/httptest"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/INFURA/go-ethlibs/jsonrpc" "github.com/INFURA/go-ethlibs/jsonrpc"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/fluent/qp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/did/didtest" "github.com/ucan-wg/go-ucan/did/didtest"
"github.com/ucan-wg/go-ucan/pkg/command" "github.com/ucan-wg/go-ucan/pkg/command"
@@ -24,7 +26,11 @@ import (
"github.com/INFURA/go-ucan-toolkit/server/exectx" "github.com/INFURA/go-ucan-toolkit/server/exectx"
) )
func ExampleContext() { const (
network = "eth-mainnet"
)
func TestCtx(t *testing.T) {
// let's use some pre-made DID+privkey. // let's use some pre-made DID+privkey.
// use go-ucan/did to generate or parse them. // use go-ucan/did to generate or parse them.
service := didtest.PersonaAlice service := didtest.PersonaAlice
@@ -44,35 +50,47 @@ func ExampleContext() {
policy.Like(".jsonrpc.method", "eth_*"), policy.Like(".jsonrpc.method", "eth_*"),
policy.Equal(".jsonrpc.method", literal.String("debug_traceCall")), policy.Equal(".jsonrpc.method", literal.String("debug_traceCall")),
), ),
// some infura constraints
// Network
policy.Equal(".inf.ntwk", literal.String(network)),
// Quota
policy.LessThanOrEqual(".inf.quota.ur", literal.Int(1234)),
) )
dlg, _ := delegation.Root(service.DID(), user.DID(), cmd, pol, dlg, err := delegation.Root(service.DID(), user.DID(), cmd, pol,
delegation.WithExpirationIn(24*time.Hour), delegation.WithExpirationIn(24*time.Hour),
) )
dlgBytes, dlgCid, _ := dlg.ToSealed(service.PrivKey()) require.NoError(t, err)
dlgBytes, dlgCid, err := dlg.ToSealed(service.PrivKey())
require.NoError(t, err)
// INVOCATION: the user leverages the delegation (power) to make a request. // INVOCATION: the user leverages the delegation (power) to make a request.
inv, _ := invocation.New(user.DID(), cmd, service.DID(), []cid.Cid{dlgCid}, inv, err := invocation.New(user.DID(), cmd, service.DID(), []cid.Cid{dlgCid},
invocation.WithExpirationIn(10*time.Minute), invocation.WithExpirationIn(10*time.Minute),
invocation.WithArgument("myarg", "hello"), // we can specify invocation parameters invocation.WithArgument("myarg", "hello"), // we can specify invocation parameters
) )
invBytes, _, _ := inv.ToSealed(user.PrivKey()) require.NoError(t, err)
invBytes, _, err := inv.ToSealed(user.PrivKey())
require.NoError(t, err)
// PACKAGING: no obligation for the transport, but the user needs to give the service the invocation // PACKAGING: no obligation for the transport, but the user needs to give the service the invocation
// and all the proof delegations. We can use a container for that. // and all the proof delegations. We can use a container for that.
cont := container.NewWriter() cont := container.NewWriter()
cont.AddSealed(dlgBytes) cont.AddSealed(dlgBytes)
cont.AddSealed(invBytes) cont.AddSealed(invBytes)
contBytes, _ := cont.ToBase64StdPadding() contBytes, err := cont.ToBase64StdPadding()
require.NoError(t, err)
// MAKING A REQUEST: we pass the container in the Bearer HTTP header // MAKING A REQUEST: we pass the container in the Bearer HTTP header
jrpc := jsonrpc.NewRequest() jrpc := jsonrpc.NewRequest()
jrpc.Method = "eth_call" jrpc.Method = "eth_call"
jrpc.Params = jsonrpc.MustParams("0x599784", true) jrpc.Params = jsonrpc.MustParams("0x599784", true)
jrpcBytes, _ := jrpc.MarshalJSON() jrpcBytes, err := jrpc.MarshalJSON()
req, _ := http.NewRequest(http.MethodGet, "/foo/bar", bytes.NewReader(jrpcBytes)) require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "/foo/bar", bytes.NewReader(jrpcBytes))
require.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+string(contBytes)) req.Header.Set("Authorization", "Bearer "+string(contBytes))
// SERVER: Auth middleware // SERVER: Auth middleware
@@ -84,13 +102,15 @@ func ExampleContext() {
// Note: we obviously want something more robust, this is an example // Note: we obviously want something more robust, this is an example
// Note: if an error occur, we'll want to return an HTTP 401 Unauthorized // Note: if an error occur, we'll want to return an HTTP 401 Unauthorized
data := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ") data := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
cont, _ := container.FromString(data) cont, err := container.FromString(data)
ucanCtx, _ := exectx.FromContainer(cont) require.NoError(t, err)
ucanCtx, err := exectx.FromContainer(cont)
require.NoError(t, err)
// insert into the go context // insert into the go context
req = req.WithContext(exectx.AddUcanCtxToContext(req.Context(), ucanCtx)) r = r.WithContext(exectx.AddUcanCtxToContext(r.Context(), ucanCtx))
next.ServeHTTP(w, req) next.ServeHTTP(w, r)
}) })
} }
@@ -98,7 +118,8 @@ func ExampleContext() {
httpMw := func(next http.Handler) http.Handler { httpMw := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ucanCtx, _ := exectx.FromContext(req.Context()) ucanCtx, ok := exectx.FromContext(r.Context())
require.True(t, ok)
err := ucanCtx.VerifyHttp(r) err := ucanCtx.VerifyHttp(r)
if err != nil { if err != nil {
@@ -115,12 +136,14 @@ func ExampleContext() {
jsonrpcMw := func(next http.Handler) http.Handler { jsonrpcMw := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ucanCtx, _ := exectx.FromContext(req.Context()) ucanCtx, ok := exectx.FromContext(r.Context())
require.True(t, ok)
var jrpc jsonrpc.Request var jrpc jsonrpc.Request
_ = json.NewDecoder(r.Body).Decode(&jrpc) err := json.NewDecoder(r.Body).Decode(&jrpc)
require.NoError(t, err)
err := ucanCtx.VerifyJsonRpc(&jrpc) err = ucanCtx.VerifyJsonRpc(&jrpc)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized) http.Error(w, err.Error(), http.StatusUnauthorized)
return return
@@ -129,21 +152,42 @@ func ExampleContext() {
}) })
} }
// SERVER: custom infura checks
infuraMw := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ucanCtx, ok := exectx.FromContext(r.Context())
require.True(t, ok)
err := ucanCtx.VerifyInfura(func(ma datamodel.MapAssembler) {
qp.MapEntry(ma, "ntwk", qp.String(network))
qp.MapEntry(ma, "quota", qp.Map(1, func(ma datamodel.MapAssembler) {
qp.MapEntry(ma, "ur", qp.Int(1234))
}))
})
require.NoError(t, err)
next.ServeHTTP(w, r)
})
}
// SERVER: final handler // SERVER: final handler
handler := func(w http.ResponseWriter, r *http.Request) { handler := func(w http.ResponseWriter, r *http.Request) {
ucanCtx, _ := exectx.FromContext(req.Context()) ucanCtx, ok := exectx.FromContext(r.Context())
require.True(t, ok)
if err := ucanCtx.ExecutionAllowed(); err != nil { if err := ucanCtx.ExecutionAllowed(); err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized) http.Error(w, err.Error(), http.StatusUnauthorized)
return return
} }
_, _ = fmt.Fprintln(w, "Success!") w.WriteHeader(http.StatusOK)
} }
// Ready to go! sut := authMw(httpMw(jsonrpcMw(infuraMw(http.HandlerFunc(handler)))))
_ = http.ListenAndServe("", authMw(httpMw(jsonrpcMw(http.HandlerFunc(handler)))))
rec := httptest.NewRecorder()
sut.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
} }
func TestGoCtx(t *testing.T) { func TestGoCtx(t *testing.T) {