test(invocation): add command.Covers and subject consistency tests

Also improve the maintainability of the tests by a) providing a set of fixed Personas
and then generating a slew of valid delegation tokens, invalid delegation tokens
and proof-chains thereof.
This commit is contained in:
Steve Moyer
2024-11-19 14:35:46 -05:00
parent bb36d61d93
commit 1098e76cba
32 changed files with 891 additions and 295 deletions

149
did/didtest/crypto.go Normal file
View File

@@ -0,0 +1,149 @@
// 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 (
"sync"
"testing"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/did"
)
const (
alicePrivKeyCfg = "CAESQHdNJLBBiuc1AdwPHBkubB2KS1p0cv2JEF7m8tfwtrcm5ajaYPm+XmVCmtcHOF2lGDlmaiDA7emfwD3IrcyES0M="
bobPrivKeyCfg = "CAESQHBz+AIop1g+9iBDj+ufUc/zm9/ry7c6kDFO8Wl/D0+H63V9hC6s9l4npf3pYEFCjBtlR0AMNWMoFQKSlYNKo20="
carolPrivKeyCfg = "CAESQPrCgkcHnYFXDT9AlAydhPECBEivEuuVx9dJxLjVvDTmJIVNivfzg6H4mAiPfYS+5ryVVUZTHZBzvMuvvvG/Ks0="
danPrivKeyCfg = "CAESQCgNhzofKhC+7hW6x+fNd7iMPtQHeEmKRhhlduf/I7/TeOEFYAEflbJ0sAhMeDJ/HQXaAvsWgHEbJ3ZLhP8q2B0="
erinPrivKeyCfg = "CAESQKhCJo5UBpQcthko8DKMFsbdZ+qqQ5oc01CtLCqrE90dF2GfRlrMmot3WPHiHGCmEYi5ZMEHuiSI095e/6O4Bpw="
frankPrivKeyCfg = "CAESQDlXPKsy3jHh7OWTWQqyZF95Ueac5DKo7xD0NOBE5F2BNr1ZVxRmJ2dBELbOt8KP9sOACcO9qlCB7uMA1UQc7sk="
)
// 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
PersonaBob
PersonaCarol
PersonaDan
PersonaErin
PersonaFrank
)
var (
once sync.Once
privKeys = make(map[Persona]crypto.PrivKey, 6)
err error
)
// DID returns a did.DID based on the Persona's Ed25519 public key.
func (p Persona) DID(t *testing.T) did.DID {
t.Helper()
did, err := did.FromPrivKey(p.PrivKey(t))
require.NoError(t, err)
return did
}
// Name returns the username of the Persona.
func (p Persona) Name(t *testing.T) string {
t.Helper()
name, ok := map[Persona]string{
PersonaAlice: "Alice",
PersonaBob: "Bob",
PersonaCarol: "Carol",
PersonaDan: "Dan",
PersonaErin: "Erin",
PersonaFrank: "Frank",
}[p]
if !ok {
t.Fatal("Unknown persona:", p)
}
return name
}
// PrivKey returns the Ed25519 private key for the Persona.
func (p Persona) PrivKey(t *testing.T) crypto.PrivKey {
t.Helper()
once.Do(func() {
for persona, privKeyCfg := range privKeyCfgs(t) {
privKeyMar, err := crypto.ConfigDecodeKey(privKeyCfg)
if err != nil {
return
}
privKey, err := crypto.UnmarshalPrivateKey(privKeyMar)
if err != nil {
return
}
privKeys[persona] = privKey
}
})
require.NoError(t, err)
return privKeys[p]
}
// PrivKeyConfig returns the marshaled and encoded Ed25519 private key
// for the Persona.
func (p Persona) PrivKeyConfig(t *testing.T) string {
t.Helper()
return privKeyCfgs(t)[p]
}
// PubKey returns the Ed25519 public key for the Persona.
func (p Persona) PubKey(t *testing.T) crypto.PubKey {
t.Helper()
return p.PrivKey(t).GetPublic()
}
// PubKeyConfig returns the marshaled and encoded Ed25519 public key
// for the Persona.
func (p Persona) PubKeyConfig(t *testing.T) string {
pubKeyMar, err := crypto.MarshalPublicKey(p.PrivKey(t).GetPublic())
require.NoError(t, err)
return crypto.ConfigEncodeKey(pubKeyMar)
}
func privKeyCfgs(t *testing.T) map[Persona]string {
t.Helper()
return map[Persona]string{
PersonaAlice: alicePrivKeyCfg,
PersonaBob: bobPrivKeyCfg,
PersonaCarol: carolPrivKeyCfg,
PersonaDan: danPrivKeyCfg,
PersonaErin: erinPrivKeyCfg,
PersonaFrank: frankPrivKeyCfg,
}
}
// Personas returns an (alphabetically) ordered list of the defined
// Persona values.
func Personas(t *testing.T) []Persona {
t.Helper()
return []Persona{
PersonaAlice,
PersonaBob,
PersonaCarol,
PersonaDan,
PersonaErin,
PersonaFrank,
}
}

1
go.mod
View File

