diff --git a/pkg/meta/meta.go b/pkg/meta/meta.go index f3ea126..a51eec7 100644 --- a/pkg/meta/meta.go +++ b/pkg/meta/meta.go @@ -4,10 +4,12 @@ import ( "errors" "fmt" "reflect" + "strings" "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/datamodel" "github.com/ipld/go-ipld-prime/node/basicnode" + "github.com/ipld/go-ipld-prime/printer" ) var ErrUnsupported = errors.New("failure adding unsupported type to meta") @@ -139,6 +141,25 @@ func (m *Meta) Equals(other *Meta) bool { return true } +func (m *Meta) String() string { + buf := strings.Builder{} + buf.WriteString("{") + + var i int + for key, node := range m.Values { + if i > 0 { + buf.WriteString(", ") + } + i++ + buf.WriteString(key) + buf.WriteString(":") + buf.WriteString(printer.Sprint(node)) + } + + buf.WriteString("}") + return buf.String() +} + func fqtn(val any) string { var name string diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index 7acac17..e7807dc 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -3,7 +3,11 @@ package policy // https://github.com/ucan-wg/delegation/blob/4094d5878b58f5d35055a3b93fccda0b8329ebae/README.md#policy import ( + "fmt" + "strings" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec/dagjson" "github.com/ucan-wg/go-ucan/pkg/policy/selector" ) @@ -24,8 +28,20 @@ const ( type Policy []Statement +func (p Policy) String() string { + if len(p) == 0 { + return "[]" + } + childs := make([]string, len(p)) + for i, statement := range p { + childs[i] = strings.ReplaceAll(statement.String(), "\n", "\n ") + } + return fmt.Sprintf("[\n %s\n]", strings.Join(childs, ",\n ")) +} + type Statement interface { Kind() string + String() string } type equality struct { @@ -38,6 +54,14 @@ func (e equality) Kind() string { return e.kind } +func (e equality) String() string { + child, err := ipld.Encode(e.value, dagjson.Encode) + if err != nil { + return "ERROR: INVALID VALUE" + } + return fmt.Sprintf(`["%s", "%s", %s]`, e.kind, e.selector, strings.ReplaceAll(string(child), "\n", "\n ")) +} + func Equal(selector selector.Selector, value ipld.Node) Statement { return equality{kind: KindEqual, selector: selector, value: value} } @@ -66,6 +90,11 @@ func (n negation) Kind() string { return KindNot } +func (n negation) String() string { + child := n.statement.String() + return fmt.Sprintf(`["%s", "%s"]`, n.Kind(), strings.ReplaceAll(child, "\n", "\n ")) +} + func Not(stmt Statement) Statement { return negation{statement: stmt} } @@ -79,6 +108,14 @@ func (c connective) Kind() string { return c.kind } +func (c connective) String() string { + childs := make([]string, len(c.statements)) + for i, statement := range c.statements { + childs[i] = strings.ReplaceAll(statement.String(), "\n", "\n ") + } + return fmt.Sprintf("[\"%s\", [\n %s]]\n", c.kind, strings.Join(childs, ",\n ")) +} + func And(stmts ...Statement) Statement { return connective{kind: KindAnd, statements: stmts} } @@ -96,6 +133,10 @@ func (n wildcard) Kind() string { return KindLike } +func (n wildcard) String() string { + return fmt.Sprintf(`["%s", "%s", "%s"]`, n.Kind(), n.selector, n.pattern) +} + func Like(selector selector.Selector, pattern string) (Statement, error) { g, err := parseGlob(pattern) if err != nil { @@ -105,6 +146,14 @@ func Like(selector selector.Selector, pattern string) (Statement, error) { return wildcard{selector: selector, pattern: g}, nil } +func MustLike(selector selector.Selector, pattern string) Statement { + g, err := Like(selector, pattern) + if err != nil { + panic(err) + } + return g +} + type quantifier struct { kind string selector selector.Selector @@ -115,6 +164,11 @@ func (n quantifier) Kind() string { return n.kind } +func (n quantifier) String() string { + child := n.statement.String() + return fmt.Sprintf("[\"%s\", \"%s\",\n %s]", n.Kind(), n.selector, strings.ReplaceAll(child, "\n", "\n ")) +} + func All(selector selector.Selector, statement Statement) Statement { return quantifier{kind: KindAll, selector: selector, statement: statement} } diff --git a/pkg/policy/selector/parsing.go b/pkg/policy/selector/parsing.go index f7778c9..e47a70a 100644 --- a/pkg/policy/selector/parsing.go +++ b/pkg/policy/selector/parsing.go @@ -27,7 +27,7 @@ func Parse(str string) (Selector, error) { if len(sel) > 0 && sel[len(sel)-1].Identity() { return nil, newParseError("selector contains unsupported recursive descent segment: '..'", str, col, tok) } - sel = append(sel, Identity) + sel = append(sel, segment{".", true, false, false, nil, "", 0}) case "[]": sel = append(sel, segment{tok, false, opt, true, nil, "", 0}) default: diff --git a/pkg/policy/selector/selector.go b/pkg/policy/selector/selector.go index 1d57718..e116c08 100644 --- a/pkg/policy/selector/selector.go +++ b/pkg/policy/selector/selector.go @@ -23,7 +23,7 @@ func (s Selector) String() string { return res.String() } -var Identity = segment{".", true, false, false, nil, "", 0} +var Identity = MustParse(".") var ( indexRegex = regexp.MustCompile(`^-?\d+$`) diff --git a/token/delegation/examples_test.go b/token/delegation/examples_test.go index ebd3650..a87a196 100644 --- a/token/delegation/examples_test.go +++ b/token/delegation/examples_test.go @@ -2,10 +2,12 @@ package delegation_test import ( "bytes" + "crypto/rand" "encoding/base64" "encoding/hex" "encoding/json" "fmt" + "time" "github.com/ipfs/go-cid" "github.com/ipld/go-ipld-prime" @@ -16,6 +18,8 @@ import ( "github.com/ucan-wg/go-ucan/did" "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/pkg/policy/selector" "github.com/ucan-wg/go-ucan/token/delegation" "github.com/ucan-wg/go-ucan/token/internal/envelope" ) @@ -23,42 +27,58 @@ import ( // The following example shows how to create a delegation.Token with // distinct DIDs for issuer (iss), audience (aud) and subject (sub). func ExampleNew() { - issuerPrivKey := examplePrivKey(issuerPrivKeyCfg) - audienceDID := exampleDID(AudienceDID) - command := exampleCommand(subJectCmd) - policy := examplePolicy(subjectPol) - subjectDID := exampleDID(subjectDID) + issPriv, issPub, err := crypto.GenerateEd25519Key(rand.Reader) + printThenPanicOnErr(err) - // Don't do this in your code - a nonce should be a cryptographically - // strong random slice of bytes to ensure the integrity of your private - // key. For this example, a fixed nonce is required to obtain the fixed - // printed output (below). If unsure of what value to supply for the - // nonce, don't pass the WithNonce option and one will be generated - // when the token is created. - nonce := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b} + issDid, err := did.FromPubKey(issPub) + printThenPanicOnErr(err) + fmt.Println("issDid:", issDid) - tkn, err := delegation.New( - issuerPrivKey, - audienceDID, - command, - policy, - delegation.WithSubject(subjectDID), - delegation.WithNonce(nonce), + audDid := did.MustParse(AudienceDID) + subDid := did.MustParse(subjectDID) + + // The command defines the shape of the arguments that will be evaluated against the policy + cmd := command.MustParse("/foo/bar") + + // The policy defines what is allowed to do. + pol := policy.Policy{ + policy.Equal(selector.MustParse(".status"), literal.String("draft")), + policy.All(selector.MustParse(".reviewer"), + policy.MustLike(selector.MustParse(".email"), "*@example.com"), + ), + policy.Any(selector.MustParse(".tags"), policy.Or( + policy.Equal(selector.Identity, literal.String("news")), + policy.Equal(selector.Identity, literal.String("press")), + )), + } + + tkn, err := delegation.New(issPriv, audDid, cmd, pol, + delegation.WithSubject(subDid), + delegation.WithExpirationAfter(time.Hour), + delegation.WithMeta("foo", "bar"), + delegation.WithMeta("baz", 123), ) printThenPanicOnErr(err) - data, id, err := tkn.ToSealed(issuerPrivKey) + + // "Seal", meaning encode and wrap into a signed envelope. + data, id, err := tkn.ToSealed(issPriv) printThenPanicOnErr(err) printCIDAndSealed(id, data) - // Output: - // CID (base58BTC): zdpuAw26pFuvZa2Z9YAtpZZnWN6VmnRFr7Z8LVY5c7RVWoxGY - // DAG-CBOR (base64) out: glhAmnAkgfjAx4SA5pzJmtaHRJtTGNpF1y6oqb4yhGoM2H2EUGbBYT4rVDjMKBgCjhdGHjipm00L8iR5SsQh3sIEBaJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cPZjaXNzeDhkaWQ6a2V5Ono2TWtwem4ybjNaR1QyVmFxTUdTUUMzdHptelY0VFM5UzcxaUZzRFhFMVdub05IMmNwb2yDg2I9PWcuc3RhdHVzZWRyYWZ0g2NhbGxpLnJldmlld2Vyg2RsaWtlZi5lbWFpbG0qQGV4YW1wbGUuY29tg2NhbnllLnRhZ3OCYm9ygoNiPT1hLmRuZXdzg2I9PWEuZXByZXNzY3N1Yng4ZGlkOmtleTp6Nk1rdEExdUJkQ3BxNHVKQnFFOWpqTWlMeXhaQmc5YTZ4Z1BQS0pqTXFzczZaYzJkbWV0YaBlbm9uY2VMAAECAwQFBgcICQoL + // Example output: + // + // issDid: did:key:z6MksKbqUiXRKVDHQJ2yezG83M6d68AQbz9rtajULF575X3s + // + // CID (base58BTC): zdpuAtXJQXZt123WNczSueoBrVcyKoJ2LH1aTmf41dZrisJJA + // + // DAG-CBOR (base64) out: glhAGCWszoibTPgkBSe5pk03wsB2orGzRKFvxLeqoDTNixxzXTDGKTj4ZfZrGOyCxf6rNW5zP8x2esFKV/akgy/nAaJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cBpnDPLWY2lzc3g4ZGlkOmtleTp6Nk1rc0ticVVpWFJLVkRIUUoyeWV6RzgzTTZkNjhBUWJ6OXJ0YWpVTEY1NzVYM3NjcG9sg4NiPT1nLnN0YXR1c2VkcmFmdINjYWxsaS5yZXZpZXdlcoNkbGlrZWYuZW1haWxtKkBleGFtcGxlLmNvbYNjYW55ZS50YWdzgmJvcoKDYj09YS5kbmV3c4NiPT1hLmVwcmVzc2NzdWJ4OGRpZDprZXk6ejZNa3RBMXVCZENwcTR1SkJxRTlqak1pTHl4WkJnOWE2eGdQUEtKak1xc3M2WmMyZG1ldGGiY2Jhehh7Y2Zvb2NiYXJlbm9uY2VMgb9wlP/cdMKutRg+ + // // Converted to DAG-JSON out: // [ // { // "/": { - // "bytes": "mnAkgfjAx4SA5pzJmtaHRJtTGNpF1y6oqb4yhGoM2H2EUGbBYT4rVDjMKBgCjhdGHjipm00L8iR5SsQh3sIEBQ" + // "bytes": "GCWszoibTPgkBSe5pk03wsB2orGzRKFvxLeqoDTNixxzXTDGKTj4ZfZrGOyCxf6rNW5zP8x2esFKV/akgy/nAQ" // } // }, // { @@ -70,12 +90,15 @@ func ExampleNew() { // "ucan/dlg@1.0.0-rc.1": { // "aud": "did:key:z6Mkq5YmbJcTrPExNDi26imrTCpKhepjBFBSHqrBDN2ArPkv", // "cmd": "/foo/bar", - // "exp": null, - // "iss": "did:key:z6Mkpzn2n3ZGT2VaqMGSQC3tzmzV4TS9S71iFsDXE1WnoNH2", - // "meta": {}, + // "exp": 1728901846, + // "iss": "did:key:z6MksKbqUiXRKVDHQJ2yezG83M6d68AQbz9rtajULF575X3s", + // "meta": { + // "baz": 123, + // "foo": "bar" + // }, // "nonce": { // "/": { - // "bytes": "AAECAwQFBgcICQoL" + // "bytes": "gb9wlP/cdMKutRg+" // } // }, // "pol": [ @@ -123,40 +146,56 @@ func ExampleNew() { // - a delegation.Token with the subject (sub) set to the value of issuer // (iss). func ExampleRoot() { - issuerPrivKey := examplePrivKey(issuerPrivKeyCfg) - audienceDID := exampleDID(AudienceDID) - command := exampleCommand(subJectCmd) - policy := examplePolicy(subjectPol) + issPriv, issPub, err := crypto.GenerateEd25519Key(rand.Reader) + printThenPanicOnErr(err) - // Don't do this in your code - a nonce should be a cryptographically - // strong random slice of bytes to ensure the integrity of your private - // key. For this example, a fixed nonce is required to obtain the fixed - // printed output (below). If unsure of what value to supply for the - // nonce, don't pass the WithNonce option and one will be generated - // when the token is created. - nonce := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b} + issDid, err := did.FromPubKey(issPub) + printThenPanicOnErr(err) + fmt.Println("issDid:", issDid) - tkn, err := delegation.Root( - issuerPrivKey, - audienceDID, - command, - policy, - delegation.WithNonce(nonce), + audDid := did.MustParse(AudienceDID) + + // The command defines the shape of the arguments that will be evaluated against the policy + cmd := command.MustParse("/foo/bar") + + // The policy defines what is allowed to do. + pol := policy.Policy{ + policy.Equal(selector.MustParse(".status"), literal.String("draft")), + policy.All(selector.MustParse(".reviewer"), + policy.MustLike(selector.MustParse(".email"), "*@example.com"), + ), + policy.Any(selector.MustParse(".tags"), policy.Or( + policy.Equal(selector.Identity, literal.String("news")), + policy.Equal(selector.Identity, literal.String("press")), + )), + } + + tkn, err := delegation.Root(issPriv, audDid, cmd, pol, + delegation.WithExpirationAfter(time.Hour), + delegation.WithMeta("foo", "bar"), + delegation.WithMeta("baz", 123), ) printThenPanicOnErr(err) - data, id, err := tkn.ToSealed(issuerPrivKey) + + // "Seal", meaning encode and wrap into a signed envelope. + data, id, err := tkn.ToSealed(issPriv) printThenPanicOnErr(err) printCIDAndSealed(id, data) - // Output: - // CID (base58BTC): zdpuAnbsR3e6DK8hBk5WA7KwbHYN6CKY4a3Bv1GNehvFYShQ8 - // DAG-CBOR (base64) out: glhA67ASBczF/wlIP0ESENn+4ZNQKukjcTNz+fo7K2tYa6OUm0rWICDJJkDWm7lJeQt+KvSA+Y4ctHTQbAr3Lr7mDqJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cPZjaXNzeDhkaWQ6a2V5Ono2TWtwem4ybjNaR1QyVmFxTUdTUUMzdHptelY0VFM5UzcxaUZzRFhFMVdub05IMmNwb2yDg2I9PWcuc3RhdHVzZWRyYWZ0g2NhbGxpLnJldmlld2Vyg2RsaWtlZi5lbWFpbG0qQGV4YW1wbGUuY29tg2NhbnllLnRhZ3OCYm9ygoNiPT1hLmRuZXdzg2I9PWEuZXByZXNzY3N1Yng4ZGlkOmtleTp6Nk1rcHpuMm4zWkdUMlZhcU1HU1FDM3R6bXpWNFRTOVM3MWlGc0RYRTFXbm9OSDJkbWV0YaBlbm9uY2VMAAECAwQFBgcICQoL + // Example output: + // + // issDid: did:key:z6MkshW2ADRrmfBuuBpKJiyNd7acLK1yjnxFJuBimwjQ4Bo5 + // + // CID (base58BTC): zdpuAoBzE3kJK1qZC9EXH7h6iCwym1TqfxT9XzUfFNfcjcAKh + // + // DAG-CBOR (base64) out: glhADpyBSrTdRn2oZdJU26CjgFbaH7LbTDWyyAdgIwAW0p151XSdJwoBS2vTCp0+7sEkf4X2wl6N5IhxiKyQ8OkbCaJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cBpnDPPCY2lzc3g4ZGlkOmtleTp6Nk1rc2hXMkFEUnJtZkJ1dUJwS0ppeU5kN2FjTEsxeWpueEZKdUJpbXdqUTRCbzVjcG9sg4NiPT1nLnN0YXR1c2VkcmFmdINjYWxsaS5yZXZpZXdlcoNkbGlrZWYuZW1haWxtKkBleGFtcGxlLmNvbYNjYW55ZS50YWdzgmJvcoKDYj09YS5kbmV3c4NiPT1hLmVwcmVzc2NzdWJ4OGRpZDprZXk6ejZNa3NoVzJBRFJybWZCdXVCcEtKaXlOZDdhY0xLMXlqbnhGSnVCaW13alE0Qm81ZG1ldGGiY2Jhehh7Y2Zvb2NiYXJlbm9uY2VMDmBGXMa/TCvhLHqu + // // Converted to DAG-JSON out: // [ // { // "/": { - // "bytes": "67ASBczF/wlIP0ESENn+4ZNQKukjcTNz+fo7K2tYa6OUm0rWICDJJkDWm7lJeQt+KvSA+Y4ctHTQbAr3Lr7mDg" + // "bytes": "DpyBSrTdRn2oZdJU26CjgFbaH7LbTDWyyAdgIwAW0p151XSdJwoBS2vTCp0+7sEkf4X2wl6N5IhxiKyQ8OkbCQ" // } // }, // { @@ -168,12 +207,15 @@ func ExampleRoot() { // "ucan/dlg@1.0.0-rc.1": { // "aud": "did:key:z6Mkq5YmbJcTrPExNDi26imrTCpKhepjBFBSHqrBDN2ArPkv", // "cmd": "/foo/bar", - // "exp": null, - // "iss": "did:key:z6Mkpzn2n3ZGT2VaqMGSQC3tzmzV4TS9S71iFsDXE1WnoNH2", - // "meta": {}, + // "exp": 1728902082, + // "iss": "did:key:z6MkshW2ADRrmfBuuBpKJiyNd7acLK1yjnxFJuBimwjQ4Bo5", + // "meta": { + // "baz": 123, + // "foo": "bar" + // }, // "nonce": { // "/": { - // "bytes": "AAECAwQFBgcICQoL" + // "bytes": "DmBGXMa/TCvhLHqu" // } // }, // "pol": [ @@ -211,7 +253,7 @@ func ExampleRoot() { // ] // ] // ], - // "sub": "did:key:z6Mkpzn2n3ZGT2VaqMGSQC3tzmzV4TS9S71iFsDXE1WnoNH2" + // "sub": "did:key:z6MkshW2ADRrmfBuuBpKJiyNd7acLK1yjnxFJuBimwjQ4Bo5" // } // } // ] @@ -220,8 +262,10 @@ func ExampleRoot() { // The following example demonstrates how to get a delegation.Token from // a DAG-CBOR []byte. func ExampleToken_FromSealed() { - cborBytes := exampleCBORData() - fmt.Println("DAG-CBOR (base64) in:", base64.StdEncoding.EncodeToString(cborBytes)) + const cborBase64 = "glhAmnAkgfjAx4SA5pzJmtaHRJtTGNpF1y6oqb4yhGoM2H2EUGbBYT4rVDjMKBgCjhdGHjipm00L8iR5SsQh3sIEBaJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cPZjaXNzeDhkaWQ6a2V5Ono2TWtwem4ybjNaR1QyVmFxTUdTUUMzdHptelY0VFM5UzcxaUZzRFhFMVdub05IMmNwb2yDg2I9PWcuc3RhdHVzZWRyYWZ0g2NhbGxpLnJldmlld2Vyg2RsaWtlZi5lbWFpbG0qQGV4YW1wbGUuY29tg2NhbnllLnRhZ3OCYm9ygoNiPT1hLmRuZXdzg2I9PWEuZXByZXNzY3N1Yng4ZGlkOmtleTp6Nk1rdEExdUJkQ3BxNHVKQnFFOWpqTWlMeXhaQmc5YTZ4Z1BQS0pqTXFzczZaYzJkbWV0YaBlbm9uY2VMAAECAwQFBgcICQoL" + + cborBytes, err := base64.StdEncoding.DecodeString(cborBase64) + printThenPanicOnErr(err) tkn, c, err := delegation.FromSealed(cborBytes) printThenPanicOnErr(err) @@ -231,64 +275,34 @@ func ExampleToken_FromSealed() { fmt.Println("Audience (aud):", tkn.Audience().String()) fmt.Println("Subject (sub):", tkn.Subject().String()) fmt.Println("Command (cmd):", tkn.Command().String()) - fmt.Println("Policy (pol): TODO") + fmt.Println("Policy (pol):", tkn.Policy().String()) fmt.Println("Nonce (nonce):", hex.EncodeToString(tkn.Nonce())) - fmt.Println("Meta (meta): TODO") + fmt.Println("Meta (meta):", tkn.Meta().String()) fmt.Println("NotBefore (nbf):", tkn.NotBefore()) fmt.Println("Expiration (exp):", tkn.Expiration()) // Output: - // DAG-CBOR (base64) in: glhAmnAkgfjAx4SA5pzJmtaHRJtTGNpF1y6oqb4yhGoM2H2EUGbBYT4rVDjMKBgCjhdGHjipm00L8iR5SsQh3sIEBaJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cPZjaXNzeDhkaWQ6a2V5Ono2TWtwem4ybjNaR1QyVmFxTUdTUUMzdHptelY0VFM5UzcxaUZzRFhFMVdub05IMmNwb2yDg2I9PWcuc3RhdHVzZWRyYWZ0g2NhbGxpLnJldmlld2Vyg2RsaWtlZi5lbWFpbG0qQGV4YW1wbGUuY29tg2NhbnllLnRhZ3OCYm9ygoNiPT1hLmRuZXdzg2I9PWEuZXByZXNzY3N1Yng4ZGlkOmtleTp6Nk1rdEExdUJkQ3BxNHVKQnFFOWpqTWlMeXhaQmc5YTZ4Z1BQS0pqTXFzczZaYzJkbWV0YaBlbm9uY2VMAAECAwQFBgcICQoL // CID (base58BTC): zdpuAw26pFuvZa2Z9YAtpZZnWN6VmnRFr7Z8LVY5c7RVWoxGY // Issuer (iss): did:key:z6Mkpzn2n3ZGT2VaqMGSQC3tzmzV4TS9S71iFsDXE1WnoNH2 // Audience (aud): did:key:z6Mkq5YmbJcTrPExNDi26imrTCpKhepjBFBSHqrBDN2ArPkv // Subject (sub): did:key:z6MktA1uBdCpq4uJBqE9jjMiLyxZBg9a6xgPPKJjMqss6Zc2 // Command (cmd): /foo/bar - // Policy (pol): TODO + // Policy (pol): [ + // ["==", ".status", "draft"], + // ["all", ".reviewer", + // ["like", ".email", "*@example.com"]], + // ["any", ".tags", + // ["or", [ + // ["==", ".", "news"], + // ["==", ".", "press"]]] + // ] + // ] // Nonce (nonce): 000102030405060708090a0b - // Meta (meta): TODO + // Meta (meta): {} // NotBefore (nbf): // Expiration (exp): } -func exampleCBORData() []byte { - data, err := base64.StdEncoding.DecodeString("glhAmnAkgfjAx4SA5pzJmtaHRJtTGNpF1y6oqb4yhGoM2H2EUGbBYT4rVDjMKBgCjhdGHjipm00L8iR5SsQh3sIEBaJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cPZjaXNzeDhkaWQ6a2V5Ono2TWtwem4ybjNaR1QyVmFxTUdTUUMzdHptelY0VFM5UzcxaUZzRFhFMVdub05IMmNwb2yDg2I9PWcuc3RhdHVzZWRyYWZ0g2NhbGxpLnJldmlld2Vyg2RsaWtlZi5lbWFpbG0qQGV4YW1wbGUuY29tg2NhbnllLnRhZ3OCYm9ygoNiPT1hLmRuZXdzg2I9PWEuZXByZXNzY3N1Yng4ZGlkOmtleTp6Nk1rdEExdUJkQ3BxNHVKQnFFOWpqTWlMeXhaQmc5YTZ4Z1BQS0pqTXFzczZaYzJkbWV0YaBlbm9uY2VMAAECAwQFBgcICQoL") - printThenPanicOnErr(err) - - return data -} - -func exampleDID(didStr string) did.DID { - id, err := did.Parse(didStr) - printThenPanicOnErr(err) - - return id -} - -func exampleCommand(cmdStr string) command.Command { - cmd, err := command.Parse(cmdStr) - printThenPanicOnErr(err) - - return cmd -} - -func examplePolicy(policyJSON string) policy.Policy { - pol, err := policy.FromDagJson(policyJSON) - printThenPanicOnErr(err) - - return pol -} - -func examplePrivKey(privKeyCfg string) crypto.PrivKey { - privKeyMar, err := crypto.ConfigDecodeKey(privKeyCfg) - printThenPanicOnErr(err) - - privKey, err := crypto.UnmarshalPrivateKey(privKeyMar) - printThenPanicOnErr(err) - - return privKey -} - func printCIDAndSealed(id cid.Cid, data []byte) { fmt.Println("CID (base58BTC):", envelope.CIDToBase58BTC(id)) fmt.Println("DAG-CBOR (base64) out:", base64.StdEncoding.EncodeToString(data)) diff --git a/token/delegation/options.go b/token/delegation/options.go index 83ffa03..c8ed9e5 100644 --- a/token/delegation/options.go +++ b/token/delegation/options.go @@ -24,6 +24,15 @@ func WithExpiration(exp time.Time) Option { } } +// WithExpirationAfter set's the Token's optional "expiration" field to Now() plus the given duration. +func WithExpirationAfter(exp time.Duration) Option { + return func(t *Token) error { + expTime := time.Now().Add(exp) + t.expiration = &expTime + return nil + } +} + // WithMeta adds a key/value pair in the "meta" field. // // WithMeta can be used multiple times in the same call. @@ -52,7 +61,7 @@ func WithNotBefore(nbf time.Time) Option { // provided did.DID. // // This Option should only be used with the New constructor - since -// Subject is a required parameter when creating a Token via the Root +// Subject is a required parameter when creating a Token via the Root // constructor, any value provided via this Option will be silently // overwritten. func WithSubject(sub did.DID) Option {