adjust the toolkit to the new location
This commit is contained in:
committed by
Michael Muré
parent
06f478b9c3
commit
0647e4ff8a
1
go.mod
1
go.mod
@@ -6,6 +6,7 @@ toolchain go1.24.5
|
||||
|
||||
require (
|
||||
github.com/MetaMask/go-did-it v1.0.0-pre1
|
||||
github.com/avast/retry-go/v4 v4.6.1
|
||||
github.com/ipfs/go-cid v0.5.0
|
||||
github.com/ipld/go-ipld-prime v0.21.0
|
||||
github.com/multiformats/go-multibase v0.2.0
|
||||
|
||||
2
go.sum
2
go.sum
@@ -1,6 +1,8 @@
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/MetaMask/go-did-it v1.0.0-pre1 h1:NTGAC7z52TwFegEF7c+csUr/6Al1nAo6ValAAxOsjto=
|
||||
github.com/MetaMask/go-did-it v1.0.0-pre1/go.mod h1:7m9syDnXFTg5GmUEcydpO4Rs3eYT4McFH7vCw5fp3A4=
|
||||
github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=
|
||||
github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
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
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
PH4sIAAAAAAAA_5zWa5cUxR3HcVcRiejmuCII3siKwgnubnd1dVW1eIS5rzM9w8zs3HZETFd19_T07PTcZ2cH1KMgoB41YgIo3nM0G1GCRhCRiILIEaIhIEo0Bi9c9ewaFfEazYMd8iCPdvIGvk8-p_71e1RnZaujyt_e2_LQLYk5Fbb1QefFR9-_5LzWO59-9PfWoWV_nN_f177z06eH31j13Je8-Wpk35bTccKjtV_22MRfnz9DufuSg8Ntn02KzZtw83sLxj-mGE441FIoVZhidal9qTl8J9fJdRRZJz_IlIpaI2pavTKjDVxZR_5MQXa4aFIFgr-gMCrrOTWUqWNajNZERzin0GLRz7IxsTcfAgnGsqrRpedyXVQpMq2Wn5K68dmLWLpU-p9msRbQ-usuHEsGorZK0BbwxWzxvCdrhDK2vL23UMCRTM0RlStVxPK5vpsXMaWvj3Vee90i5Wq9s6r0VbRTWalC__-smtXKyu8MERNOwJSZkCACeAFgzkC6xGkYAhOLqkJEiBXJkJAuCUBUTMIA4KEgAEWzchbT5DXHfxwc7LiqNnPmRwdHgBa8NO6jDQ9uvW6wa7DHYetfP3DxO4s7JhybLa1YcerUV-Yae7aDww_P2_zjvH9euWqOOW3yjBWtO2__bKzLvfnR_enEpmaBMtFuy4qEsgrlqRMpPm86mql6apjI4V63V_bFgl5WT-USpQhDowVK2amiFmM8tEUsiRJcCeowHM9AvVQreLGE83LMkcBBI99dAE0ANZNtAGFewJjwkilBJPG8TphoKEQhmihKpsgBXuIVhXEGRRQpRGAmhzkRqIpOaQNo4afuoTWTj60cfjw5fQQoxy98ZPGmF3YsWLJo8voJ587Y9-X38geyLBq_nHXZIy9fPun5m1pebqVzv7J-NuaM08XEzYl3v3kmtfuCr2Yc-VAa3vtxS7NAnpKSKsBEzVaMphWdFB0kgNSEgHpwiPe7a0qaxYtVG_SVq_7RAhkey-asYmfUZ1XKMbGSr-Vo1UZ60rZyNV6Uy8l8MY_iES1ic0abAGom2wCCEqcLVFRNpEKBaghAaCBJUYgKOVNVGMQCUyXRQBqFTJWwKQkIIoZFQW8AvZV64bRDez_6flYbbmu8oDa_euu2yJB9w1vX3uO-YrZvobj2yK07V7-1YebjrRcgbsvS4fiyJ9_gpx4KvT5n_A7PTz-cqF_asuuaoTFnn2N-bbU2CaSHi0bJlrEi3kyh6NFrgqOu59VKOJ2t9Xj4sllT4wXqJdFQfzI2aiBvIGSG_Zls2IsDSdKblRV3QlQz2X7Z2evudVBk16QIM1yiCZsBaiLbAOIEnROQppu8AjgKiKqIhgQVwDQCTCKJkqCIBIqGpoicCilvcoxDWJewcPLE3TD0fHzb0y_BrtRd2gjQN3fd--36ju_CYzuV8adcsOeA95UvVv-09N1t05_bN7DllouWTN-18r4nTm-9Y8uZ2L02fj1XLizHlzyfrZ-x65Pt503b0uyJs0oDhpWuSh4vwFpJjlcDciKRCbtQTzmvxkWrKIWCUQm51FomO1ogKyMLyXSPx0xHxKrixwFT7a9XgzUUTuedXgDi8XKmgGJmJOwMNQHUTLYBhERGkE6YCTXEYYAYxxuE5xGnCbopAkWjAtCgbhCMKAUQmQqkvA4wJaQBpNyhPfDyK9Ijs-yOv40ArTv_hoMtSbB12NjtzD1Ef35ow47k_D3frn41f-2LX6YmdTxzzba3t180_7XFV5m-fz304tj2Td-t7Nx_oblszv7ctI2Hmz1xhayl1myOkhEOxUt2S_S4shFvMI7dIbseyPa4AlalJ8q6g2KhGBktUJGVfQEaSsXtsK7EnXnC6zIP84ZHrYUyGR9vcxXj9lwuaIvG7E0ANZM9-QcBHkNCNFMFEKhYFTRiSBoUOV0RTE3DKmWMCJKhijzgeJWakgoIwyLUuAZQSlq-Y177mBOSHt49AjRF-PBAe9d1H3ffcvyZczrHLAhP9X3jC5y797YFa-mNxYl_evDqo4fu_8NZh3d88un1R1f_5cfBf8_76p3tv_psyooll_k-nj2mWaC6b8BCmVrCytYjIFZKF1wytmUFKdijVuoCLIdrBV8dk3AxbIwWqJRAkpyRMnkY9ReLcipX8PR3u6RcrheykFINGCCc7-lJ25LMpjUB1Ez25IljDHFYpCYUiUYR4yVkUMKYIEiSiTDBSFcRgAajPAOU6ibjmShBToOwAXRf-6V31zceuXHdjHV9I0D7Jn7wgXzr4TVX_-PYw_mpLatO69_qW7R1Q-m8o9OcsGP2rM_f3lM_3n0_-aL18zWHV-eHzkik9r4_LriMt16a98Tm889sdiTASDqRwFLW3evj6_mU6i96cqInGEY4Ea0F1brlsPUXJSlCrcpogfRCzGll1YKRsqXrWEuVajq21wK5buKv5FxOV7wQy4Wz5Wwg3NRIaCbbABKopoi6BkymSSrTRCpSQ8UqEEQBmkTTBQp0VcSGBgAvYaiZCgQMUiz89wXdVtm7cNtjqvLmb5ZtHAHq2vfX9Uem2q-ZXTzw3oub0ruemj52f29hwuWeZ2_6_qylidcO5d9p3310-_wptTV3Dv1WXZzbuLsNDU2-dzDy7Sf84MxxTQKZAxWi5wO0Agci0WTOnStHklUpbPaHSi7JcsV9UbvH6_IHzHTVNeqRwLtlj6rV_T3ZehxGex2KLA3EXX4XhNmaGZdj-VjCRgy37HA4mhkJTWQbQBImhOkcbyIeQA1oiOcNBVBEdBWYlOc0IgBAREPnBYoABSavUAI0RhFuAM22T_nzs8NTTyz94YFVI0Bze_9-4utz3dvfW5B7fXL7uJWvr-1rO-uG5evusY_jlk-ynvzheOfE5IFIt4ieunAfO3bK-CWuX2w-0HbQP_fDi-8Y2NPsitNYLCfr2YLGEhrwg7LNoL3ZWKLSAzwDxNvNV_OA1L0oFVdToz5xuURswBupyoo7Ggua8Vg8kIjV5TLvcyCUDJsV7PXKuglTehn5mwBqJnvyBSEd6qpKTV5HCEOeU4mBCJSwpImmIkBd4BRGoUEUHUlMQ6amQVVVJB2CBpBwxRsH-9-cvnYnOfvd_wQAAP__hMJ751MQAAA
|
||||
PH4sIAAAAAAAA_5zW65MU1f3HcS7-hB8igpfIgqUxCqLC0tfTfUADMzs7M87ObubaOzMEsM-lp6eb6Zmee49GvEKwVBQRgygIUVHLCwoqoJAAikCQaJRICCqFGPCCcXVFoaBMJbukKs929h94P3nV-ZzvSg2XrEkV9u7kwFW3J6bv7bj7tflfrjm4e8_grc7Dke4jPx6c_Mm87olH_O9Of35_59-X7z2xe056dKx7156F074aOXzCcemCRe-Y4T3n3v7SNV-fdd4qVfcLA48NPDbwArtYxqo1mcxJT2ebmWZmUgE3s09jtUxqMsmQKSZ1ptRBu5lF0WK75ZbawpVO25NW2rLtfgG5XFZKtlwBr1GPVfyKU9YjZS_BOEv0yVouNxmpBUxr-TH6osmv4Uyx-L9NM5PUWqLtmqTohbQnkWp1i5JlxiOWLxAFAdaJeU13Z0gwWsN2AOdzc267A6tz5uDmGTPvUH-pNVfUOWU6CBfLqP9ZkqUl9fc6w3KqRmTVUDWGB1BiiKoDCBhJptRABAuCxrJY0zHPQBZjbLCY8hKCVBOplbMwDXbdsO9O4ZT5m20fTPywBymh3HfTBjDl5MzlT65xpm15kC4-f_2hlTtKyQUbrj78wBPfjn1e3T9i7OmuEfmt25d4z_q2K_0H36lRiw_9NbK0Szy0d3A_kPKuFk6qOYqVTSCj2uGNknxdT0BN0Etlqklxr8QaSqzF468oQl-R0oo7rztKQaK5crqzmMq7vOV4uh4jFZ2zPIrfFL0hS_ZYOFikDSA1ku1FAqJEZIRUA2gMy0OeY4EOKGVlHmCDIgZKKhYhp2NKGQR5YvBYZAWVp4j0Io1b4N905xU_e_SJj1-4pAfpbefa207fnMit_9XP39xxw7D3Fk0bH7vs09W_3uwcG3Vk5Nxv14yYP2Zj7lLPQ01Nnw0bsmXTssuZprL6-dX3zB3U9NH9q8_tB1KhgwUG8gA54HH5OcMyW1vqjmLnO11GNB0Ph6ut8WI1zAbdSMv2FclKhWK2riCS9trFvDvmd3tC0bpo8SAeC5QExZfiO3LeIOuKePkGkBrJnkEiWJYlnjEg1nhERSKouowIUgEvGUTGRBN5Ims6QiyVZVUwNIp4GUNOxr1I199x4OH_y6103Q-3DOhBuhYw1x1Y9eHYjejREfahp2Z9SNaqo83Xb0yff-mFr3_srFg7at3FV2aEy28atv2p8zbc-313YOQ3y65gm1Z9MHzdipHm0H4gFYmaLrQGWjpFLp4ICmagamWKHI560tlAsoa1uNcotoQKWj7fGuvz3Ck1v5hJgvZQJGYIKTlcFrypWocVLHPuEC_oWa2e0M3OVsen-BuZuwayZ-YOckQARDJ4TLAsChqUdVHVOBkA1pA5GVKGkaiqA04CDISiIcoUMhRjCHuRmjJraxO7f_ji-PiTE3pf0g83F4Zc-hF4-7uTV4XPG7xw986TC__2_qlnjrf9OLDrrKfeXioPGvtN0-NHRv8OHh7_5fzP1V3Lt40Zar-z-c3Vtx5O54b0AykTCrGsVS1iKmkdapQ1q6lqKWkV2gD1J3MIY09SlHw5O6yHgn1FqnoTLU66YobsbASoKq46rmoka2c8LomNkHBFYH2VVldNa-GTQgNIjWR7kTiVAIYRsYEFTcMCgpDqogw4TFXGwKqmYaACRtMJ4ClmEDAQFaHAYAbSXqRXxt9z8fIVxXOXjpjI9SDtXIkK6MJ3T6xbv-WuN7azi9K8UGl5a1t056x9G6fgxJJ5g03lSBk_O2juXV0Lnh2kVHfIwcqrKw-9dboId5X-eU5__iRPLhyxMsF03C9ZnWYKtRfZajIbUDjb68k7XFG2UL5adKesKOwrku42qkgrRaK2lI1jYJaMelnpaLf9RhW0sfmUrjk-FymaAcHBDSA1ku1FYjVeBBpGBqUCpjIUKdI5TuAwwqqhiQAAyvAQ6BoDBCgiwSCizMsSQhzfi_SP-2K_-PGE9saxTzMze5Dy-z0P7Di679olRys3fzdIXbZgbpvy0Krm5oXnnP_1w9yzj0yF8_64bCjzW2H22feEv48NLk6_8UD32fWPNvlf_CS18OUB_UDSE1WcaNUKSsqx_SjA-zNeLYI4T0cybfM4pbMg4AknnbbOetjqK1K-blcKUt4DWcNrVBzBIiJ2fGE7W8gCKZbI4WhBy4KkP9MWhA0gNZL9LxJLVMQhA_KEkTAjEkbnBZFILMQGQRLPi5gSpAOqEShqqoFFWZA4jCHoRRLeO9XcMqtwy7p35cd6kIY99snirRBtnvqcHN9benz4hCuG3LJt9bafZki7thTP_mHpi5PGXfbY6N1Nm68Z992tLD51S-q5DR98dsnhAbNSNzwZvOr_-4FUTdpy0XIjlg0nZSHkqaneUBVUCylgZEtmwQ3LhZrhR3WtkrX7PHegSpISVKtKuCYFyu11s5pK6FHbR112BNtsIZUOt1fSHVIuLzYydw1kz8ydoGqUF4nBizIHZYFyog5ZLHCU0QwEOZGROVEiusr824blDCgBSSIMgGdO8ClPP7KYPJnsQIeKG3uQrnz5teFvvD98hrrOc3nrmK3rn9s_-09PjBzaddOwxDl3Hz1xfEx810uPhvYcbL3sL9dNWjnuxamjXnCFEheAP29i5nXf3tGf6y7r6qwrJZfHlLyGLCvuWCf05Qp6JGDYBaNWqnvS5bKN_VRBVaWvSDnJBG4lHBEAl05yIccVIHZabmmLBttdobSjaKY3FRY9alvY42oAqZFsL5KKCAaEQkMlokgpyzKijhlRpaIoGZClCLCU1ahOIGQhFWSDQwLLqkhlzrwkvOH-ScEtSw68Omr87B6kpYWRa185WbvuC8u8aMC0i1pXbHeO72P9982fsHhh2xbf8WGe0y5Zq-wAk3YenZ05evG93MzVAx78aXJlxr1r2hce7A9Srqh6jIIVjNU7INENUg7Tqmz7tFBb2pfy-oq6zxuOOO0kWo27-3yC58tCwi900DxOtpk-k2txFCfuCGKllnOscqVk-W0N1dmoFcw3coI3kO1FElVWYhiiGhzgCS-xPES6zFKVAZxgyIAXJZWjAOsygppAEG_8Z-wIQZzai7Ri5eyp3V8981nL9fET_woAAP__svLMp3sQAAA
|
||||
Binary file not shown.
Binary file not shown.
@@ -142,12 +142,29 @@ func (ctn Reader) GetDelegation(cid cid.Cid) (*delegation.Token, error) {
|
||||
return nil, delegation.ErrDelegationNotFound
|
||||
}
|
||||
|
||||
// GetDelegationBundle is the same as GetToken but only return a delegation.Bundle, with the right type.
|
||||
// If not found, delegation.ErrDelegationNotFound is returned.
|
||||
func (ctn Reader) GetDelegationBundle(cid cid.Cid) (*delegation.Bundle, error) {
|
||||
bndl, ok := ctn[cid]
|
||||
if !ok {
|
||||
return nil, delegation.ErrDelegationNotFound
|
||||
}
|
||||
if tkn, ok := bndl.token.(*delegation.Token); ok {
|
||||
return &delegation.Bundle{
|
||||
Cid: cid,
|
||||
Decoded: tkn,
|
||||
Sealed: bndl.sealed,
|
||||
}, nil
|
||||
}
|
||||
return nil, delegation.ErrDelegationNotFound
|
||||
}
|
||||
|
||||
// GetAllDelegations returns all the delegation.Token in the container.
|
||||
func (ctn Reader) GetAllDelegations() iter.Seq[delegation.Bundle] {
|
||||
return func(yield func(delegation.Bundle) bool) {
|
||||
func (ctn Reader) GetAllDelegations() iter.Seq[*delegation.Bundle] {
|
||||
return func(yield func(*delegation.Bundle) bool) {
|
||||
for c, bndl := range ctn {
|
||||
if t, ok := bndl.token.(*delegation.Token); ok {
|
||||
if !yield(delegation.Bundle{
|
||||
if !yield(&delegation.Bundle{
|
||||
Cid: c,
|
||||
Decoded: t,
|
||||
Sealed: bndl.sealed,
|
||||
|
||||
@@ -73,12 +73,12 @@ func (ctn Writer) ToBase64URLWriter(w io.Writer) error {
|
||||
return ctn.toWriter(headerBase64URL, w)
|
||||
}
|
||||
|
||||
// ToBase64URL encode the container into pre-gzipped base64 string, with URL-safe encoding and no padding.
|
||||
// ToBase64URLGzipped encode the container into pre-gzipped base64 string, with URL-safe encoding and no padding.
|
||||
func (ctn Writer) ToBase64URLGzipped() (string, error) {
|
||||
return ctn.toString(headerBase64URLGzip)
|
||||
}
|
||||
|
||||
// ToBase64URLWriter is the same as ToBase64URL, but with an io.Writer.
|
||||
// ToBase64URLGzipWriter is the same as ToBase64URL, but with an io.Writer.
|
||||
func (ctn Writer) ToBase64URLGzipWriter(w io.Writer) error {
|
||||
return ctn.toWriter(headerBase64URLGzip, w)
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/MetaMask/go-did-it/didtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/didtest"
|
||||
)
|
||||
|
||||
//go:embed testdata/new.dagjson
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/MetaMask/go-did-it"
|
||||
didkeyctl "github.com/MetaMask/go-did-it/controller/did-key"
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
"github.com/MetaMask/go-did-it/didtest"
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
@@ -19,7 +20,6 @@ import (
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/policytest"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation/delegationtest"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/didtest"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ucan-wg/go-ucan/token/internal/didtest"
|
||||
"github.com/MetaMask/go-did-it/didtest"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/MetaMask/go-did-it/didtest"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||
@@ -17,7 +18,6 @@ import (
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/didtest"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
_ "embed"
|
||||
"testing"
|
||||
|
||||
"github.com/MetaMask/go-did-it/didtest"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/didtest"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
// Package didtest provides Personas that can be used for testing. Each
|
||||
// Persona has a name, crypto.PrivKey and associated crypto.PubKey and
|
||||
// did.DID.
|
||||
package didtest
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/MetaMask/go-did-it"
|
||||
didkeyctl "github.com/MetaMask/go-did-it/controller/did-key"
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
"github.com/MetaMask/go-did-it/crypto/ed25519"
|
||||
)
|
||||
|
||||
const (
|
||||
// all are ed25519 as base64
|
||||
alicePrivKeyB64 = "zth/9cTSUVwlLzfEWwLCcOkaEmjrRGPOI6mOJksWAYZ3Toe7ymxAzDeiseyxbmEpJ81qYM3dZ8XrXqgonnTTEw=="
|
||||
bobPrivKeyB64 = "+p1REV3MkUnLhUMbFe9RcSsmo33TT/FO85yaV+c6fiYJCBsdiwfMwodlkzSAG3sHQIuZj8qnJ678oJucYy7WEg=="
|
||||
carolPrivKeyB64 = "aSu3vTwE7z3pXaTaAhVLeizuqnZUJZQHTCSLMLxyZh5LDoZQn80uoQgMEdsbOhR+zIqrjBn5WviGurDkKYVfug=="
|
||||
danPrivKeyB64 = "s1zM1av6og3o0UMNbEs/RyezS7Nk/jbSYL2Z+xPEw9Cho/KuEAa75Sf4yJHclLwpKXNucbrZ2scE8Iy8K05KWQ=="
|
||||
erinPrivKeyB64 = "+qHpaAR3iivWMEl+pkXmq+uJeHtqFiY++XOXtZ9Tu/WPABCO+eRFrTCLJykJEzAPGFmkJF8HQ7DMwOH7Ry3Aqw=="
|
||||
frankPrivKeyB64 = "4k/1N0+Fq73DxmNbGis9PY2KgKxWmtDWhmi1E6sBLuGd7DS0TWjCn1Xa3lXkY49mFszMjhWC+V6DCBf7R68u4Q=="
|
||||
)
|
||||
|
||||
// Persona is a generic participant used for cryptographic testing.
|
||||
type Persona int
|
||||
|
||||
// The provided Personas were selected from the first few generic
|
||||
// participants listed in this [table].
|
||||
//
|
||||
// [table]: https://en.wikipedia.org/wiki/Alice_and_Bob#Cryptographic_systems
|
||||
const (
|
||||
PersonaAlice Persona = iota + 1
|
||||
PersonaBob
|
||||
PersonaCarol
|
||||
PersonaDan
|
||||
PersonaErin
|
||||
PersonaFrank
|
||||
)
|
||||
|
||||
var privKeys map[Persona]crypto.PrivateKeySigningBytes
|
||||
|
||||
func init() {
|
||||
privKeys = make(map[Persona]crypto.PrivateKeySigningBytes, 6)
|
||||
for persona, pB64 := range privKeyB64() {
|
||||
privBytes, err := base64.StdEncoding.DecodeString(pB64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
privKey, err := ed25519.PrivateKeyFromBytes(privBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
privKeys[persona] = privKey
|
||||
}
|
||||
}
|
||||
|
||||
// DID returns a did.DID based on the Persona's Ed25519 public key.
|
||||
func (p Persona) DID() did.DID {
|
||||
return didkeyctl.FromPrivateKey(p.PrivKey())
|
||||
}
|
||||
|
||||
// Name returns the username of the Persona.
|
||||
func (p Persona) Name() string {
|
||||
name, ok := map[Persona]string{
|
||||
PersonaAlice: "Alice",
|
||||
PersonaBob: "Bob",
|
||||
PersonaCarol: "Carol",
|
||||
PersonaDan: "Dan",
|
||||
PersonaErin: "Erin",
|
||||
PersonaFrank: "Frank",
|
||||
}[p]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Unknown persona: %v", p))
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
// PrivKey returns the Ed25519 private key for the Persona.
|
||||
func (p Persona) PrivKey() crypto.PrivateKeySigningBytes {
|
||||
res, ok := privKeys[p]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Unknown persona: %v", p))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (p Persona) PrivKeyConfig() string {
|
||||
res, ok := privKeyB64()[p]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Unknown persona: %v", p))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// PubKey returns the Ed25519 public key for the Persona.
|
||||
func (p Persona) PubKey() crypto.PublicKey {
|
||||
return p.PrivKey().Public()
|
||||
}
|
||||
|
||||
func privKeyB64() map[Persona]string {
|
||||
return map[Persona]string{
|
||||
PersonaAlice: alicePrivKeyB64,
|
||||
PersonaBob: bobPrivKeyB64,
|
||||
PersonaCarol: carolPrivKeyB64,
|
||||
PersonaDan: danPrivKeyB64,
|
||||
PersonaErin: erinPrivKeyB64,
|
||||
PersonaFrank: frankPrivKeyB64,
|
||||
}
|
||||
}
|
||||
|
||||
// Personas returns an (alphabetically) ordered list of the defined
|
||||
// Persona values.
|
||||
func Personas() []Persona {
|
||||
return []Persona{
|
||||
PersonaAlice,
|
||||
PersonaBob,
|
||||
PersonaCarol,
|
||||
PersonaDan,
|
||||
PersonaErin,
|
||||
PersonaFrank,
|
||||
}
|
||||
}
|
||||
|
||||
// DidToName retrieve the persona's name from its DID.
|
||||
func DidToName(d did.DID) string {
|
||||
return map[did.DID]string{
|
||||
PersonaAlice.DID(): "Alice",
|
||||
PersonaBob.DID(): "Bob",
|
||||
PersonaCarol.DID(): "Carol",
|
||||
PersonaDan.DID(): "Dan",
|
||||
PersonaErin.DID(): "Erin",
|
||||
PersonaFrank.DID(): "Frank",
|
||||
}[d]
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
_ "embed"
|
||||
"testing"
|
||||
|
||||
"github.com/MetaMask/go-did-it/didtest"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/policytest"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation/delegationtest"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/didtest"
|
||||
"github.com/ucan-wg/go-ucan/token/invocation"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ Please note that UCAN in itself doesn't enforce any protocol, topology or transp
|
||||
|
||||
Your situation may be different from this, and would call for a different setup.
|
||||
|
||||
Remember that everything in `go-ucan-toolkit` is essentially helpers, pre-made building blocks. You can use them, change them or make your own.
|
||||
Remember that everything in `/toolkit` is essentially helpers, pre-made building blocks. You can use them, change them or make your own.
|
||||
|
||||
## Scenario 1
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
|
||||
"github.com/INFURA/go-ucan-toolkit/issuer"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/issuer"
|
||||
)
|
||||
|
||||
func RequestResolver(r *http.Request) (*issuer.ResolvedRequest, error) {
|
||||
|
||||
@@ -8,12 +8,13 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
|
||||
"github.com/INFURA/go-ucan-toolkit/client"
|
||||
"github.com/INFURA/go-ucan-toolkit/issuer"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/client"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/issuer"
|
||||
)
|
||||
|
||||
var _ client.DelegationRequester = &Requester{}
|
||||
@@ -55,5 +56,5 @@ func (r Requester) RequestDelegation(ctx context.Context, audience did.DID, cmd
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return issuer.DecodeResponse(res)
|
||||
return issuer.DecodeResponse(res, audience, cmd, subject)
|
||||
}
|
||||
|
||||
@@ -13,19 +13,20 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/container"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
|
||||
example "github.com/INFURA/go-ucan-toolkit/_example"
|
||||
protocol "github.com/INFURA/go-ucan-toolkit/_example/_protocol-issuer"
|
||||
"github.com/INFURA/go-ucan-toolkit/client"
|
||||
"github.com/INFURA/go-ucan-toolkit/issuer"
|
||||
"github.com/INFURA/go-ucan-toolkit/server/bearer"
|
||||
example "github.com/ucan-wg/go-ucan/toolkit/_example"
|
||||
protocol "github.com/ucan-wg/go-ucan/toolkit/_example/_protocol-issuer"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/client"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/issuer"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/server/bearer"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -49,7 +50,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func run(ctx context.Context, ownIssuerUrl string, priv crypto.PrivKey, d did.DID,
|
||||
func run(ctx context.Context, ownIssuerUrl string, priv crypto.PrivateKeySigningBytes, d did.DID,
|
||||
serviceIssuerUrl string, serviceUrl string, serviceDid did.DID) error {
|
||||
log.Printf("Alice DID is %s", d.String())
|
||||
|
||||
@@ -67,7 +68,7 @@ func run(ctx context.Context, ownIssuerUrl string, priv crypto.PrivKey, d did.DI
|
||||
return delegation.New(iss, aud, cmd, policies, subject)
|
||||
}
|
||||
|
||||
cli, err := client.NewWithIssuer(priv, protocol.NewRequester("http://"+serviceIssuerUrl), issuingLogic)
|
||||
cli, err := client.NewWithIssuer(priv, d, protocol.NewRequester("http://"+serviceIssuerUrl), issuingLogic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -12,14 +12,17 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
didkeyctl "github.com/MetaMask/go-did-it/controller/did-key"
|
||||
"github.com/MetaMask/go-did-it/crypto/ed25519"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/container"
|
||||
|
||||
example "github.com/INFURA/go-ucan-toolkit/_example"
|
||||
protocol "github.com/INFURA/go-ucan-toolkit/_example/_protocol-issuer"
|
||||
"github.com/INFURA/go-ucan-toolkit/client"
|
||||
"github.com/INFURA/go-ucan-toolkit/server/bearer"
|
||||
example "github.com/ucan-wg/go-ucan/toolkit/_example"
|
||||
protocol "github.com/ucan-wg/go-ucan/toolkit/_example/_protocol-issuer"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/client"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/server/bearer"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -43,14 +46,15 @@ func main() {
|
||||
|
||||
func run(ctx context.Context, aliceUrl string, aliceDid did.DID, serverUrl string, serviceDid did.DID) error {
|
||||
// Let's generate a keypair for our client:
|
||||
priv, d, err := did.GenerateEd25519()
|
||||
pub, priv, err := ed25519.GenerateKeyPair()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d := didkeyctl.FromPublicKey(pub)
|
||||
|
||||
log.Printf("Bob DID is %s", d.String())
|
||||
|
||||
cli, err := client.NewClient(priv, protocol.NewRequester("http://"+aliceUrl))
|
||||
cli, err := client.NewClient(priv, d, protocol.NewRequester("http://"+aliceUrl))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -69,13 +73,13 @@ func run(ctx context.Context, aliceUrl string, aliceDid did.DID, serverUrl strin
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-time.After(5 * time.Second):
|
||||
case <-time.After(1 * time.Second):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeRequest(ctx context.Context, clientDid did.DID, serviceUrl string, aliceDid did.DID, proofs container.Writer) error {
|
||||
// we construct a URL that include the our DID and Alice DID as path, as requested by the UCAN policy we get issued
|
||||
// we construct a URL that include our DID and Alice DID as path, as requested by the UCAN policy we get issued
|
||||
u, err := url.JoinPath("http://"+serviceUrl, aliceDid.String(), clientDid.String())
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -11,16 +11,17 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
|
||||
"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/delegation"
|
||||
|
||||
example "github.com/INFURA/go-ucan-toolkit/_example"
|
||||
protocol "github.com/INFURA/go-ucan-toolkit/_example/_protocol-issuer"
|
||||
"github.com/INFURA/go-ucan-toolkit/issuer"
|
||||
example "github.com/ucan-wg/go-ucan/toolkit/_example"
|
||||
protocol "github.com/ucan-wg/go-ucan/toolkit/_example/_protocol-issuer"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/issuer"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -42,7 +43,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func run(ctx context.Context, issuerUrl string, servicePrivKey crypto.PrivKey) error {
|
||||
func run(ctx context.Context, issuerUrl string, servicePrivKey crypto.PrivateKeySigningBytes) error {
|
||||
issuingLogic := func(iss did.DID, aud did.DID, cmd command.Command) (*delegation.Token, error) {
|
||||
log.Printf("issuing delegation to %v for %v", aud, cmd)
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
|
||||
example "github.com/INFURA/go-ucan-toolkit/_example"
|
||||
"github.com/INFURA/go-ucan-toolkit/server/exectx"
|
||||
example "github.com/ucan-wg/go-ucan/toolkit/_example"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/server/exectx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -3,8 +3,10 @@ package example
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
didkeyctl "github.com/MetaMask/go-did-it/controller/did-key"
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
"github.com/MetaMask/go-did-it/crypto/ed25519"
|
||||
)
|
||||
|
||||
// Endpoints
|
||||
@@ -16,20 +18,20 @@ var AliceIssuerUrl = ":8082"
|
||||
|
||||
// Service
|
||||
|
||||
var ServicePrivKey crypto.PrivKey
|
||||
var ServicePrivKey crypto.PrivateKeySigningBytes
|
||||
var ServiceDid did.DID
|
||||
|
||||
// Alice
|
||||
|
||||
var AlicePrivKey crypto.PrivKey
|
||||
var AlicePrivKey crypto.PrivateKeySigningBytes
|
||||
var AliceDid did.DID
|
||||
|
||||
func init() {
|
||||
servPrivRaw, _ := base64.StdEncoding.DecodeString("CAESQGs7hPBRBmxH1UmHrdcPrBkecuFUuCWHK0kMJvZYCBqIa35SGxUdXVGuigQDkMpf7xO4C2C2Acl8QTtSrYS7Cnc=")
|
||||
ServicePrivKey, _ = crypto.UnmarshalPrivateKey(servPrivRaw)
|
||||
ServiceDid, _ = did.FromPrivKey(ServicePrivKey)
|
||||
servPrivRaw, _ := base64.StdEncoding.DecodeString("HVcbgoj30c+7zoQzUgpl7Jc7bkXoyvo9bMX5OHaAohpv036EMxuWXGqmEWhFKHPEuRAaIGSURK8pyUYOAseiiQ==")
|
||||
ServicePrivKey, _ = ed25519.PrivateKeyFromBytes(servPrivRaw)
|
||||
ServiceDid = didkeyctl.FromPrivateKey(ServicePrivKey)
|
||||
|
||||
alicePrivRaw, _ := base64.StdEncoding.DecodeString("CAESQFESA31nDYUhXXwbCNSFvg7M+TOFgyxy0tVX6o+TkJAKqAwDvtGxZeGyUjibGd/op+xOLvzE6BrTIOw62K3yLp8=")
|
||||
AlicePrivKey, _ = crypto.UnmarshalPrivateKey(alicePrivRaw)
|
||||
AliceDid, _ = did.FromPrivKey(AlicePrivKey)
|
||||
alicePrivRaw, _ := base64.StdEncoding.DecodeString("jIIk/4ZBgIzx7fU41AWYRUDjgQmgFTIXxN4WeZAPCjwE04oLfiHgNjwIIZi97a6WwSIL5tFGdkrqDkSmDx95tw==")
|
||||
AlicePrivKey, _ = ed25519.PrivateKeyFromBytes(alicePrivRaw)
|
||||
AliceDid = didkeyctl.FromPrivateKey(AlicePrivKey)
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@ import (
|
||||
"fmt"
|
||||
"iter"
|
||||
|
||||
"github.com/MetaMask/go-did-it"
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
"github.com/ipfs/go-cid"
|
||||
"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/container"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
@@ -17,17 +18,13 @@ import (
|
||||
|
||||
type Client struct {
|
||||
did did.DID
|
||||
privKey crypto.PrivKey
|
||||
privKey crypto.PrivateKeySigningBytes
|
||||
|
||||
pool *Pool
|
||||
requester DelegationRequester
|
||||
}
|
||||
|
||||
func NewClient(privKey crypto.PrivKey, requester DelegationRequester) (*Client, error) {
|
||||
d, err := did.FromPrivKey(privKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func NewClient(privKey crypto.PrivateKeySigningBytes, d did.DID, requester DelegationRequester) (*Client, error) {
|
||||
return &Client{
|
||||
did: d,
|
||||
privKey: privKey,
|
||||
|
||||
@@ -6,8 +6,9 @@ import (
|
||||
"iter"
|
||||
"time"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
"github.com/MetaMask/go-did-it/didtest"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
@@ -21,7 +22,7 @@ func ExampleNewClient() {
|
||||
// requester is an adaptor for a real world issuer, we use a mock in that example
|
||||
requester := &requesterMock{persona: servicePersona}
|
||||
|
||||
client, err := NewClient(clientPersona.PrivKey(), requester)
|
||||
client, err := NewClient(clientPersona.PrivKey(), clientPersona.DID(), requester)
|
||||
handleError(err)
|
||||
|
||||
cont, err := client.PrepareInvoke(
|
||||
|
||||
@@ -5,9 +5,10 @@ import (
|
||||
"fmt"
|
||||
"iter"
|
||||
|
||||
"github.com/MetaMask/go-did-it"
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
"github.com/ipfs/go-cid"
|
||||
"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/token/delegation"
|
||||
)
|
||||
@@ -29,8 +30,8 @@ type WithIssuer struct {
|
||||
logic DlgIssuingLogic
|
||||
}
|
||||
|
||||
func NewWithIssuer(privKey crypto.PrivKey, requester DelegationRequester, logic DlgIssuingLogic) (*WithIssuer, error) {
|
||||
client, err := NewClient(privKey, requester)
|
||||
func NewWithIssuer(privKey crypto.PrivateKeySigningBytes, d did.DID, requester DelegationRequester, logic DlgIssuingLogic) (*WithIssuer, error) {
|
||||
client, err := NewClient(privKey, d, requester)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/MetaMask/go-did-it"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
)
|
||||
|
||||
@@ -4,8 +4,9 @@ import (
|
||||
"iter"
|
||||
"math"
|
||||
|
||||
"github.com/MetaMask/go-did-it"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
)
|
||||
@@ -23,12 +24,12 @@ func FindProof(dlgs func() iter.Seq[*delegation.Bundle], issuer did.DID, cmd com
|
||||
// TODO: maybe that should be part of delegation.Token directly?
|
||||
dlgMatch := func(dlg *delegation.Token, issuer did.DID, cmd command.Command, subject did.DID) bool {
|
||||
// The Subject of each delegation must equal the invocation's Subject (or Audience if defined). - 4f
|
||||
if dlg.Subject() != subject {
|
||||
if !dlg.Subject().Equal(subject) {
|
||||
return false
|
||||
}
|
||||
// The first proof must be issued to the Invoker (audience DID). - 4c
|
||||
// The Issuer of each delegation must be the Audience in the next one. - 4d
|
||||
if dlg.Audience() != issuer {
|
||||
if !dlg.Audience().Equal(issuer) {
|
||||
return false
|
||||
}
|
||||
// The command of each delegation must "allow" the one before it. - 4g
|
||||
@@ -72,7 +73,7 @@ func FindProof(dlgs func() iter.Seq[*delegation.Bundle], issuer did.DID, cmd com
|
||||
at := cur.bundle
|
||||
|
||||
// if it's a root delegation, we found a valid proof
|
||||
if at.Decoded.Issuer() == at.Decoded.Subject() {
|
||||
if at.Decoded.Issuer().Equal(at.Decoded.Subject()) {
|
||||
if len(bestProof) == 0 || len(cur.path) < len(bestProof) || len(cur.path) == len(bestProof) && cur.size < bestSize {
|
||||
bestProof = append([]cid.Cid{}, cur.path...) // make a copy
|
||||
bestSize = cur.size
|
||||
|
||||
@@ -4,8 +4,9 @@ import (
|
||||
"iter"
|
||||
"testing"
|
||||
|
||||
"github.com/MetaMask/go-did-it/didtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation/delegationtest"
|
||||
|
||||
@@ -5,8 +5,9 @@ import (
|
||||
"iter"
|
||||
"time"
|
||||
|
||||
"github.com/MetaMask/go-did-it"
|
||||
"github.com/avast/retry-go/v4"
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
)
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"iter"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
)
|
||||
|
||||
var _ DelegationRequester = &InfuraRequester{}
|
||||
|
||||
type InfuraRequester struct {
|
||||
baseURL string
|
||||
}
|
||||
|
||||
// NewInfuraRequester create a requester client for the Infura UCAN token issuer.
|
||||
// dev: http://ucan-issuer-api.commercial-dev.eks-dev.infura.org
|
||||
// prod: http://ucan-issuer-api.commercial-prod.eks.infura.org
|
||||
func NewInfuraRequester(baseURL string) *InfuraRequester {
|
||||
return &InfuraRequester{baseURL: baseURL}
|
||||
}
|
||||
|
||||
func (i InfuraRequester) RequestDelegation(ctx context.Context, audience did.DID, cmd command.Command, subject did.DID) (iter.Seq2[*delegation.Bundle, error], error) {
|
||||
p, err := url.JoinPath(i.baseURL, "v1/token/generate-with-did")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payload := struct {
|
||||
Cmd string `json:"cmd"`
|
||||
Aud string `json:"aud"`
|
||||
}{
|
||||
Cmd: cmd.String(),
|
||||
Aud: audience.String(),
|
||||
}
|
||||
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := http.NewRequest(http.MethodPost, p, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := http.DefaultClient.Do(r.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
msg, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed with status %d, then failed to read response body: %w", res.StatusCode, err)
|
||||
}
|
||||
return nil, fmt.Errorf("request failed with status %d: %s", res.StatusCode, msg)
|
||||
}
|
||||
|
||||
resp := struct {
|
||||
Cid string `json:"cid"`
|
||||
Content string `json:"content"`
|
||||
}{}
|
||||
if err := json.NewDecoder(res.Body).Decode(&resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
raw, err := base64.StdEncoding.DecodeString(resp.Content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tkn, c, err := delegation.FromSealed(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// For sanity, we verify that the delegation we got matches the expected subject,
|
||||
// meaning that we are talking to the expected issuer.
|
||||
if tkn.Subject() != subject {
|
||||
return nil, fmt.Errorf("received token has unexpected subject: expected %s, got %s", subject, tkn.Subject())
|
||||
}
|
||||
|
||||
return func(yield func(*delegation.Bundle, error) bool) {
|
||||
yield(&delegation.Bundle{
|
||||
Cid: c,
|
||||
Decoded: tkn,
|
||||
Sealed: raw,
|
||||
}, nil)
|
||||
}, nil
|
||||
}
|
||||
@@ -6,12 +6,12 @@ import (
|
||||
"iter"
|
||||
"net/http"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/container"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
|
||||
"github.com/INFURA/go-ucan-toolkit/client"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/client"
|
||||
)
|
||||
|
||||
type RequestResolver func(r *http.Request) (*ResolvedRequest, error)
|
||||
@@ -60,7 +60,7 @@ func HttpWrapper(requester client.DelegationRequester, resolver RequestResolver)
|
||||
})
|
||||
}
|
||||
|
||||
func DecodeResponse(res *http.Response) (iter.Seq2[*delegation.Bundle, error], error) {
|
||||
func DecodeResponse(res *http.Response, audience did.DID, cmd command.Command, subject did.DID) (iter.Seq2[*delegation.Bundle, error], error) {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
msg, err := io.ReadAll(res.Body)
|
||||
@@ -73,9 +73,20 @@ func DecodeResponse(res *http.Response) (iter.Seq2[*delegation.Bundle, error], e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// the container doesn't guarantee the ordering, so we must order the delegation in a chain
|
||||
proof := client.FindProof(func() iter.Seq[*delegation.Bundle] {
|
||||
return cont.GetAllDelegations()
|
||||
}, audience, cmd, subject)
|
||||
|
||||
return func(yield func(*delegation.Bundle, error) bool) {
|
||||
for bundle := range cont.GetAllDelegations() {
|
||||
if !yield(&bundle, nil) {
|
||||
for _, c := range proof {
|
||||
bndl, err := cont.GetDelegationBundle(c)
|
||||
if err != nil {
|
||||
yield(nil, err)
|
||||
return
|
||||
}
|
||||
if !yield(bndl, nil) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@ import (
|
||||
"fmt"
|
||||
"iter"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
didkeyctl "github.com/MetaMask/go-did-it/controller/did-key"
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
|
||||
"github.com/INFURA/go-ucan-toolkit/client"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/client"
|
||||
)
|
||||
|
||||
// RootIssuingLogic is a function that decides what powers are given to a client.
|
||||
@@ -29,16 +30,13 @@ var _ client.DelegationRequester = &RootIssuer{}
|
||||
// Feel free to replace this component with your own flavor.
|
||||
type RootIssuer struct {
|
||||
did did.DID
|
||||
privKey crypto.PrivKey
|
||||
privKey crypto.PrivateKeySigningBytes
|
||||
|
||||
logic RootIssuingLogic
|
||||
}
|
||||
|
||||
func NewRootIssuer(privKey crypto.PrivKey, logic RootIssuingLogic) (*RootIssuer, error) {
|
||||
d, err := did.FromPrivKey(privKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func NewRootIssuer(privKey crypto.PrivateKeySigningBytes, logic RootIssuingLogic) (*RootIssuer, error) {
|
||||
d := didkeyctl.FromPrivateKey(privKey)
|
||||
return &RootIssuer{
|
||||
did: d,
|
||||
privKey: privKey,
|
||||
@@ -47,7 +45,7 @@ func NewRootIssuer(privKey crypto.PrivKey, logic RootIssuingLogic) (*RootIssuer,
|
||||
}
|
||||
|
||||
func (r *RootIssuer) RequestDelegation(ctx context.Context, audience did.DID, cmd command.Command, subject did.DID) (iter.Seq2[*delegation.Bundle, error], error) {
|
||||
if subject != r.did {
|
||||
if !subject.Equal(r.did) {
|
||||
return nil, fmt.Errorf("subject DID doesn't match the issuer DID")
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
_ "github.com/MetaMask/go-did-it/verifiers/did-key"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/container"
|
||||
"github.com/ucan-wg/go-ucan/pkg/container/containertest"
|
||||
)
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
|
||||
"github.com/INFURA/go-ucan-toolkit/server/bearer"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/server/bearer"
|
||||
)
|
||||
|
||||
// ExtractMW returns an HTTP middleware tasked with:
|
||||
@@ -38,7 +38,7 @@ func ExtractMW(next http.Handler, serviceDID did.DID) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
if ucanCtx.Invocation().Subject() != serviceDID {
|
||||
if !ucanCtx.Invocation().Subject().Equal(serviceDID) {
|
||||
http.Error(w, "UCAN delegation doesn't match the service DID", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,9 +6,10 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/MetaMask/go-did-it/didtest"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/container"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
"github.com/ucan-wg/go-ucan/token/invocation"
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"net/http"
|
||||
"slices"
|
||||
|
||||
"github.com/INFURA/go-ethlibs/jsonrpc"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipld/go-ipld-prime/datamodel"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/args"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/container"
|
||||
@@ -16,8 +16,7 @@ import (
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
"github.com/ucan-wg/go-ucan/token/invocation"
|
||||
|
||||
"github.com/INFURA/go-ucan-toolkit/server/extargs"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/server/extargs"
|
||||
)
|
||||
|
||||
var _ delegation.Loader = &UcanCtx{}
|
||||
@@ -33,9 +32,8 @@ type UcanCtx struct {
|
||||
meta *meta.Meta // all meta combined, with no overwriting
|
||||
|
||||
// argument sources
|
||||
http *extargs.HttpExtArgs
|
||||
jsonrpc *extargs.JsonRpcExtArgs
|
||||
infura *extargs.InfuraExtArgs
|
||||
http *extargs.HttpExtArgs
|
||||
custom map[string]*extargs.CustomExtArgs
|
||||
}
|
||||
|
||||
// FromContainer prepare a UcanCtx from a UCAN container, for further evaluation in a server pipeline.
|
||||
@@ -79,7 +77,7 @@ func FromContainer(cont container.Reader) (*UcanCtx, error) {
|
||||
chainTo := inv.Issuer()
|
||||
for _, c := range inv.Proof() {
|
||||
dlg := ctx.dlgs[c]
|
||||
if dlg.Audience() != chainTo {
|
||||
if !dlg.Audience().Equal(chainTo) {
|
||||
return nil, fmt.Errorf("proof chain is broken or not ordered correctly")
|
||||
}
|
||||
chainTo = dlg.Issuer()
|
||||
@@ -137,28 +135,19 @@ func (ctn *UcanCtx) VerifyHttp(req *http.Request) error {
|
||||
return ctn.http.Verify()
|
||||
}
|
||||
|
||||
// VerifyJsonRpc verify the delegation's policies against arguments constructed from the JsonRpc request.
|
||||
// These arguments will be set in the `.jsonrpc` argument key, at the root.
|
||||
// This function can only be called once per context.
|
||||
// VerifyCustom verify the delegation's policies against arbitrary arguments provider through an IPLD MapAssembler.
|
||||
// These arguments will be set under the given argument key, at the root.
|
||||
// This function can only be called once per context and key.
|
||||
// After being used, those constructed arguments will be used in ExecutionAllowed as well.
|
||||
func (ctn *UcanCtx) VerifyJsonRpc(req *jsonrpc.Request) error {
|
||||
if ctn.jsonrpc != nil {
|
||||
panic("only use once per request context")
|
||||
func (ctn *UcanCtx) VerifyCustom(key string, assembler func(ma datamodel.MapAssembler)) error {
|
||||
if ctn.custom == nil {
|
||||
ctn.custom = make(map[string]*extargs.CustomExtArgs)
|
||||
}
|
||||
ctn.jsonrpc = extargs.NewJsonRpcExtArgs(ctn.policies, ctn.inv.Arguments(), req)
|
||||
return ctn.jsonrpc.Verify()
|
||||
}
|
||||
|
||||
// VerifyInfura verify the delegation's policies against arbitrary arguments provider through an IPLD MapAssembler.
|
||||
// These arguments will be set in the `.inf` argument key, at the root.
|
||||
// This function can only be called once per context.
|
||||
// After being used, those constructed arguments will be used in ExecutionAllowed as well.
|
||||
func (ctn *UcanCtx) VerifyInfura(assembler func(ma datamodel.MapAssembler)) error {
|
||||
if ctn.infura != nil {
|
||||
panic("only use once per request context")
|
||||
if _, ok := ctn.custom[key]; ok {
|
||||
panic("only use once per request context and key")
|
||||
}
|
||||
ctn.infura = extargs.NewInfuraExtArgs(ctn.policies, assembler)
|
||||
return ctn.infura.Verify()
|
||||
ctn.custom[key] = extargs.NewCustomExtArgs(key, ctn.policies, assembler)
|
||||
return ctn.custom[key].Verify()
|
||||
}
|
||||
|
||||
// ExecutionAllowed does the final verification of the invocation.
|
||||
@@ -174,19 +163,14 @@ func (ctn *UcanCtx) ExecutionAllowed() error {
|
||||
}
|
||||
newArgs.Include(httpArgs)
|
||||
}
|
||||
if ctn.jsonrpc != nil {
|
||||
jsonRpcArgs, err := ctn.jsonrpc.Args()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if ctn.custom != nil {
|
||||
for _, cea := range ctn.custom {
|
||||
customArgs, err := cea.Args()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newArgs.Include(customArgs)
|
||||
}
|
||||
newArgs.Include(jsonRpcArgs)
|
||||
}
|
||||
if ctn.infura != nil {
|
||||
infuraArgs, err := ctn.infura.Args()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newArgs.Include(infuraArgs)
|
||||
}
|
||||
|
||||
return newArgs, nil
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
package exectx_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/INFURA/go-ethlibs/jsonrpc"
|
||||
"github.com/MetaMask/go-did-it/didtest"
|
||||
"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/ucan-wg/go-ucan/did/didtest"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/container"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
"github.com/ucan-wg/go-ucan/token/invocation"
|
||||
|
||||
"github.com/INFURA/go-ucan-toolkit/server/exectx"
|
||||
"github.com/ucan-wg/go-ucan/toolkit/server/exectx"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -45,16 +42,11 @@ func TestUcanCtxFullFlow(t *testing.T) {
|
||||
// some basic HTTP constraints
|
||||
policy.Equal(".http.method", literal.String("GET")),
|
||||
policy.Like(".http.path", "/foo/*"),
|
||||
// some JsonRpc constraints
|
||||
policy.Or(
|
||||
policy.Like(".jsonrpc.method", "eth_*"),
|
||||
policy.Equal(".jsonrpc.method", literal.String("debug_traceCall")),
|
||||
),
|
||||
// some infura constraints
|
||||
// some custom constraints
|
||||
// Network
|
||||
policy.Equal(".inf.ntwk", literal.String(network)),
|
||||
policy.Equal(".custom.ntwk", literal.String(network)),
|
||||
// Quota
|
||||
policy.LessThanOrEqual(".inf.quota.ur", literal.Int(1234)),
|
||||
policy.LessThanOrEqual(".custom.quota.ur", literal.Int(1234)),
|
||||
)
|
||||
|
||||
dlg, err := delegation.Root(service.DID(), user.DID(), cmd, pol,
|
||||
@@ -84,12 +76,7 @@ func TestUcanCtxFullFlow(t *testing.T) {
|
||||
|
||||
// MAKING A REQUEST: we pass the container in the Bearer HTTP header
|
||||
|
||||
jrpc := jsonrpc.NewRequest()
|
||||
jrpc.Method = "eth_call"
|
||||
jrpc.Params = jsonrpc.MustParams("0x599784", true)
|
||||
jrpcBytes, err := jrpc.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
req, err := http.NewRequest(http.MethodGet, "/foo/bar", bytes.NewReader(jrpcBytes))
|
||||
req, err := http.NewRequest(http.MethodGet, "/foo/bar", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer "+string(contBytes))
|
||||
|
||||
@@ -132,33 +119,13 @@ func TestUcanCtxFullFlow(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// SERVER: JsonRpc checks
|
||||
// SERVER: custom args checks
|
||||
|
||||
jsonrpcMw := func(next http.Handler) http.Handler {
|
||||
customArgsMw := 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)
|
||||
|
||||
var jrpc jsonrpc.Request
|
||||
err := json.NewDecoder(r.Body).Decode(&jrpc)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ucanCtx.VerifyJsonRpc(&jrpc)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// 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) {
|
||||
err := ucanCtx.VerifyCustom("custom", 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))
|
||||
@@ -183,7 +150,7 @@ func TestUcanCtxFullFlow(t *testing.T) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
sut := authMw(httpMw(jsonrpcMw(infuraMw(http.HandlerFunc(handler)))))
|
||||
sut := authMw(httpMw(customArgsMw(http.HandlerFunc(handler))))
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
sut.ServeHTTP(rec, req)
|
||||
|
||||
@@ -8,31 +8,28 @@ In this package, we cross the chasm of the pure UCAN world into our practical ne
|
||||
|
||||
## Example
|
||||
|
||||
Below is an example of `args` in Dag-Json format, where the values are recomposed server-side from the HTTP request (header and JSONRPC body):
|
||||
Below is an example of `args` in Dag-Json format, where the values are recomposed server-side from the HTTP request:
|
||||
|
||||
```json
|
||||
{
|
||||
"http": {
|
||||
"scheme": "https",
|
||||
"method": "POST",
|
||||
"host": "mainnet.infura.io",
|
||||
"host": "example.com",
|
||||
"path": ""
|
||||
},
|
||||
"jsonrpc": {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_blockbynumber",
|
||||
"params": [],
|
||||
"id": 1
|
||||
"custom": {
|
||||
"foo": "bar"
|
||||
}
|
||||
}
|
||||
```
|
||||
Those `args` can be evaluated against a delegation's policy, for example:
|
||||
```json
|
||||
{
|
||||
"cmd": "/infura/jsonrpc",
|
||||
"cmd": "/foo/bar",
|
||||
"pol": [
|
||||
["==", ".http.host", "mainnet.infura.io"],
|
||||
["like", ".jsonrpc.method", "eth_*"]
|
||||
["==", ".http.host", "example.com"],
|
||||
["like", ".custom.foo", "ba*"]
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -50,7 +47,7 @@ There is a way to get around that, and have the best of both worlds, but **it co
|
||||
```json
|
||||
{
|
||||
"http": "zQmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D",
|
||||
"jsonrpc": "zQmP8jTG1m9GSDJLCbeWhVSVgEzCPPwXRdCRuJtQ5Tz9Kc9"
|
||||
"custom": "zQmP8jTG1m9GSDJLCbeWhVSVgEzCPPwXRdCRuJtQ5Tz9Kc9"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -63,4 +60,4 @@ Therefore, the server-side logic is made to have this hashing optional:
|
||||
- the client can opt out of passing that hash, and won't benefit from the enforced security
|
||||
|
||||
The particular hash selected is SHA2-256 of the DAG-CBOR encoded argument, expressed in the form of a Multihash in raw bytes.
|
||||
The arguments being hashed are the complete map of values, including the root key being replaced (for example `jsonrpc` or `http`).
|
||||
The arguments being hashed are the complete map of values, including the root key being replaced (for example `http` or `custom` here).
|
||||
@@ -8,13 +8,13 @@ import (
|
||||
"github.com/ipld/go-ipld-prime/datamodel"
|
||||
"github.com/ipld/go-ipld-prime/fluent/qp"
|
||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/args"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
)
|
||||
|
||||
const InfuraArgsKey = "inf"
|
||||
|
||||
type InfuraExtArgs struct {
|
||||
type CustomExtArgs struct {
|
||||
key string
|
||||
pol policy.Policy
|
||||
originalArgs args.ReadOnly
|
||||
assembler func(ma datamodel.MapAssembler)
|
||||
@@ -24,44 +24,44 @@ type InfuraExtArgs struct {
|
||||
argsIpld ipld.Node
|
||||
}
|
||||
|
||||
func NewInfuraExtArgs(pol policy.Policy, assembler func(ma datamodel.MapAssembler)) *InfuraExtArgs {
|
||||
return &InfuraExtArgs{pol: pol, assembler: assembler}
|
||||
func NewCustomExtArgs(key string, pol policy.Policy, assembler func(ma datamodel.MapAssembler)) *CustomExtArgs {
|
||||
return &CustomExtArgs{key: key, pol: pol, assembler: assembler}
|
||||
}
|
||||
|
||||
func (ia *InfuraExtArgs) Verify() error {
|
||||
if err := ia.makeArgs(); err != nil {
|
||||
func (cea *CustomExtArgs) Verify() error {
|
||||
if err := cea.makeArgs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Note: InfuraExtArgs doesn't support verifying a hash computed client-side like the other
|
||||
// Note: CustomExtArgs doesn't support verifying a hash computed client-side like the other
|
||||
// external args, as the arguments are by nature dynamic. The client can't generate a meaningful hash.
|
||||
|
||||
ok, leaf := ia.pol.PartialMatch(ia.argsIpld)
|
||||
ok, leaf := cea.pol.PartialMatch(cea.argsIpld)
|
||||
if !ok {
|
||||
return fmt.Errorf("the following UCAN policy is not satisfied: %v", leaf.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ia *InfuraExtArgs) Args() (*args.Args, error) {
|
||||
if err := ia.makeArgs(); err != nil {
|
||||
func (cea *CustomExtArgs) Args() (*args.Args, error) {
|
||||
if err := cea.makeArgs(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ia.args, nil
|
||||
return cea.args, nil
|
||||
}
|
||||
|
||||
func (ia *InfuraExtArgs) makeArgs() error {
|
||||
func (cea *CustomExtArgs) makeArgs() error {
|
||||
var outerErr error
|
||||
ia.once.Do(func() {
|
||||
cea.once.Do(func() {
|
||||
var err error
|
||||
|
||||
ia.args, err = makeInfuraArgs(ia.assembler)
|
||||
cea.args, err = makeCustomArgs(cea.key, cea.assembler)
|
||||
if err != nil {
|
||||
outerErr = err
|
||||
return
|
||||
}
|
||||
|
||||
ia.argsIpld, err = ia.args.ToIPLD()
|
||||
cea.argsIpld, err = cea.args.ToIPLD()
|
||||
if err != nil {
|
||||
outerErr = err
|
||||
return
|
||||
@@ -70,14 +70,14 @@ func (ia *InfuraExtArgs) makeArgs() error {
|
||||
return outerErr
|
||||
}
|
||||
|
||||
func makeInfuraArgs(assembler func(ma datamodel.MapAssembler)) (*args.Args, error) {
|
||||
func makeCustomArgs(key string, assembler func(ma datamodel.MapAssembler)) (*args.Args, error) {
|
||||
n, err := qp.BuildMap(basicnode.Prototype.Any, -1, assembler)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := args.New()
|
||||
err = res.Add(InfuraArgsKey, n)
|
||||
err = res.Add(key, n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -7,18 +7,19 @@ import (
|
||||
"github.com/ipld/go-ipld-prime/datamodel"
|
||||
"github.com/ipld/go-ipld-prime/fluent/qp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||
)
|
||||
|
||||
func ExampleInfuraExtArgs() {
|
||||
// Note: this is an example for how to build arguments, but you likely want to use InfuraExtArgs
|
||||
func ExampleCustomExtArgs() {
|
||||
// Note: this is an example for how to build arguments, but you likely want to use CustomExtArgs
|
||||
// through UcanCtx.
|
||||
|
||||
pol := policy.Policy{} // policies from the delegations
|
||||
|
||||
// We will construct the following args:
|
||||
// {
|
||||
// "key": {
|
||||
// "ntwk":"eth-mainnet",
|
||||
// "quota":{
|
||||
// "ur":1234,
|
||||
@@ -29,7 +30,7 @@ func ExampleInfuraExtArgs() {
|
||||
// "up":1234
|
||||
// }
|
||||
// }
|
||||
infArgs := NewInfuraExtArgs(pol, func(ma datamodel.MapAssembler) {
|
||||
customArgs := NewCustomExtArgs("key", pol, func(ma datamodel.MapAssembler) {
|
||||
qp.MapEntry(ma, "ntwk", qp.String("eth-mainnet"))
|
||||
qp.MapEntry(ma, "quota", qp.Map(6, func(ma datamodel.MapAssembler) {
|
||||
qp.MapEntry(ma, "ur", qp.Int(1234))
|
||||
@@ -41,14 +42,14 @@ func ExampleInfuraExtArgs() {
|
||||
}))
|
||||
})
|
||||
|
||||
err := infArgs.Verify()
|
||||
err := customArgs.Verify()
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
}
|
||||
|
||||
func TestInfura(t *testing.T) {
|
||||
func TestCustom(t *testing.T) {
|
||||
assembler := func(ma datamodel.MapAssembler) {
|
||||
qp.MapEntry(ma, "ntwk", qp.String("eth-mainnet"))
|
||||
qp.MapEntry(ma, "quota", qp.Map(6, func(ma datamodel.MapAssembler) {
|
||||
@@ -74,24 +75,24 @@ func TestInfura(t *testing.T) {
|
||||
{
|
||||
name: "matching args",
|
||||
pol: policy.MustConstruct(
|
||||
policy.Equal(".inf.ntwk", literal.String("eth-mainnet")),
|
||||
policy.LessThanOrEqual(".inf.quota.ur", literal.Int(1234)),
|
||||
policy.Equal(".key.ntwk", literal.String("eth-mainnet")),
|
||||
policy.LessThanOrEqual(".key.quota.ur", literal.Int(1234)),
|
||||
),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "wrong network",
|
||||
pol: policy.MustConstruct(
|
||||
policy.Equal(".inf.ntwk", literal.String("avalanche-fuji")),
|
||||
policy.LessThanOrEqual(".inf.quota.ur", literal.Int(1234)),
|
||||
policy.Equal(".key.ntwk", literal.String("avalanche-fuji")),
|
||||
policy.LessThanOrEqual(".key.quota.ur", literal.Int(1234)),
|
||||
),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "unrespected quota",
|
||||
pol: policy.MustConstruct(
|
||||
policy.Equal(".inf.ntwk", literal.String("eth-mainnet")),
|
||||
policy.LessThanOrEqual(".inf.quota.ur", literal.Int(100)),
|
||||
policy.Equal(".key.ntwk", literal.String("eth-mainnet")),
|
||||
policy.LessThanOrEqual(".key.quota.ur", literal.Int(100)),
|
||||
),
|
||||
expected: false,
|
||||
},
|
||||
@@ -99,7 +100,7 @@ func TestInfura(t *testing.T) {
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
extArgs := NewInfuraExtArgs(tc.pol, assembler)
|
||||
extArgs := NewCustomExtArgs("key", tc.pol, assembler)
|
||||
|
||||
_, err := extArgs.Args()
|
||||
require.NoError(t, err)
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/ipld/go-ipld-prime/fluent/qp"
|
||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||
"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"
|
||||
@@ -88,7 +89,7 @@ func (hea *HttpExtArgs) verifyHash() error {
|
||||
|
||||
mhBytes, err := n.AsBytes()
|
||||
if err != nil {
|
||||
return fmt.Errorf("http args hash should be a string")
|
||||
return fmt.Errorf("http args hash should be bytes")
|
||||
}
|
||||
|
||||
data, err := ipld.Encode(hea.argsIpld, dagcbor.Encode)
|
||||
@@ -112,7 +113,7 @@ 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.
|
||||
// For convenience, the hash is returned as a read to use invocation argument.
|
||||
// For convenience, the hash is returned as a ready-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)
|
||||
|
||||
@@ -5,9 +5,10 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/MetaMask/go-did-it/didtest"
|
||||
"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"
|
||||
@@ -200,3 +201,10 @@ func TestHttpHash(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func must[T any](t T, err error) T {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
package extargs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/INFURA/go-ethlibs/jsonrpc"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||
"github.com/ipld/go-ipld-prime/datamodel"
|
||||
"github.com/ipld/go-ipld-prime/fluent/qp"
|
||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||
"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/pkg/policy/literal"
|
||||
"github.com/ucan-wg/go-ucan/token/invocation"
|
||||
)
|
||||
|
||||
// JsonRpcArgsKey is the key in the args, used for:
|
||||
// - if it exists in the invocation, holds a hash of the args derived from the JsonRpc request
|
||||
// - in the final args to be evaluated against the policies, holds the args derived from the JsonRpc request
|
||||
const JsonRpcArgsKey = "jsonrpc"
|
||||
|
||||
type JsonRpcExtArgs struct {
|
||||
pol policy.Policy
|
||||
originalArgs args.ReadOnly
|
||||
req *jsonrpc.Request
|
||||
|
||||
once sync.Once
|
||||
args *args.Args
|
||||
argsIpld ipld.Node
|
||||
}
|
||||
|
||||
func NewJsonRpcExtArgs(pol policy.Policy, originalArgs args.ReadOnly, req *jsonrpc.Request) *JsonRpcExtArgs {
|
||||
return &JsonRpcExtArgs{pol: pol, originalArgs: originalArgs, req: req}
|
||||
}
|
||||
|
||||
func (jrea *JsonRpcExtArgs) Verify() error {
|
||||
if err := jrea.makeArgs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := jrea.verifyHash(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ok, leaf := jrea.pol.PartialMatch(jrea.argsIpld)
|
||||
if !ok {
|
||||
return fmt.Errorf("the following UCAN policy is not satisfied: %v", leaf.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (jrea *JsonRpcExtArgs) Args() (*args.Args, error) {
|
||||
if err := jrea.makeArgs(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return jrea.args, nil
|
||||
}
|
||||
|
||||
func (jrea *JsonRpcExtArgs) makeArgs() error {
|
||||
var outerErr error
|
||||
jrea.once.Do(func() {
|
||||
var err error
|
||||
jrea.args, err = makeJsonRpcArgs(jrea.req)
|
||||
if err != nil {
|
||||
outerErr = err
|
||||
return
|
||||
}
|
||||
|
||||
jrea.argsIpld, err = jrea.args.ToIPLD()
|
||||
if err != nil {
|
||||
outerErr = err
|
||||
return
|
||||
}
|
||||
})
|
||||
return outerErr
|
||||
}
|
||||
|
||||
func (jrea *JsonRpcExtArgs) verifyHash() error {
|
||||
n, err := jrea.originalArgs.GetNode(JsonRpcArgsKey)
|
||||
if err != nil {
|
||||
// no hash found, nothing to verify
|
||||
return nil
|
||||
}
|
||||
|
||||
mhBytes, err := n.AsBytes()
|
||||
if err != nil {
|
||||
return fmt.Errorf("jsonrpc args hash should be a string")
|
||||
}
|
||||
|
||||
data, err := ipld.Encode(jrea.argsIpld, dagcbor.Encode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't encode derived args in dag-cbor: %w", err)
|
||||
}
|
||||
|
||||
sum, err := multihash.Sum(data, multihash.SHA2_256, -1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(mhBytes, sum) {
|
||||
return fmt.Errorf("derived args from jsonrpc request don't match the expected hash")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MakeJsonRpcHash compute the hash of the derived arguments from the JsonRPC request.
|
||||
// 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.
|
||||
// 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
|
||||
}
|
||||
|
||||
n, err := computedArgs.ToIPLD()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ipld.Encode(n, dagcbor.Encode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sum, err := multihash.Sum(data, multihash.SHA2_256, -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return invocation.WithArgument(JsonRpcArgsKey, []byte(sum)), nil
|
||||
}
|
||||
|
||||
func makeJsonRpcArgs(req *jsonrpc.Request) (*args.Args, error) {
|
||||
deserialized := make([]any, len(req.Params))
|
||||
for i, param := range req.Params {
|
||||
err := json.Unmarshal(param, &deserialized[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
params, err := literal.List(deserialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n, err := qp.BuildMap(basicnode.Prototype.Any, 3, func(ma datamodel.MapAssembler) {
|
||||
qp.MapEntry(ma, "jsonrpc", qp.String(req.JSONRPC))
|
||||
qp.MapEntry(ma, "method", qp.String(req.Method))
|
||||
qp.MapEntry(ma, "params", qp.Node(params))
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := args.New()
|
||||
err = res.Add(JsonRpcArgsKey, n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
package extargs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"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) {
|
||||
tests := []struct {
|
||||
name string
|
||||
req *jsonrpc.Request
|
||||
pol policy.Policy
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "or on method, not matching",
|
||||
req: jsonrpc.MustRequest(1839673506133526, "eth_getBlockByNumber",
|
||||
"0x599784", true,
|
||||
),
|
||||
pol: policy.MustConstruct(
|
||||
policy.Or(
|
||||
policy.Equal(".jsonrpc.method", literal.String("eth_getCode")),
|
||||
policy.Equal(".jsonrpc.method", literal.String("eth_getBalance")),
|
||||
policy.Equal(".jsonrpc.method", literal.String("eth_call")),
|
||||
policy.Equal(".jsonrpc.method", literal.String("eth_blockNumber")),
|
||||
),
|
||||
),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "or on method, matching",
|
||||
req: jsonrpc.MustRequest(1839673506133526, "eth_call",
|
||||
map[string]string{"to": "0xBADBADBADBADBADBADBADBADBADBADBADBADBAD1"},
|
||||
),
|
||||
pol: policy.MustConstruct(
|
||||
policy.Or(
|
||||
policy.Equal(".jsonrpc.method", literal.String("eth_getCode")),
|
||||
policy.Equal(".jsonrpc.method", literal.String("eth_getBalance")),
|
||||
policy.Equal(".jsonrpc.method", literal.String("eth_call")),
|
||||
policy.Equal(".jsonrpc.method", literal.String("eth_blockNumber")),
|
||||
),
|
||||
),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "complex, optional parameter, matching",
|
||||
req: jsonrpc.MustRequest(1839673506133526, "debug_traceCall",
|
||||
true, false, 1234, "callTracer",
|
||||
),
|
||||
pol: policy.MustConstruct(
|
||||
policy.Equal(".jsonrpc.method", literal.String("debug_traceCall")),
|
||||
policy.Or(
|
||||
policy.Equal(".jsonrpc.params[3]?", literal.String("callTracer")),
|
||||
policy.Equal(".jsonrpc.params[3]?", literal.String("prestateTracer")),
|
||||
),
|
||||
),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "complex, optional parameter, missing parameter",
|
||||
req: jsonrpc.MustRequest(1839673506133526, "debug_traceCall",
|
||||
true, false, 1234,
|
||||
),
|
||||
pol: policy.MustConstruct(
|
||||
policy.Equal(".jsonrpc.method", literal.String("debug_traceCall")),
|
||||
policy.Or(
|
||||
policy.Equal(".jsonrpc.params[3]?", literal.String("callTracer")),
|
||||
policy.Equal(".jsonrpc.params[3]?", literal.String("prestateTracer")),
|
||||
),
|
||||
),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "complex, parameter not matching",
|
||||
req: jsonrpc.MustRequest(1839673506133526, "debug_traceCall",
|
||||
true, false, 1234, "ho_no",
|
||||
),
|
||||
pol: policy.MustConstruct(
|
||||
policy.Equal(".jsonrpc.method", literal.String("debug_traceCall")),
|
||||
policy.Or(
|
||||
policy.Equal(".jsonrpc.params[3]?", literal.String("callTracer")),
|
||||
policy.Equal(".jsonrpc.params[3]?", literal.String("prestateTracer")),
|
||||
),
|
||||
),
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// we don't test the args hash here
|
||||
emptyArgs := args.New().ReadOnly()
|
||||
|
||||
ctx := NewJsonRpcExtArgs(tc.pol, emptyArgs, tc.req)
|
||||
|
||||
_, err := ctx.Args()
|
||||
require.NoError(t, err)
|
||||
|
||||
if tc.expected {
|
||||
require.NoError(t, ctx.Verify())
|
||||
} else {
|
||||
require.Error(t, ctx.Verify())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJsonRpcHash(t *testing.T) {
|
||||
servicePersona := didtest.PersonaAlice
|
||||
clientPersona := didtest.PersonaBob
|
||||
|
||||
req := jsonrpc.MustRequest(1839673506133526, "debug_traceCall",
|
||||
true, false, 1234, "ho_no",
|
||||
)
|
||||
pol := policy.MustConstruct(
|
||||
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
|
||||
argOptions []invocation.Option
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "correct hash",
|
||||
argOptions: []invocation.Option{must(MakeJsonRpcHash(req))},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "non-matching hash",
|
||||
argOptions: []invocation.Option{makeArg([]byte{1, 2, 3, 4}, multihash.SHA2_256)},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "wrong type of hash",
|
||||
argOptions: []invocation.Option{makeArg([]byte{1, 2, 3, 4}, multihash.BLAKE3)},
|
||||
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) {
|
||||
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, inv.Arguments(), req)
|
||||
|
||||
if tc.expected {
|
||||
require.NoError(t, ctx.Verify())
|
||||
} else {
|
||||
require.Error(t, ctx.Verify())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func must[T any](t T, err error) T {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
Reference in New Issue
Block a user