@@ -3,6 +3,7 @@ module github.com/ucan-wg/go-ucan
go 1.23 go 1.23
require ( require (
github.com/dave/jennifer v1.7.1
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
github.com/ipfs/go-cid v0.4.1 github.com/ipfs/go-cid v0.4.1
github.com/ipld/go-ipld-prime v0.21.0 github.com/ipld/go-ipld-prime v0.21.0

2
go.sum
View File

@@ -1,5 +1,7 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

View File

@@ -0,0 +1,20 @@
// Package policytest provides values and functions that are useful when
// testing code that relies on Policies.
package policytest
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/pkg/policy"
)
// EmptyPolicy provides a policy with no statements for testing purposes.
func EmptyPolicy(t *testing.T) policy.Policy {
t.Helper()
pol, err := policy.FromDagJson("[]")
require.NoError(t, err)
return pol
}

View File

@@ -0,0 +1,5 @@
# delegationtest
See the package documentation for instructions on how to use the generated
tokens as well as information on how to regenerate the code if changes have
been made.

View File

@@ -0,0 +1,33 @@
// Package delegationtest provides a set of pre-built delegation tokens
// for a variety of test cases.
//
// For all delegation tokens, the name of the delegation token is the
// Issuer appended with the Audience. The tokens are generated so that
// an invocation can be created for any didtest.Persona.
//
// Delegation proof-chain names contain each didtest.Persona name in
// order starting with the root delegation (which will always be generated
// by Alice.) This is opposite of the list of cic.Cids that represent the
// proof chain.
//
// For both the generated delegation tokens granted to Carol's Persona and
// the proof chains containing Carol's delegations to Dan, if there is no
// suffix, the proof chain will be deemed valid. If there is a suffix, it
// will consist of either the word "Valid" or "Invalid" and the name of the
// field that has been altered. Only optional fields will generate proof
// chains with Valid suffixes.
//
// If changes are made to the list of Personas included in the chain, or
// in the variants that are specified, the generated Go file and delegation
// tokens stored in the data/ directory should be regenerated by running
// the following command in this directory:
//
// go test . -update
//
// Generated delegation Tokens are stored in the data/ directory and loaded
// into the DelegationLoader on the first call to GetDelegationLoader.
// Generated references to these tokens and the tokens themselves are
// created in the token_gen.go file. See /token/invocation/invocation_test.go
// for an example of how these delegation tokens and proof-chains can
// be used during testing.
package delegationtest

View File

@@ -0,0 +1,224 @@
package delegationtest
import (
"os"
"path/filepath"
"slices"
"testing"
"time"
"github.com/dave/jennifer/jen"
"github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/did"
"github.com/ucan-wg/go-ucan/did/didtest"
"github.com/ucan-wg/go-ucan/pkg/command"
"github.com/ucan-wg/go-ucan/pkg/policy"
"github.com/ucan-wg/go-ucan/pkg/policy/policytest"
"github.com/ucan-wg/go-ucan/token/delegation"
"gotest.tools/v3/golden"
)
const (
tokenNamePrefix = "Token"
proorChainNamePrefix = "Proof"
)
var constantNonce = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b}
// TestUpdate doesn't actually run a test but uses the Go testing library
// to trigger generation of the delegation tokens and associated Go file.
func TestUpdate(t *testing.T) {
if golden.FlagUpdate() {
update(t)
}
}
type newDelegationParams struct {
privKey crypto.PrivKey
aud did.DID
sub did.DID
cmd command.Command
pol policy.Policy
opts []delegation.Option
}
type newDelegationParamsVariant func(*newDelegationParams)
type token struct {
name string
id cid.Cid
}
type proof struct {
name string
prf []cid.Cid
}
type generator struct {
dlgs []token
chains []proof
}
type acc struct {
name string
chain []cid.Cid
}
type variant struct {
name string
variant func(*newDelegationParams)
}
func noopVariant() variant {
return variant{
name: "",
variant: func(_ *newDelegationParams) {},
}
}
func update(t *testing.T) {
t.Helper()
gen := &generator{}
gen.chainPersonas(t, didtest.Personas(t), acc{}, noopVariant())
gen.writeGoFile(t)
}
func (g *generator) chainPersonas(t *testing.T, personas []didtest.Persona, acc acc, vari variant) {
t.Helper()
acc.name += personas[0].Name(t)
g.createProofChain(t, acc.name+vari.name, acc.chain)
if len(personas) < 2 {
return
}
name := personas[0].Name(t) + personas[1].Name(t)
params := newDelegationParams{
privKey: personas[0].PrivKey(t),
aud: personas[1].DID(t),
cmd: NominalCommand,
pol: policytest.EmptyPolicy(t),
opts: []delegation.Option{
delegation.WithSubject(didtest.PersonaAlice.DID(t)),
delegation.WithNonce(constantNonce),
},
}
// Create each nominal token and continue the chain
id := g.createDelegation(t, params, name, vari)
acc.chain = append(acc.chain, id)
g.chainPersonas(t, personas[1:], acc, vari)
// If the user is Carol, create variants for each invalid and/or optional
// parameter and also continue the chain
if personas[0] == didtest.PersonaCarol {
variants := []variant{
{name: "InvalidExpandedCommand", variant: func(p *newDelegationParams) {
p.cmd = ExpandedCommand
}},
{name: "ValidAttenuatedCommand", variant: func(p *newDelegationParams) {
p.cmd = AttenuatedCommand
}},
{name: "InvalidSubject", variant: func(p *newDelegationParams) {
p.opts = append(p.opts, delegation.WithSubject(didtest.PersonaBob.DID(t)))
}},
{name: "InvalidExpired", variant: func(p *newDelegationParams) {
p.opts = append(p.opts, delegation.WithExpiration(time.Now().Add(time.Second)))
}},
{name: "InvalidInactive", variant: func(p *newDelegationParams) {
nbf, err := time.Parse(time.RFC3339, "2070-01-01T00:00:00Z")
require.NoError(t, err)
p.opts = append(p.opts, delegation.WithNotBefore(nbf))
}},
}
// Start a branch in the recursion for each of the variants
for _, v := range variants {
id := g.createDelegation(t, params, name, v)
// replace the previous Carol token id with the one from the variant
acc.chain[len(acc.chain)-1] = id
g.chainPersonas(t, personas[1:], acc, v)
}
}
}
func (g *generator) createDelegation(t *testing.T, params newDelegationParams, name string, vari variant) cid.Cid {
t.Helper()
vari.variant(&params)
tkn, err := delegation.New(params.privKey, params.aud, params.cmd, params.pol, params.opts...)
require.NoError(t, err)
data, id, err := tkn.ToSealed(params.privKey)
require.NoError(t, err)
require.NoError(t, os.WriteFile(filepath.Join(tokenDir, tokenNamePrefix+name+vari.name+tokenExt), data, 0o644))
g.dlgs = append(g.dlgs, token{
name: tokenNamePrefix + name + vari.name,
id: id,
})
return id
}
func (g *generator) createProofChain(t *testing.T, name string, prf []cid.Cid) {
t.Helper()
if len(prf) < 1 {
return
}
clone := make([]cid.Cid, len(prf))
copy(clone, prf)
g.chains = append(g.chains, proof{
name: proorChainNamePrefix + name,
prf: clone,
})
}
func (g *generator) writeGoFile(t *testing.T) {
t.Helper()
file := jen.NewFile("delegationtest")
file.HeaderComment("Code generated by delegationtest - DO NOT EDIT.")
refs := map[cid.Cid]string{}
for _, d := range g.dlgs {
refs[d.id] = d.name + "CID"
file.Var().Defs(
jen.Id(d.name+"CID").Op("=").Qual("github.com/ipfs/go-cid", "MustParse").Call(jen.Lit(d.id.String())),
jen.Id(d.name).Op("=").Id("mustGetDelegation").Call(jen.Id(d.name+"CID")),
)
file.Line()
}
for _, c := range g.chains {
g := jen.CustomFunc(jen.Options{
Multi: true,
Separator: ",",
Close: "\n",
}, func(g *jen.Group) {
slices.Reverse(c.prf)
for _, p := range c.prf {
g.Id(refs[p])
}
})
file.Var().Id(c.name).Op("=").Index().Qual("github.com/ipfs/go-cid", "Cid").Values(g)
file.Line()
}
require.NoError(t, file.Save("token_gen.go"))
}

