feat(delegation): Rename View -> Token and make immutable (unexported fields and accessors)

This commit is contained in:
Steve Moyer
2024-09-18 14:21:53 -04:00
parent abe8a8150a
commit f44b6ec2c3
2 changed files with 105 additions and 56 deletions

View File

@@ -16,8 +16,8 @@ import (
// Encode marshals a View to the format specified by the provided
// codec.Encoder.
func (d *View) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, error) {
node, err := d.ToIPLD(privKey)
func (t *Token) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, error) {
node, err := t.ToIPLD(privKey)
if err != nil {
return nil, err
}
@@ -26,8 +26,8 @@ func (d *View) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, erro
}
// EncodeWriter is the same as Encode but accepts an io.Writer.
func (d *View) EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.Encoder) error {
node, err := d.ToIPLD(privKey)
func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.Encoder) error {
node, err := t.ToIPLD(privKey)
if err != nil {
return err
}
@@ -36,68 +36,68 @@ func (d *View) EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.Enc
}
// ToDagCbor marshals the View to the DAG-CBOR format.
func (d *View) ToDagCbor(privKey crypto.PrivKey) ([]byte, error) {
return d.Encode(privKey, dagcbor.Encode)
func (t *Token) ToDagCbor(privKey crypto.PrivKey) ([]byte, error) {
return t.Encode(privKey, dagcbor.Encode)
}
// ToDagCborWriter is the same as ToDagCbor but it accepts an io.Writer.
func (d *View) ToDagCborWriter(w io.Writer, privKey crypto.PrivKey) error {
return d.EncodeWriter(w, privKey, dagcbor.Encode)
func (t *Token) ToDagCborWriter(w io.Writer, privKey crypto.PrivKey) error {
return t.EncodeWriter(w, privKey, dagcbor.Encode)
}
// ToDagJson marshals the View to the DAG-JSON format.
func (d *View) ToDagJson(privKey crypto.PrivKey) ([]byte, error) {
return d.Encode(privKey, dagjson.Encode)
func (t *Token) ToDagJson(privKey crypto.PrivKey) ([]byte, error) {
return t.Encode(privKey, dagjson.Encode)
}
// ToDagJsonWriter is the same as ToDagJson but it accepts an io.Writer.
func (d *View) ToDagJsonWriter(w io.Writer, privKey crypto.PrivKey) error {
return d.EncodeWriter(w, privKey, dagjson.Encode)
func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivKey) error {
return t.EncodeWriter(w, privKey, dagjson.Encode)
}
// ToIPLD wraps the View in an IPLD datamodel.Node.
func (d *View) ToIPLD(privKey crypto.PrivKey) (datamodel.Node, error) {
func (t *Token) ToIPLD(privKey crypto.PrivKey) (datamodel.Node, error) {
var sub *string
if d.Subject != did.Undef {
s := d.Subject.String()
if t.subject != did.Undef {
s := t.subject.String()
sub = &s
}
pol, err := d.Policy.ToIPLD()
pol, err := t.policy.ToIPLD()
if err != nil {
return nil, err
}
metaKeys := make([]string, len(d.Meta))
metaKeys := make([]string, len(t.meta))
i := 0
for k := range d.Meta {
for k := range t.meta {
metaKeys[i] = k
i++
}
var nbf *int64
if d.NotBefore != nil {
u := d.NotBefore.Unix()
if t.notBefore != nil {
u := t.notBefore.Unix()
nbf = &u
}
var exp *int64
if d.Expiration != nil {
u := d.Expiration.Unix()
if t.expiration != nil {
u := t.expiration.Unix()
exp = &u
}
model := &tokenPayloadModel{
Iss: d.Issuer.String(),
Aud: d.Audience.String(),
Iss: t.issuer.String(),
Aud: t.audience.String(),
Sub: sub,
Cmd: d.Command.String(),
Cmd: t.command.String(),
Pol: pol,
Nonce: d.Nonce,
Nonce: t.nonce,
Meta: metaModel{
Keys: metaKeys,
Values: d.Meta,
Values: t.meta,
},
Nbf: nbf,
Exp: exp,
@@ -111,7 +111,7 @@ func (d *View) ToIPLD(privKey crypto.PrivKey) (datamodel.Node, error) {
//
// An error is returned if the conversion fails, or if the resulting
// View is invalid.
func Decode(b []byte, decFn codec.Decoder) (*View, error) {
func Decode(b []byte, decFn codec.Decoder) (*Token, error) {
node, err := ipld.Decode(b, decFn)
if err != nil {
return nil, err
@@ -120,7 +120,7 @@ func Decode(b []byte, decFn codec.Decoder) (*View, error) {
}
// DecodeReader is the same as Decode, but accept an io.Reader.
func DecodeReader(r io.Reader, decFn codec.Decoder) (*View, error) {
func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, error) {
node, err := ipld.DecodeStreaming(r, decFn)
if err != nil {
return nil, err
@@ -132,12 +132,12 @@ func DecodeReader(r io.Reader, decFn codec.Decoder) (*View, error) {
//
// An error is returned if the conversion fails, or if the resulting
// View is invalid.
func FromDagCbor(data []byte) (*View, error) {
func FromDagCbor(data []byte) (*Token, error) {
return Decode(data, dagcbor.Decode)
}
// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader.
func FromDagCborReader(r io.Reader) (*View, error) {
func FromDagCborReader(r io.Reader) (*Token, error) {
return DecodeReader(r, dagcbor.Decode)
}
@@ -145,12 +145,12 @@ func FromDagCborReader(r io.Reader) (*View, error) {
//
// An error is returned if the conversion fails, or if the resulting
// View is invalid.
func FromDagJson(data []byte) (*View, error) {
func FromDagJson(data []byte) (*Token, error) {
return Decode(data, dagjson.Decode)
}
// FromDagJsonReader is the same as FromDagJson, but accept an io.Reader.
func FromDagJsonReader(r io.Reader) (*View, error) {
func FromDagJsonReader(r io.Reader) (*Token, error) {
return DecodeReader(r, dagjson.Decode)
}
@@ -158,7 +158,7 @@ func FromDagJsonReader(r io.Reader) (*View, error) {
//
// An error is returned if the conversion fails, or if the resulting
// View is invalid.
func FromIPLD(node datamodel.Node) (*View, error) {
func FromIPLD(node datamodel.Node) (*Token, error) {
tkn, _, err := envelope.FromIPLD[*tokenPayloadModel](node) // TODO add CID to view
if err != nil {
return nil, err

View File

@@ -11,58 +11,107 @@ import (
"github.com/ucan-wg/go-ucan/did"
)
type View struct {
type Token struct {
// Issuer DID (sender)
Issuer did.DID
issuer did.DID
// Audience DID (receiver)
Audience did.DID
audience did.DID
// Principal that the chain is about (the Subject)
Subject did.DID
subject did.DID
// The Command to eventually invoke
Command *command.Command
command *command.Command
// The delegation policy
Policy policy.Policy
policy policy.Policy
// A unique, random nonce
Nonce []byte
nonce []byte
// Arbitrary Metadata
Meta map[string]datamodel.Node
meta map[string]datamodel.Node
// "Not before" UTC Unix Timestamp in seconds (valid from), 53-bits integer
NotBefore *time.Time
notBefore *time.Time
// The timestamp at which the Invocation becomes invalid
Expiration *time.Time
expiration *time.Time
}
// Issuer returns the did.DID representing the Token's issuer.
func (t *Token) Issuer() did.DID {
return t.issuer
}
// Audience returns the did.DID representing the Token's audience.
func (t *Token) Audience() did.DID {
return t.audience
}
// Subject returns the did.DID representing the Token's subject.
//
// This field may be did.Undef for delegations that are [Powerlined] but
// must be equal to the value returned by the Issuer method for root
// tokens.
func (t *Token) Subject() did.DID {
return t.subject
}
// Command returns the capability's command.Command.
func (t *Token) Command() *command.Command {
return t.command
}
// Policy returns the capability's policy.Policy.
func (t *Token) Policy() policy.Policy {
return t.policy
}
// Nonce returns the random Nonce encapsulated in this Token.
func (t *Token) Nonce() []byte {
return t.nonce
}
// Meta returns the Token's metadata.
func (t *Token) Meta() map[string]datamodel.Node {
return t.meta
}
// NotBefore returns the time at which the Token becomes "active".
func (t *Token) NotBefore() *time.Time {
return t.notBefore
}
// Expiration returns the time at which the Token expires.
func (t *Token) Expiration() *time.Time {
return t.expiration
}
// viewFromModel build a decoded view of the raw IPLD data.
// This function also serves as validation.
func viewFromModel(m tokenPayloadModel) (*View, error) {
var view View
func viewFromModel(m tokenPayloadModel) (*Token, error) {
var view Token
var err error
view.Issuer, err = did.Parse(m.Iss)
view.issuer, err = did.Parse(m.Iss)
if err != nil {
return nil, fmt.Errorf("parse iss: %w", err)
}
view.Audience, err = did.Parse(m.Aud)
view.audience, err = did.Parse(m.Aud)
if err != nil {
return nil, fmt.Errorf("parse audience: %w", err)
}
if m.Sub != nil {
view.Subject, err = did.Parse(*m.Sub)
view.subject, err = did.Parse(*m.Sub)
if err != nil {
return nil, fmt.Errorf("parse subject: %w", err)
}
} else {
view.Subject = did.Undef
view.subject = did.Undef
}
view.Command, err = command.Parse(m.Cmd)
view.command, err = command.Parse(m.Cmd)
if err != nil {
return nil, fmt.Errorf("parse command: %w", err)
}
view.Policy, err = policy.FromIPLD(m.Pol)
view.policy, err = policy.FromIPLD(m.Pol)
if err != nil {
return nil, fmt.Errorf("parse policy: %w", err)
}
@@ -70,19 +119,19 @@ func viewFromModel(m tokenPayloadModel) (*View, error) {
if len(m.Nonce) == 0 {
return nil, fmt.Errorf("nonce is required")
}
view.Nonce = m.Nonce
view.nonce = m.Nonce
// TODO: copy?
view.Meta = m.Meta.Values
view.meta = m.Meta.Values
if m.Nbf != nil {
t := time.Unix(*m.Nbf, 0)
view.NotBefore = &t
view.notBefore = &t
}
if m.Exp != nil {
t := time.Unix(*m.Exp, 0)
view.Expiration = &t
view.expiration = &t
}
return &view, nil