package delegationtest import ( "os" "path/filepath" "slices" "time" "github.com/dave/jennifer/jen" "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/did/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" ) const ( tokenNamePrefix = "Token" proorChainNamePrefix = "Proof" ) var constantNonce = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b} type newDelegationParams struct { privKey crypto.PrivKey aud did.DID sub did.DID cmd command.Command pol policy.Policy opts []delegation.Option } type token struct { name string id cid.Cid } type proof struct { name string prf []cid.Cid } 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) {}, } } type generator struct { dlgs []token chains []proof } func (g *generator) chainPersonas(personas []didtest.Persona, acc acc, vari variant) error { acc.name += personas[0].Name() proofName := acc.name if len(vari.name) > 0 { proofName += "_" + vari.name } g.createProofChain(proofName, acc.chain) if len(personas) < 2 { return nil } name := personas[0].Name() + personas[1].Name() params := newDelegationParams{ privKey: personas[0].PrivKey(), aud: personas[1].DID(), cmd: NominalCommand, pol: policy.Policy{}, opts: []delegation.Option{ delegation.WithSubject(didtest.PersonaAlice.DID()), delegation.WithNonce(constantNonce), }, } // Create each nominal token and continue the chain id, err := g.createDelegation(params, name, vari) if err != nil { return err } acc.chain = append(acc.chain, id) err = g.chainPersonas(personas[1:], acc, vari) if err != nil { return err } // 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())) }}, {name: "InvalidExpired", variant: func(p *newDelegationParams) { // Note: this makes the generator not deterministic 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") if err != nil { panic(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, err := g.createDelegation(params, name, v) if err != nil { return err } // replace the previous Carol token id with the one from the variant acc.chain[len(acc.chain)-1] = id err = g.chainPersonas(personas[1:], acc, v) if err != nil { return err } } } return nil } func (g *generator) createDelegation(params newDelegationParams, name string, vari variant) (cid.Cid, error) { vari.variant(¶ms) tkn, err := delegation.New(params.privKey, params.aud, params.cmd, params.pol, params.opts...) if err != nil { return cid.Undef, err } data, id, err := tkn.ToSealed(params.privKey) if err != nil { return cid.Undef, err } dlgName := tokenNamePrefix + name if len(vari.name) > 0 { dlgName += "_" + vari.name } err = os.WriteFile(filepath.Join(tokenDir, dlgName+tokenExt), data, 0o644) if err != nil { return cid.Undef, err } g.dlgs = append(g.dlgs, token{ name: dlgName, id: id, }) return id, nil } func (g *generator) createProofChain(name string, prf []cid.Cid) { 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() error { 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() } return file.Save("token_gen.go") }