View File

@@ -0,0 +1,120 @@
package delegationtest
import (
"embed"
"fmt"
"path/filepath"
"sync"
"github.com/ipfs/go-cid"
"github.com/ucan-wg/go-ucan/pkg/command"
"github.com/ucan-wg/go-ucan/token/delegation"
"github.com/ucan-wg/go-ucan/token/invocation"
)
const (
tokenDir = "data"
tokenExt = ".dagcbor"
)
var (
// ExpandedCommand is the parent of the NominalCommand and represents
// the cases where the delegation proof-chain or invocation token tries
// to increase the privileges granted by the root delegation token.
// Execution of this command is generally prohibited in tests.
ExpandedCommand = command.MustParse("/expanded")
// NominalCommand is the command used for most test tokens and proof-
// chains. Execution of this command is generally allowed in tests.
NominalCommand = ExpandedCommand.Join("nominal")
// AttenuatedCommand is a sub-command of the NominalCommand. Execution
// of this command is generally allowed in tests.
AttenuatedCommand = NominalCommand.Join("attenuated")
)
// ProofEmpty provides an empty proof chain for testing purposes.
var ProofEmpty = []cid.Cid{}
//go:embed data
var fs embed.FS
var (
once sync.Once
ldr invocation.DelegationLoader
err error
)
var _ invocation.DelegationLoader = (*delegationLoader)(nil)
type delegationLoader struct {
tokens map[cid.Cid]*delegation.Token
}
// GetDelegationLoader returns a singleton instance of a test
// DelegationLoader containing all the tokens present in the data/
// directory.
func GetDelegationLoader() (invocation.DelegationLoader, error) {
once.Do(func() {
ldr, err = loadDelegations()
})
return ldr, err
}
// GetDelegation implements invocation.DelegationLoader.
func (l *delegationLoader) GetDelegation(id cid.Cid) (*delegation.Token, error) {
tkn, ok := l.tokens[id]
if !ok {
return nil, fmt.Errorf("%w: CID %s", invocation.ErrMissingDelegation, id.String())
}
return tkn, nil
}
func loadDelegations() (invocation.DelegationLoader, error) {
dirEntries, err := fs.ReadDir("data")
if err != nil {
return nil, err
}
tkns := make(map[cid.Cid]*delegation.Token, len(dirEntries))
for _, dirEntry := range dirEntries {
data, err := fs.ReadFile(filepath.Join(tokenDir, dirEntry.Name()))
if err != nil {
return nil, err
}
tkn, id, err := delegation.FromSealed(data)
if err != nil {
return nil, err
}
tkns[id] = tkn
}
return &delegationLoader{
tokens: tkns,
}, nil
}
// GetDelegation is a shortcut that gets (or creates) the DelegationLoader
// and attempts to return the token referenced by the provided CID.
func GetDelegation(id cid.Cid) (*delegation.Token, error) {
ldr, err := GetDelegationLoader()
if err != nil {
return nil, err
}
return ldr.GetDelegation(id)
}
func mustGetDelegation(id cid.Cid) *delegation.Token {
tkn, err := GetDelegation(id)
if err != nil {
panic(err)
}
return tkn
}

View File

