Skip to content

Commit

Permalink
feat(delegation): add validation/accessors
Browse files Browse the repository at this point in the history
  • Loading branch information
smoyer64 committed Sep 16, 2024
1 parent da9f2e7 commit f85ece4
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 58 deletions.
30 changes: 0 additions & 30 deletions delegation/delegatiom_options.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

141 changes: 113 additions & 28 deletions delegation/delegation.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package delegation

import (
"crypto/rand"
"errors"
"fmt"
"time"
Expand All @@ -23,30 +24,22 @@ type Delegation struct {
}

//go:generate -command options go run github.com/selesy/go-options
//go:generate options -type=config -prefix=With -output=delegatiom_options.go -cmp=false -stringer=false -imports=time,github.com/ucan-wg/go-ucan/did,github.com/ipld/go-ipld-prime/datamodel
//go:generate options -type=config -prefix=With -output=delegatiom_options.go -cmp=false -stringer=false -imports=time,github.com/ipld/go-ipld-prime/datamodel

type config struct {
Expiration *time.Time
Meta map[string]datamodel.Node
NoExpiration bool
NotBefore *time.Time
// is a did.DID representing the Subject.
Subject *did.DID
Powerline bool
Meta map[string]datamodel.Node
NotBefore *time.Time
}

// Required fields for delegation

// Requirements for root

func New(privKey crypto.PrivKey, iss did.DID, aud did.DID, cmd *command.Command, pol *policy.Policy, exp *time.Time, nonce []byte, opts ...Option) (*Delegation, error) {
func New(privKey crypto.PrivKey, aud did.DID, sub *did.DID, cmd *command.Command, pol policy.Policy, nonce []byte, exp *time.Time, opts ...Option) (*Delegation, error) {
cfg, err := newConfig(opts...)
if err != nil {
return nil, err
}

if !iss.Defined() {
return nil, fmt.Errorf("%w: %s", token.ErrMissingRequiredDID, "iss")
issuer, err := did.FromPrivKey(privKey)
if err != nil {
return nil, err
}

if !aud.Defined() {
Expand All @@ -55,8 +48,8 @@ func New(privKey crypto.PrivKey, iss did.DID, aud did.DID, cmd *command.Command,
audience := aud.String()

var subject *string
if cfg.Subject != nil && cfg.Subject.Defined() {
s := cfg.Subject.String()
if sub != nil {
s := sub.String()
subject = &s
}

Expand All @@ -65,28 +58,26 @@ func New(privKey crypto.PrivKey, iss did.DID, aud did.DID, cmd *command.Command,
return nil, err
}

nonce = []uint8(nonce)
var meta *token.Map__String__Any
if len(cfg.Meta) > 0 {
m := token.ToIPLDMapStringAny(cfg.Meta)
meta = &m
}

var notBefore *int
if cfg.NotBefore != nil {
n := int(cfg.NotBefore.Unix())
notBefore = &n
}

var meta *token.Map__String__Any
if len(cfg.Meta) > 0 {
m := token.ToIPLDMapStringAny(cfg.Meta)
meta = &m
}

var expiration *int
if exp != nil && !cfg.NoExpiration {
e := int(cfg.NotBefore.Unix())
if exp != nil {
e := int(exp.Unix())
expiration = &e
}

tkn := &token.Token{
Issuer: iss.String(),
Issuer: issuer.String(),
Audience: &audience,
Subject: subject,
Command: cmd.String(),
Expand All @@ -111,16 +102,82 @@ func New(privKey crypto.PrivKey, iss did.DID, aud did.DID, cmd *command.Command,
return dlg, nil
}

type validateFunc func() error
func Root(privKey crypto.PrivKey, aud did.DID, cmd *command.Command, pol policy.Policy, nonce []byte, exp *time.Time, opts ...Option) (*Delegation, error) {
sub, err := did.FromPrivKey(privKey)
if err != nil {
return nil, err
}

return New(privKey, aud, &sub, cmd, pol, nonce, exp, opts...)
}

func (d *Delegation) Audience() did.DID {
id, _ := did.Parse(*d.envel.TokenPayload().Audience)

return id
}

func (d *Delegation) Command() *command.Command {
cmd, _ := command.Parse(d.envel.TokenPayload().Command)

return cmd
}

func (d *Delegation) IsPowerline() bool {
return d.envel.TokenPayload().Subject == nil
}

func (d *Delegation) IsRoot() bool {
return &d.envel.TokenPayload().Issuer == d.envel.TokenPayload().Subject
}

func (d *Delegation) Issuer() did.DID {
id, _ := did.Parse(d.envel.TokenPayload().Issuer)

return id
}

func (d *Delegation) Meta() map[string]datamodel.Node {
return d.envel.TokenPayload().Meta.Values
}

func (d *Delegation) Nonce() []byte {
return *d.envel.TokenPayload().Nonce
}

func (d *Delegation) Policy() policy.Policy {
pol, _ := policy.FromIPLD(*d.envel.TokenPayload().Policy)

return pol
}

func (d *Delegation) Subject() *did.DID {
if d.envel.TokenPayload().Subject == nil {
return nil
}

id, _ := did.Parse(*d.envel.TokenPayload().Subject)

return &id
}

func (d *Delegation) Validate() error {
return errors.Join(
d.validateDID("iss", &d.envel.TokenPayload().Issuer, false),
d.validateDID("aud", d.envel.TokenPayload().Audience, false),
d.validateDID("sub", d.envel.TokenPayload().Subject, true),
d.validateCommand(),
d.validatePolicy(),
d.validateNonce(),
)
}

func (d *Delegation) validateCommand() error {
_, err := command.Parse(d.envel.TokenPayload().Command)

return err
}

func (d *Delegation) validateDID(fieldName string, identity *string, nullableOrOptional bool) error {
if identity == nil && !nullableOrOptional {
return fmt.Errorf("a required DID is missing: %s", fieldName)
Expand All @@ -137,3 +194,31 @@ func (d *Delegation) validateDID(fieldName string, identity *string, nullableOrO

return nil
}

func (d *Delegation) validateNonce() error {
if d.envel.TokenPayload().Nonce == nil || len(*d.envel.TokenPayload().Nonce) < 1 {
return fmt.Errorf("nonce is required: must not be nil or empty")
}

return nil
}

func (d *Delegation) validatePolicy() error {
if d.envel.TokenPayload().Policy == nil {
return fmt.Errorf("the \"pol\" field is required")
}

_, err := policy.FromIPLD(*d.envel.TokenPayload().Policy)

return err
}

func Nonce() ([]byte, error) {
nonce := make([]byte, 32)

if _, err := rand.Read(nonce); err != nil {
return nil, err
}

return nonce, nil
}
Loading

0 comments on commit f85ece4

Please sign in to comment.