package main import ( "fmt" "os" "path/filepath" "slices" "time" "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/pkg/policy/policytest" "github.com/ucan-wg/go-ucan/token/delegation" "github.com/ucan-wg/go-ucan/token/delegation/delegationtest" ) const ( tokenNamePrefix = "Token" proorChainNamePrefix = "Proof" tokenExt = ".dagcbor" ) var constantNonce = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b} type newDelegationParams struct { privKey crypto.PrivKey aud 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: delegationtest.NominalCommand, pol: policytest.EmptyPolicy, 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 = delegationtest.ExpandedCommand }}, {name: "ValidAttenuatedCommand", variant: func(p *newDelegationParams) { p.cmd = delegationtest.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)) }}, {name: "ValidExamplePolicy", variant: func(p *newDelegationParams) { p.pol = policytest.SpecPolicy }}, } // 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) issDID, err := did.FromPrivKey(params.privKey) if err != nil { return cid.Undef, err } tkn, err := delegation.New(issDID, 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("..", delegationtest.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 { var err error f, err := os.Create("../token_gen.go") if err != nil { return err } defer func() { err = f.Close() }() _, _ = fmt.Fprintln(f, "// Code generated by delegationtest - DO NOT EDIT.") _, _ = fmt.Fprintln(f) _, _ = fmt.Fprintln(f, "package delegationtest") _, _ = fmt.Fprintln(f) _, _ = fmt.Fprintln(f, "import (") _, _ = fmt.Fprintln(f, "\t\"github.com/ipfs/go-cid\"") _, _ = fmt.Fprintln(f) _, _ = fmt.Fprintln(f, "\t\"github.com/ucan-wg/go-ucan/token/delegation\"") _, _ = fmt.Fprintln(f, ")") refs := make(map[cid.Cid]string, len(g.dlgs)) for _, d := range g.dlgs { refs[d.id] = d.name + "CID" _, _ = fmt.Fprintln(f) _, _ = fmt.Fprintln(f, "var (") _, _ = fmt.Fprintf(f, "\t%sCID = cid.MustParse(\"%s\")\n", d.name, d.id.String()) _, _ = fmt.Fprintf(f, "\t%sSealed = mustGetBundle(%s).Sealed\n", d.name, d.name+"CID") _, _ = fmt.Fprintf(f, "\t%sBundle = mustGetBundle(%s)\n", d.name, d.name+"CID") _, _ = fmt.Fprintf(f, "\t%s = mustGetBundle(%s).Decoded\n", d.name, d.name+"CID") _, _ = fmt.Fprintln(f, ")") } _, _ = fmt.Fprintln(f) _, _ = fmt.Fprintln(f, "var AllTokens = []*delegation.Token{") for _, d := range g.dlgs { _, _ = fmt.Fprintf(f, "\t%s,\n", d.name) } _, _ = fmt.Fprintln(f, "}") _, _ = fmt.Fprintln(f) _, _ = fmt.Fprintln(f, "var AllBundles = []*delegation.Bundle{") for _, d := range g.dlgs { _, _ = fmt.Fprintf(f, "\t%sBundle,\n", d.name) } _, _ = fmt.Fprintln(f, "}") _, _ = fmt.Fprintln(f) _, _ = fmt.Fprintln(f, "var cidToName = map[cid.Cid]string{") for _, d := range g.dlgs { _, _ = fmt.Fprintf(f, "\t%sCID: \"%s\",\n", d.name, d.name) } _, _ = fmt.Fprintln(f, "}") for _, c := range g.chains { _, _ = fmt.Fprintln(f) _, _ = fmt.Fprintf(f, "var %s = []cid.Cid{\n", c.name) for _, d := range slices.Backward(c.prf) { _, _ = fmt.Fprintf(f, "\t%s,\n", refs[d]) } _, _ = fmt.Fprintln(f, "}") } return err }