@@ -0,0 +1,240 @@
// Code generated by delegationtest - DO NOT EDIT.
package delegationtest
import gocid "github.com/ipfs/go-cid"
var (
TokenAliceBobCID = gocid.MustParse("bafyreicidrwvmac5lvjypucgityrtjsknojraio7ujjli4r5eyby66wjzm")
TokenAliceBob = mustGetDelegation(TokenAliceBobCID)
)
var (
TokenBobCarolCID = gocid.MustParse("bafyreihxv2uhq43oxllzs2xfvxst7wtvvvl7pohb2chcz6hjvfv2ntea5u")
TokenBobCarol = mustGetDelegation(TokenBobCarolCID)
)
var (
TokenCarolDanCID = gocid.MustParse("bafyreihclsgiroazq3heqdswvj2cafwqbpboicq7immo65scl7ahktpsdq")
TokenCarolDan = mustGetDelegation(TokenCarolDanCID)
)
var (
TokenDanErinCID = gocid.MustParse("bafyreicja6ihewy64p3ake56xukotafjlkh4uqep2qhj52en46zzfwby3e")
TokenDanErin = mustGetDelegation(TokenDanErinCID)
)
var (
TokenErinFrankCID = gocid.MustParse("bafyreicjlx3lobxm6hl5s4htd4ydwkkqeiou6rft4rnvulfdyoew565vka")
TokenErinFrank = mustGetDelegation(TokenErinFrankCID)
)
var (
TokenCarolDanInvalidExpandedCommandCID = gocid.MustParse("bafyreid3m3pk53gqgp5rlzqhvpedbwsqbidqlp4yz64vknwbzj7bxrmsr4")
TokenCarolDanInvalidExpandedCommand = mustGetDelegation(TokenCarolDanInvalidExpandedCommandCID)
)
var (
TokenDanErinInvalidExpandedCommandCID = gocid.MustParse("bafyreifn4sy5onwajx3kqvot5mib6m6xarzrqjozqbzgmzpmc5ox3g2uzm")
TokenDanErinInvalidExpandedCommand = mustGetDelegation(TokenDanErinInvalidExpandedCommandCID)
)
var (
TokenErinFrankInvalidExpandedCommandCID = gocid.MustParse("bafyreidmpgd36jznmq42bs34o4qi3fcbrsh4idkg6ejahudejzwb76fwxe")
TokenErinFrankInvalidExpandedCommand = mustGetDelegation(TokenErinFrankInvalidExpandedCommandCID)
)
var (
TokenCarolDanValidAttenuatedCommandCID = gocid.MustParse("bafyreiekhtm237vyapk3c6voeb5lnz54crebqdqi3x4wn4u4cbrrhzsqfe")
TokenCarolDanValidAttenuatedCommand = mustGetDelegation(TokenCarolDanValidAttenuatedCommandCID)
)
var (
TokenDanErinValidAttenuatedCommandCID = gocid.MustParse("bafyreicrvzqferyy7rgo75l5rn6r2nl7zyeexxjmu3dm4ff7rn2coblj4y")
TokenDanErinValidAttenuatedCommand = mustGetDelegation(TokenDanErinValidAttenuatedCommandCID)
)
var (
TokenErinFrankValidAttenuatedCommandCID = gocid.MustParse("bafyreie6fhspk53kplcc2phla3e7z7fzldlbmmpuwk6nbow5q6s2zjmw2q")
TokenErinFrankValidAttenuatedCommand = mustGetDelegation(TokenErinFrankValidAttenuatedCommandCID)
)
var (
TokenCarolDanInvalidSubjectCID = gocid.MustParse("bafyreifgksz6756if42tnc6rqsnbaa2u3fdrveo7ek44lnj2d64d5sw26u")
TokenCarolDanInvalidSubject = mustGetDelegation(TokenCarolDanInvalidSubjectCID)
)
var (
TokenDanErinInvalidSubjectCID = gocid.MustParse("bafyreibdwew5nypsxrm4fq73wu6hw3lgwwiolj3bi33xdrbgcf3ogm6fty")
TokenDanErinInvalidSubject = mustGetDelegation(TokenDanErinInvalidSubjectCID)
)
var (
TokenErinFrankInvalidSubjectCID = gocid.MustParse("bafyreicr364mj3n7x4iyhcksxypelktcqkkw3ptg7ggxtqegw3p3mr6zc4")
TokenErinFrankInvalidSubject = mustGetDelegation(TokenErinFrankInvalidSubjectCID)
)
var (
TokenCarolDanInvalidExpiredCID = gocid.MustParse("bafyreibgtlioorouqpwr6olk6boc3pprl5tx5xs6zpfnv3pvxtggueofii")
TokenCarolDanInvalidExpired = mustGetDelegation(TokenCarolDanInvalidExpiredCID)
)
var (
TokenDanErinInvalidExpiredCID = gocid.MustParse("bafyreidhq3hjsfrucbecgcjf2nkcgmq3sh3m5gjxz23vzcaynozs5p3uh4")
TokenDanErinInvalidExpired = mustGetDelegation(TokenDanErinInvalidExpiredCID)
)
var (
TokenErinFrankInvalidExpiredCID = gocid.MustParse("bafyreido4om3y3ttkmp4c4gxm6pqug76vu3aekb666vdp6zewpvir5zs7u")
TokenErinFrankInvalidExpired = mustGetDelegation(TokenErinFrankInvalidExpiredCID)
)
var (
TokenCarolDanInvalidInactiveCID = gocid.MustParse("bafyreicea5y2nvlitvxijkupeavtg23i7ktjk3uejnaquguurzptiabk4u")
TokenCarolDanInvalidInactive = mustGetDelegation(TokenCarolDanInvalidInactiveCID)
)
var (
TokenDanErinInvalidInactiveCID = gocid.MustParse("bafyreifsgqzkmxj2vexuts3z766mwcjreiisjg2jykyzf7tbj5sclutpvq")
TokenDanErinInvalidInactive = mustGetDelegation(TokenDanErinInvalidInactiveCID)
)
var (
TokenErinFrankInvalidInactiveCID = gocid.MustParse("bafyreifbfegon24c6dndiqyktahzs65vhyasrygbw7nhsvojn6distsdre")
TokenErinFrankInvalidInactive = mustGetDelegation(TokenErinFrankInvalidInactiveCID)
)
var ProofAliceBob = []gocid.Cid{
TokenAliceBobCID,
}
var ProofAliceBobCarol = []gocid.Cid{
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDan = []gocid.Cid{
TokenCarolDanCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanErin = []gocid.Cid{
TokenDanErinCID,
TokenCarolDanCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanErinFrank = []gocid.Cid{
TokenErinFrankCID,
TokenDanErinCID,
TokenCarolDanCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanInvalidExpandedCommand = []gocid.Cid{
TokenCarolDanInvalidExpandedCommandCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanErinInvalidExpandedCommand = []gocid.Cid{
TokenDanErinInvalidExpandedCommandCID,
TokenCarolDanInvalidExpandedCommandCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanErinFrankInvalidExpandedCommand = []gocid.Cid{
TokenErinFrankInvalidExpandedCommandCID,
TokenDanErinInvalidExpandedCommandCID,
TokenCarolDanInvalidExpandedCommandCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanValidAttenuatedCommand = []gocid.Cid{
TokenCarolDanValidAttenuatedCommandCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanErinValidAttenuatedCommand = []gocid.Cid{
TokenDanErinValidAttenuatedCommandCID,
TokenCarolDanValidAttenuatedCommandCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanErinFrankValidAttenuatedCommand = []gocid.Cid{
TokenErinFrankValidAttenuatedCommandCID,
TokenDanErinValidAttenuatedCommandCID,
TokenCarolDanValidAttenuatedCommandCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanInvalidSubject = []gocid.Cid{
TokenCarolDanInvalidSubjectCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanErinInvalidSubject = []gocid.Cid{
TokenDanErinInvalidSubjectCID,
TokenCarolDanInvalidSubjectCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanErinFrankInvalidSubject = []gocid.Cid{
TokenErinFrankInvalidSubjectCID,
TokenDanErinInvalidSubjectCID,
TokenCarolDanInvalidSubjectCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanInvalidExpired = []gocid.Cid{
TokenCarolDanInvalidExpiredCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanErinInvalidExpired = []gocid.Cid{
TokenDanErinInvalidExpiredCID,
TokenCarolDanInvalidExpiredCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanErinFrankInvalidExpired = []gocid.Cid{
TokenErinFrankInvalidExpiredCID,
TokenDanErinInvalidExpiredCID,
TokenCarolDanInvalidExpiredCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanInvalidInactive = []gocid.Cid{
TokenCarolDanInvalidInactiveCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanErinInvalidInactive = []gocid.Cid{
TokenDanErinInvalidInactiveCID,
TokenCarolDanInvalidInactiveCID,
TokenBobCarolCID,
TokenAliceBobCID,
}
var ProofAliceBobCarolDanErinFrankInvalidInactive = []gocid.Cid{
TokenErinFrankInvalidInactiveCID,
TokenDanErinInvalidInactiveCID,
TokenCarolDanInvalidInactiveCID,
TokenBobCarolCID,
TokenAliceBobCID,
}

View File

@@ -0,0 +1,32 @@
package delegationtest_test
import (
"testing"
"github.com/ipfs/go-cid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/token/delegation/delegationtest"
"github.com/ucan-wg/go-ucan/token/invocation"
)
func TestGetDelegation(t *testing.T) {
t.Parallel()
t.Run("passes with valid CID", func(t *testing.T) {
t.Parallel()
tkn, err := delegationtest.GetDelegation(delegationtest.TokenAliceBobCID)
require.NoError(t, err)
assert.NotZero(t, tkn)
})
t.Run("fails with unknown CID", func(t *testing.T) {
t.Parallel()
tkn, err := delegationtest.GetDelegation(cid.Undef)
require.ErrorIs(t, err, invocation.ErrMissingDelegation)
require.ErrorContains(t, err, "CID b")
assert.Nil(t, tkn)
})
}

View File

@@ -1,334 +1,143 @@
package invocation_test package invocation_test
import ( import (
"errors"
"fmt"
"testing" "testing"
"time"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/did" "github.com/ucan-wg/go-ucan/did/didtest"
"github.com/ucan-wg/go-ucan/pkg/args" "github.com/ucan-wg/go-ucan/pkg/args"
"github.com/ucan-wg/go-ucan/pkg/command" "github.com/ucan-wg/go-ucan/pkg/command"
"github.com/ucan-wg/go-ucan/pkg/policy" "github.com/ucan-wg/go-ucan/token/delegation/delegationtest"
"github.com/ucan-wg/go-ucan/token/delegation"
"github.com/ucan-wg/go-ucan/token/invocation" "github.com/ucan-wg/go-ucan/token/invocation"
"github.com/ucan-wg/go-ucan/token/invocation/invocationtest"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
const ( const (
rootPrivKeyCfg = "CAESQNMjVXyblHUrtmvdzjfkdrUlLkxh/cHZMFirki7dJLFjW7cCs74VxjO8Wh04f8Xg0uqyKw7N0wTUBiPy2h4hGPQ="
rootDIDStr = "did:key:z6MkkdH136Kee5akqx1DiK3hE3WGx4SKmHo11tYw2cWmjjZV"
rootTknCIDStr = "bafyreibk6lkgd32zldpqqqsdn7diyosokelrhder5lic4ujadtxl5blkei"
rootOnlyTknCIDStr = "bafyreidyknlfnsah63jujdkhe5vvjil2yoznlgoaezeq62qqhghaxzpfya"
dlg0PrivKeyCfg = "CAESQFIb3aD0lZzidUzTtwdpHtCyx1VJxe+Uq4x/S+XQFDDgz/NZIi/TR3rhgUn550RSBOSxNmw0QnR0FOPmAB7SXAg="
dlg0DIDStr = "did:key:z6MktT1f5LXQ2MFSUwwTTY9DDU2QdBzZA11V5bjou4YDQY6K"
dlg0TknCIDStr = "bafyreihocbstcdvgyeczjoijiyo2bdppyplm2aglqumwtutyapd2zlp2bi"
expiredTknDagJson = `[{"/":{"bytes":"U4IH1Q52UrdOyOHkHtXSkH0uf5ouk10k/LTOMz3UvP2k1kqv9/rbGXUwhQCy6JP3s8hc+U3h/lBXYFYzIlULAw"}},{"h":{"/":{"bytes":"NO0BcQ"}},"ucan/dlg@1.0.0-rc.1":{"aud":"did:key:z6MkqTkYrRV6WpFCWRcEHBXnTV3cVqzMtVZREhJKAAswrrGq","cmd":"/seg0","exp":1731439015,"iss":"did:key:z6MktT1f5LXQ2MFSUwwTTY9DDU2QdBzZA11V5bjou4YDQY6K","nonce":{"/":{"bytes":"AAECAwQFBgcICQoL"}},"pol":[],"sub":"did:key:z6MkkdH136Kee5akqx1DiK3hE3WGx4SKmHo11tYw2cWmjjZV"}}]`
expiredDlg0TknCIDStr = "bafyreigezbrohmjmzv5nxdw2nhjabnrvs52vcv4uvbuscnbyr2jzep2vru"
inactiveDlg0TknCIDStr = "bafyreiffflxvtfiv722i3lomrqcogc6nncxai3voxeltvrphpuwecdfbgq"
dlg1PrivKeyCfg = "CAESQPHkXp4OatPxpJ1veMAxEYzP4rd3/sPUz9PRQuJaDKTco5DJTdJC5iXxjCe1VDYAmRwlJOBvBdbsvS3qFgV6zoI="
dlg1DIDStr = "did:key:z6MkqTkYrRV6WpFCWRcEHBXnTV3cVqzMtVZREhJKAAswrrGq"
dlg1TknCIDStr = "bafyreihjbkvom3tdivzolh6aieuwamz4x6bu3dh667bxytdc5vzo37obo4"
// dlg2PrivKeyCfg = "CAESQJryJWe3uGt+7BTH2doqsN+2H83rNXv7Yw0tMoKE+lBKTqByESll668G1Jiy9gW/8hm9/jVcrFD9Y1BWyokfNBE="
// dlg2DIDStr = "did:key:z6MkjkBk9hzMhcr6rYMXHPEsXy1LAWK5dHniNdbaEPojGXr8"
// dlg3PrivKeyCfg = "CAESQPrLMlX2p+Dgz70YCyVXbHAJfVMT7lLUYAbuZ1rBKQmBLiD7WJ4Spc4VFZAsJ7HUnkneJWNTk/FFaN2z3pb/OZI="
// dlg3DIDStr = "did:key:z6MkhZKy9X4sLtbH1fGQmPVMjXmBAEQ3vAN6DRSfebSBCqpu"
invPrivKeyCfg = "CAESQHJW8WYTZDRzxjLBjrFN35raIGvVsPoXAJB/5X+J8miboVWVLZFyQmxCAIXOMpwLqWW7R2I98qsCGvxgEJZ5qgY="
invDIDStr = "did:key:z6MkqK3NgTnZZo77iptQdU9proJn1ozMmcTSKR98t8sZzJAq"
invTknCIDStr = ""
missingPrivKeyCfg = "CAESQMjRvrEIjpPYRQKmkAGw/pV0XgE958rYa4vlnKJjl1zz/sdnGnyV1xKLJk8D39edyjhHWyqcpgFnozQK62SG16k=" missingPrivKeyCfg = "CAESQMjRvrEIjpPYRQKmkAGw/pV0XgE958rYa4vlnKJjl1zz/sdnGnyV1xKLJk8D39edyjhHWyqcpgFnozQK62SG16k="
missingDIDStr = "bafyreigwypmw6eul6vadi6g6lnfbsfo2zck7gfzsbjoroqs3djhnzzc7mm" missingTknCIDStr = "bafyreigwypmw6eul6vadi6g6lnfbsfo2zck7gfzsbjoroqs3djhnzzc7mm"
missingTknCIDStr = "did:key:z6MkwboxFsH3kEuehBZ5fLkRmxi68yv1u38swA4r9Jm2VRma" missingDIDStr = "did:key:z6MkwboxFsH3kEuehBZ5fLkRmxi68yv1u38swA4r9Jm2VRma"
) )
var emptyArguments = args.New()
func TestToken_ExecutionAllowed(t *testing.T) { func TestToken_ExecutionAllowed(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("passes - only root", func(t *testing.T) { t.Run("passes - only root", func(t *testing.T) {
t.Parallel() t.Parallel()
args := invocationtest.EmptyArguments testPasses(t, didtest.PersonaBob, delegationtest.NominalCommand, emptyArguments, delegationtest.ProofAliceBob)
prf := invocationtest.Proof(t, rootOnlyTknCIDStr)
testPasses(t, []string{"seg0"}, args, prf)
}) })
t.Run("passes - valid chain", func(t *testing.T) { t.Run("passes - valid chain", func(t *testing.T) {
t.Parallel() t.Parallel()
args := invocationtest.EmptyArguments testPasses(t, didtest.PersonaFrank, delegationtest.NominalCommand, emptyArguments, delegationtest.ProofAliceBobCarolDanErinFrank)
prf := invocationtest.Proof(t, dlg1TknCIDStr, dlg0TknCIDStr, rootTknCIDStr) })
testPasses(t, []string{"seg0"}, args, prf)
t.Run("passes - proof chain attenuates command", func(t *testing.T) {
t.Parallel()
testPasses(t, didtest.PersonaFrank, delegationtest.AttenuatedCommand, emptyArguments, delegationtest.ProofAliceBobCarolDanErinFrankValidAttenuatedCommand)
})
t.Run("passes - invocation attenuates command", func(t *testing.T) {
t.Parallel()
testPasses(t, didtest.PersonaFrank, delegationtest.AttenuatedCommand, emptyArguments, delegationtest.ProofAliceBobCarolDanErinFrank)
}) })
t.Run("fails - no proof", func(t *testing.T) { t.Run("fails - no proof", func(t *testing.T) {
t.Parallel() t.Parallel()
args := args.New() testFails(t, invocation.ErrNoProof, didtest.PersonaCarol, delegationtest.NominalCommand, emptyArguments, delegationtest.ProofEmpty)
testFails(t, invocation.ErrNoProof, []string{"seg0"}, args, []cid.Cid{})
}) })
t.Run("fails - missing referenced delegation", func(t *testing.T) { t.Run("fails - missing referenced delegation", func(t *testing.T) {
t.Parallel() t.Parallel()
args := invocationtest.EmptyArguments missingTknCID, err := cid.Parse(missingTknCIDStr)
prf := invocationtest.Proof(t, missingDIDStr, rootTknCIDStr) require.NoError(t, err)
testFails(t, invocation.ErrMissingDelegation, []string{"seg0"}, args, prf)
prf := []cid.Cid{missingTknCID, delegationtest.TokenAliceBobCID}
testFails(t, invocation.ErrMissingDelegation, didtest.PersonaCarol, delegationtest.NominalCommand, emptyArguments, prf)
}) })
t.Run("fails - referenced delegation expired", func(t *testing.T) { t.Run("fails - referenced delegation expired", func(t *testing.T) {
t.Parallel() t.Parallel()
args := invocationtest.EmptyArguments testFails(t, invocation.ErrTokenInvalidNow, didtest.PersonaFrank, delegationtest.NominalCommand, emptyArguments, delegationtest.ProofAliceBobCarolDanErinFrankInvalidExpired)
prf := invocationtest.Proof(t, dlg1TknCIDStr, expiredDlg0TknCIDStr, rootTknCIDStr)
testFails(t, invocation.ErrTokenInvalidNow, []string{"seg0"}, args, prf)
}) })
t.Run("fails - referenced delegation inactive", func(t *testing.T) { t.Run("fails - referenced delegation inactive", func(t *testing.T) {
t.Parallel() t.Parallel()
args := invocationtest.EmptyArguments testFails(t, invocation.ErrTokenInvalidNow, didtest.PersonaFrank, delegationtest.NominalCommand, emptyArguments, delegationtest.ProofAliceBobCarolDanErinFrankInvalidInactive)
prf := invocationtest.Proof(t, dlg1TknCIDStr, inactiveDlg0TknCIDStr, rootTknCIDStr)
testFails(t, invocation.ErrTokenInvalidNow, []string{"seg0"}, args, prf)
}) })
t.Run("fails - last (or only) delegation not root", func(t *testing.T) { t.Run("fails - last (or only) delegation not root", func(t *testing.T) {
t.Parallel() t.Parallel()
args := args.New() prf := []cid.Cid{delegationtest.TokenErinFrankCID, delegationtest.TokenDanErinCID, delegationtest.TokenCarolDanCID}
prf := invocationtest.Proof(t, dlg1TknCIDStr) testFails(t, invocation.ErrLastNotRoot, didtest.PersonaFrank, delegationtest.NominalCommand, emptyArguments, prf)
testFails(t, invocation.ErrLastNotRoot, []string{"seg0"}, args, prf)
}) })
t.Run("fails - broken chain", func(t *testing.T) { t.Run("fails - broken chain", func(t *testing.T) {
t.Parallel() t.Parallel()
args := invocationtest.EmptyArguments prf := []cid.Cid{delegationtest.TokenCarolDanCID, delegationtest.TokenAliceBobCID}
prf := invocationtest.Proof(t, dlg1TknCIDStr, rootTknCIDStr) testFails(t, invocation.ErrBrokenChain, didtest.PersonaFrank, delegationtest.NominalCommand, emptyArguments, prf)
testFails(t, invocation.ErrBrokenChain, []string{"seg0"}, args, prf)
}) })
t.Run("fails - first not issued to invoker", func(t *testing.T) { t.Run("fails - first not issued to invoker", func(t *testing.T) {
t.Parallel() t.Parallel()
args := invocationtest.EmptyArguments prf := []cid.Cid{delegationtest.TokenBobCarolCID, delegationtest.TokenAliceBobCID}
prf := invocationtest.Proof(t, dlg0TknCIDStr, rootTknCIDStr) testFails(t, invocation.ErrBrokenChain, didtest.PersonaFrank, delegationtest.NominalCommand, emptyArguments, prf)
testFails(t, invocation.ErrBrokenChain, []string{"seg0"}, args, prf) })
t.Run("fails - proof chain expands command", func(t *testing.T) {
t.Parallel()
testFails(t, invocation.ErrCommandNotCovered, didtest.PersonaFrank, delegationtest.NominalCommand, emptyArguments, delegationtest.ProofAliceBobCarolDanErinFrankInvalidExpandedCommand)
})
t.Run("fails - invocation expands command", func(t *testing.T) {
t.Parallel()
testFails(t, invocation.ErrCommandNotCovered, didtest.PersonaFrank, delegationtest.ExpandedCommand, emptyArguments, delegationtest.ProofAliceBobCarolDanErinFrank)
})
t.Run("fails - inconsistent subject", func(t *testing.T) {
t.Parallel()
testFails(t, invocation.ErrWrongSub, didtest.PersonaFrank, delegationtest.ExpandedCommand, emptyArguments, delegationtest.ProofAliceBobCarolDanErinFrankInvalidSubject)
}) })
} }
func test(t *testing.T, cmdSegs []string, args *args.Args, prf []cid.Cid, opts ...invocation.Option) (bool, error) { func test(t *testing.T, persona didtest.Persona, cmd command.Command, args *args.Args, prf []cid.Cid, opts ...invocation.Option) (bool, error) {
ldr := newTestDelegationLoader(t) t.Helper()
tkn := testInvocation(t, invPrivKeyCfg, rootDIDStr, rootDIDStr, cmdSegs, args, prf, opts...)
tkn, err := invocation.New(persona.DID(t), didtest.PersonaAlice.DID(t), cmd, prf, opts...)
require.NoError(t, err)
ldr, err := delegationtest.GetDelegationLoader()
require.NoError(t, err)
return tkn.ExecutionAllowed(ldr) return tkn.ExecutionAllowed(ldr)
} }
func testFails(t *testing.T, expErr error, cmdSegs []string, args *args.Args, prf []cid.Cid, opts ...invocation.Option) { func testFails(t *testing.T, expErr error, persona didtest.Persona, cmd command.Command, args *args.Args, prf []cid.Cid, opts ...invocation.Option) {
t.Helper() ok, err := test(t, persona, cmd, args, prf, opts...)
ok, err := test(t, cmdSegs, args, prf, opts...)
require.ErrorIs(t, err, expErr) require.ErrorIs(t, err, expErr)
assert.False(t, ok) assert.False(t, ok)
} }
func testPasses(t *testing.T, cmdSegs []string, args *args.Args, prf []cid.Cid, opts ...invocation.Option) { func testPasses(t *testing.T, persona didtest.Persona, cmd command.Command, args *args.Args, prf []cid.Cid, opts ...invocation.Option) {
ok, err := test(t, cmdSegs, args, prf, opts...) ok, err := test(t, persona, cmd, args, prf, opts...)
require.NoError(t, err) require.NoError(t, err)
assert.True(t, ok) assert.True(t, ok)
} }
var _ invocation.DelegationLoader = (*testDelegationLoader)(nil)
type testDelegationLoader struct {
tkns map[cid.Cid]*delegation.Token
}
func newTestDelegationLoader(t *testing.T) *testDelegationLoader {
t.Helper()
cmdSegs := []string{"seg0"}
ldr := &testDelegationLoader{
tkns: map[cid.Cid]*delegation.Token{},
}
// aud, err := did.Parse(dlg0DIDStr)
// require.NoError(t, err)
// cmd := command.New(cmdSegs...)
// pol, err := policy.FromDagJson("[]")
// require.NoError(t, err)
// rootDlg, err := delegation.Root(rootPrivKey, aud, cmd, pol)
// require.NoError(t, err)
// _, rootCID, err := rootDlg.ToSealed(rootPrivKey)
// require.NoError(t, err)
// Do not add this one to the loader
_, missingCID := testDelegation(t, missingPrivKeyCfg, dlg1DIDStr, rootDIDStr, cmdSegs, "[]")
t.Log("Missing CID", missingCID)
rootTkn, rootDlgCID := testDelegation(t, rootPrivKeyCfg, dlg0DIDStr, rootDIDStr, cmdSegs, "[]")
t.Log("Root chain CID:", rootDlgCID)
ldr.tkns[rootDlgCID] = rootTkn
rootOnlyTkn, rootOnlyDlgCID := testDelegation(t, rootPrivKeyCfg, invDIDStr, rootDIDStr, cmdSegs, "[]")
t.Log("Root only chain CID:", rootOnlyDlgCID)
ldr.tkns[rootOnlyDlgCID] = rootOnlyTkn
dlg0Tkn, dlg0CID := testDelegation(t, dlg0PrivKeyCfg, dlg1DIDStr, rootDIDStr, cmdSegs, "[]")
t.Log("Dlg0 CID:", dlg0CID)
ldr.tkns[dlg0CID] = dlg0Tkn
// exp := time.Now().Add(time.Second)
// expiredDlg0Tkn, expiredDlg0CID := testDelegation(t, dlg0PrivKeyCfg, dlg1DIDStr, rootDIDStr, cmdSegs, "[]", delegation.WithExpiration(exp))
// t.Log("Expired Dlg0 CID:", expiredDlg0CID)
// dlg0PrivKeyEnc, err := crypto.ConfigDecodeKey(dlg0PrivKeyCfg)
// require.NoError(t, err)
// dlg0PrivKey, err := crypto.UnmarshalPrivateKey(dlg0PrivKeyEnc)
// require.NoError(t, err)
// dlg0DagJson, err := expiredDlg0Tkn.ToDagJson(dlg0PrivKey)
// require.NoError(t, err)
// t.Log("Expired token DAGJSON:", string(dlg0DagJson))
expiredDlg0Tkn, err := delegation.FromDagJson([]byte(expiredTknDagJson))
require.NoError(t, err)
expiredDlg0TknCID, err := cid.Parse(expiredDlg0TknCIDStr)
ldr.tkns[expiredDlg0TknCID] = expiredDlg0Tkn
nbf, err := time.Parse(time.RFC3339, "2500-01-01T00:00:00Z")
require.NoError(t, err)
inactiveDlg0Tkn, inactiveDlg0CID := testDelegation(t, dlg0PrivKeyCfg, dlg1DIDStr, rootDIDStr, cmdSegs, "[]", delegation.WithNotBefore(nbf))
t.Log("Inactive Dlg0 CID:", inactiveDlg0CID)
ldr.tkns[inactiveDlg0CID] = inactiveDlg0Tkn
dlg1Tkn, dlg1CID := testDelegation(t, dlg1PrivKeyCfg, invDIDStr, rootDIDStr, cmdSegs, "[]")
t.Log("Dlg1 CID:", dlg1CID)
ldr.tkns[dlg1CID] = dlg1Tkn
// for i := 0; i < 5; i++ {
// privKey, id, err := did.GenerateEd25519()
// require.NoError(t, err)
// privKeyEnc, err := crypto.MarshalPrivateKey(privKey)
// require.NoError(t, err)
// t.Log("PrivKey:", crypto.ConfigEncodeKey(privKeyEnc), "DID:", id)
// }
t.Log("Delegation loader length:", len(ldr.tkns))
return ldr
}
var ErrUnknownDelegationTokenCID = errors.New("unknown delegation token CID")
func (l *testDelegationLoader) GetDelegation(c cid.Cid) (*delegation.Token, error) {
tkn, ok := l.tkns[c]
if !ok {
return nil, fmt.Errorf("%w: %s", ErrUnknownDelegationTokenCID, c.String())
}
return tkn, nil
}
func testDelegation(t *testing.T, privKeyCfg string, audStr string, subStr string, cmdSegs []string, polDAGJSON string, opts ...delegation.Option) (*delegation.Token, cid.Cid) {
t.Helper()
privKey, aud, sub, cmd := parseTokenArgs(t, privKeyCfg, audStr, subStr, cmdSegs)
pol, err := policy.FromDagJson(polDAGJSON)
require.NoError(t, err)
opts = append(
opts,
delegation.WithNonce([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b}),
delegation.WithSubject(sub),
)
tkn, err := delegation.New(privKey, aud, cmd, pol, opts...)
require.NoError(t, err)
_, id, err := tkn.ToSealed(privKey)
require.NoError(t, err)
return tkn, id
}
func testInvocation(t *testing.T, privKeyCfg string, audStr string, subStr string, cmdSegs []string, args *args.Args, prf []cid.Cid, opts ...invocation.Option) *invocation.Token {
t.Helper()
privKey, _, sub, cmd := parseTokenArgs(t, privKeyCfg, audStr, subStr, cmdSegs)
iss, err := did.FromPrivKey(privKey)
require.NoError(t, err)
tkn, err := invocation.New(iss, sub, cmd, prf)
require.NoError(t, err)
return tkn
}
func testProof(t *testing.T, cidStrs ...string) []cid.Cid {
var prf []cid.Cid
for cidStr := range cidStrs {
id, err := cid.Parse(cidStr)
require.NoError(t, err)
prf = append(prf, id)
}
return prf
}
func parseTokenArgs(
t *testing.T,
privKeyCfg string,
audStr string,
subStr string,
cmdSegs []string,
) (
privKey crypto.PrivKey,
aud did.DID,
sub did.DID,
cmd command.Command,
) {
t.Helper()
var err error
privKeyEnc, err := crypto.ConfigDecodeKey(privKeyCfg)
require.NoError(t, err)
privKey, err = crypto.UnmarshalPrivateKey(privKeyEnc)
require.NoError(t, err)
aud, err = did.Parse(audStr)
require.NoError(t, err)
sub, err = did.Parse(subStr)
require.NoError(t, err)
cmd = command.New(cmdSegs...)
return
}

View File

@@ -1,39 +0,0 @@
package invocationtest
import (
"testing"
"github.com/ipfs/go-cid"
"github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/pkg/args"
"github.com/ucan-wg/go-ucan/pkg/policy"
)
var (
EmptyArguments = args.New()
EmptyPolicy = emptyPolicy()
)
func emptyPolicy() policy.Policy {
pol, err := policy.FromDagJson("[]")
if err != nil {
panic(err)
}
return pol
}
func Proof(t *testing.T, cidStrs ...string) []cid.Cid {
// t.Helper()
prf := make([]cid.Cid, len(cidStrs))
for i, cidStr := range cidStrs {
id, err := cid.Parse(cidStr)
require.NoError(t, err)
prf[i] = id
}
return prf
}