mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
refactor/internal (#1216)
* refactor: update import paths in gateway handlers * refactor: remove obsolete devtools Makefile and README * build: optimize build process for improved efficiency * refactor: remove obsolete pkl files related to Matrix and Sonr network configurations * refactor: move embed code to x/dwn/types
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
# UCAN Tokens in Go
|
||||
|
||||

|
||||
|
||||
Originally by @b5 as one of the first from scratch implementations of UCAN outside of the Fission teams initial work in TypeScript / Haskell.
|
||||
|
||||
**If you're interested in updating this codebase to the 1.0 version of the UCAN spec, [get involved in the discussion »](https://github.com/orgs/ucan-wg/discussions/163)**
|
||||
|
||||
## About UCAN Tokens
|
||||
|
||||
User Controlled Authorization Networks (UCANs) are a way of doing authorization where users are fully in control. OAuth is designed for a centralized world, UCAN is the distributed user controlled version.
|
||||
|
||||
### UCAN Gopher
|
||||
|
||||

|
||||
|
||||
Artwork by [Bruno Monts](https://www.instagram.com/bruno_monts). Thank you [Renee French](http://reneefrench.blogspot.com/) for creating the [Go Gopher](https://blog.golang.org/gopher)
|
||||
@@ -1,142 +0,0 @@
|
||||
package ucan
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Attenuations is a list of attenuations
|
||||
type Attenuations []Attenuation
|
||||
|
||||
func (att Attenuations) String() string {
|
||||
str := ""
|
||||
for _, a := range att {
|
||||
str += fmt.Sprintf("%s\n", a)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// Contains is true if all attenuations in b are contained
|
||||
func (att Attenuations) Contains(b Attenuations) bool {
|
||||
// fmt.Printf("%scontains\n%s?\n\n", att, b)
|
||||
LOOP:
|
||||
for _, bb := range b {
|
||||
for _, aa := range att {
|
||||
if aa.Contains(bb) {
|
||||
// fmt.Printf("%s contains %s\n", aa, bb)
|
||||
continue LOOP
|
||||
} else if aa.Rsc.Contains(bb.Rsc) {
|
||||
// fmt.Printf("%s < %s\n", aa, bb)
|
||||
// fmt.Printf("rscEq:%t rscContains: %t capContains:%t\n", aa.Rsc.Type() == bb.Rsc.Type(), aa.Rsc.Contains(bb.Rsc), aa.Cap.Contains(bb.Cap))
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AttenuationConstructorFunc is a function that creates an attenuation from a map
|
||||
// Users of this package provide an Attenuation Constructor to the parser to
|
||||
// bind attenuation logic to a UCAN
|
||||
type AttenuationConstructorFunc func(v map[string]interface{}) (Attenuation, error)
|
||||
|
||||
// Attenuation is a capability on a resource
|
||||
type Attenuation struct {
|
||||
Cap Capability
|
||||
Rsc Resource
|
||||
}
|
||||
|
||||
// String formats an attenuation as a string
|
||||
func (a Attenuation) String() string {
|
||||
return fmt.Sprintf("cap:%s %s:%s", a.Cap, a.Rsc.Type(), a.Rsc.Value())
|
||||
}
|
||||
|
||||
// Contains returns true if both
|
||||
func (a Attenuation) Contains(b Attenuation) bool {
|
||||
return a.Rsc.Contains(b.Rsc) && a.Cap.Contains(b.Cap)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaller interface
|
||||
func (a Attenuation) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]interface{}{
|
||||
a.Rsc.Type(): a.Rsc.Value(),
|
||||
CapKey: a.Cap.String(),
|
||||
})
|
||||
}
|
||||
|
||||
// Resource is a unique identifier for a thing, usually stored state. Resources
|
||||
// are organized by string types
|
||||
type Resource interface {
|
||||
Type() string
|
||||
Value() string
|
||||
Contains(b Resource) bool
|
||||
}
|
||||
|
||||
// Capability is an action users can perform
|
||||
type Capability interface {
|
||||
// A Capability must be expressable as a string
|
||||
String() string
|
||||
// Capabilities must be comparable to other same-type capabilities
|
||||
Contains(b Capability) bool
|
||||
}
|
||||
|
||||
// NestedCapabilities is a basic implementation of the Capabilities interface
|
||||
// based on a hierarchal list of strings ordered from most to least capable
|
||||
// It is both a capability and a capability factory with the .Cap method
|
||||
type NestedCapabilities struct {
|
||||
cap string
|
||||
idx int
|
||||
hierarchy *[]string
|
||||
}
|
||||
|
||||
// assert at compile-time NestedCapabilities implements Capability
|
||||
var _ Capability = (*NestedCapabilities)(nil)
|
||||
|
||||
// NewNestedCapabilities creates a set of NestedCapabilities
|
||||
func NewNestedCapabilities(strs ...string) NestedCapabilities {
|
||||
return NestedCapabilities{
|
||||
cap: strs[0],
|
||||
idx: 0,
|
||||
hierarchy: &strs,
|
||||
}
|
||||
}
|
||||
|
||||
// Cap creates a new capability from the hierarchy
|
||||
func (nc NestedCapabilities) Cap(str string) Capability {
|
||||
idx := -1
|
||||
for i, c := range *nc.hierarchy {
|
||||
if c == str {
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if idx == -1 {
|
||||
panic(fmt.Sprintf("%s is not a nested capability. must be one of: %v", str, *nc.hierarchy))
|
||||
}
|
||||
|
||||
return NestedCapabilities{
|
||||
cap: str,
|
||||
idx: idx,
|
||||
hierarchy: nc.hierarchy,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the Capability value as a string
|
||||
func (nc NestedCapabilities) String() string {
|
||||
return nc.cap
|
||||
}
|
||||
|
||||
// Contains returns true if cap is equal or less than the NestedCapability value
|
||||
func (nc NestedCapabilities) Contains(cap Capability) bool {
|
||||
str := cap.String()
|
||||
for i, c := range *nc.hierarchy {
|
||||
if c == str {
|
||||
if i >= nc.idx {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package attns
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/apple/pkl-go/pkl"
|
||||
)
|
||||
|
||||
type UCAN struct {
|
||||
}
|
||||
|
||||
// LoadFromPath loads the pkl module at the given path and evaluates it into a UCAN
|
||||
func LoadFromPath(ctx context.Context, path string) (ret *UCAN, err error) {
|
||||
evaluator, err := pkl.NewEvaluator(ctx, pkl.PreconfiguredOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
cerr := evaluator.Close()
|
||||
if err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
ret, err = Load(ctx, evaluator, pkl.FileSource(path))
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Load loads the pkl module at the given source and evaluates it with the given evaluator into a UCAN
|
||||
func Load(ctx context.Context, evaluator pkl.Evaluator, source *pkl.ModuleSource) (*UCAN, error) {
|
||||
var ret UCAN
|
||||
if err := evaluator.EvaluateModule(ctx, source, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ret, nil
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package capaccount
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CapAccount string
|
||||
|
||||
const (
|
||||
ExecBroadcast CapAccount = "exec/broadcast"
|
||||
ExecQuery CapAccount = "exec/query"
|
||||
ExecSimulate CapAccount = "exec/simulate"
|
||||
ExecVote CapAccount = "exec/vote"
|
||||
ExecDelegate CapAccount = "exec/delegate"
|
||||
ExecInvoke CapAccount = "exec/invoke"
|
||||
ExecSend CapAccount = "exec/send"
|
||||
)
|
||||
|
||||
// String returns the string representation of CapAccount
|
||||
func (rcv CapAccount) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(CapAccount)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for CapAccount.
|
||||
func (rcv *CapAccount) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "exec/broadcast":
|
||||
*rcv = ExecBroadcast
|
||||
case "exec/query":
|
||||
*rcv = ExecQuery
|
||||
case "exec/simulate":
|
||||
*rcv = ExecSimulate
|
||||
case "exec/vote":
|
||||
*rcv = ExecVote
|
||||
case "exec/delegate":
|
||||
*rcv = ExecDelegate
|
||||
case "exec/invoke":
|
||||
*rcv = ExecInvoke
|
||||
case "exec/send":
|
||||
*rcv = ExecSend
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid CapAccount`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package capaccount
|
||||
|
||||
import "github.com/onsonr/sonr/crypto/ucan"
|
||||
|
||||
func NewCap(ty CapAccount) ucan.Capability {
|
||||
return ucan.Capability(ty)
|
||||
}
|
||||
|
||||
func (c CapAccount) Contains(b ucan.Capability) bool {
|
||||
return c.String() == b.String()
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package capinterchain
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CapInterchain string
|
||||
|
||||
const (
|
||||
TransferSwap CapInterchain = "transfer/swap"
|
||||
TransferSend CapInterchain = "transfer/send"
|
||||
TransferAtomic CapInterchain = "transfer/atomic"
|
||||
TransferBatch CapInterchain = "transfer/batch"
|
||||
TransferP2p CapInterchain = "transfer/p2p"
|
||||
)
|
||||
|
||||
// String returns the string representation of CapInterchain
|
||||
func (rcv CapInterchain) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(CapInterchain)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for CapInterchain.
|
||||
func (rcv *CapInterchain) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "transfer/swap":
|
||||
*rcv = TransferSwap
|
||||
case "transfer/send":
|
||||
*rcv = TransferSend
|
||||
case "transfer/atomic":
|
||||
*rcv = TransferAtomic
|
||||
case "transfer/batch":
|
||||
*rcv = TransferBatch
|
||||
case "transfer/p2p":
|
||||
*rcv = TransferP2p
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid CapInterchain`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package capinterchain
|
||||
|
||||
import "github.com/onsonr/sonr/crypto/ucan"
|
||||
|
||||
func NewCap(ty CapInterchain) ucan.Capability {
|
||||
return ucan.Capability(ty)
|
||||
}
|
||||
|
||||
func (c CapInterchain) Contains(b ucan.Capability) bool {
|
||||
return c.String() == b.String()
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package capvault
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CapVault string
|
||||
|
||||
const (
|
||||
CrudAsset CapVault = "crud/asset"
|
||||
CrudAuthzgrant CapVault = "crud/authzgrant"
|
||||
CrudProfile CapVault = "crud/profile"
|
||||
CrudRecord CapVault = "crud/record"
|
||||
UseRecovery CapVault = "use/recovery"
|
||||
UseSync CapVault = "use/sync"
|
||||
UseSigner CapVault = "use/signer"
|
||||
)
|
||||
|
||||
// String returns the string representation of CapVault
|
||||
func (rcv CapVault) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(CapVault)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for CapVault.
|
||||
func (rcv *CapVault) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "crud/asset":
|
||||
*rcv = CrudAsset
|
||||
case "crud/authzgrant":
|
||||
*rcv = CrudAuthzgrant
|
||||
case "crud/profile":
|
||||
*rcv = CrudProfile
|
||||
case "crud/record":
|
||||
*rcv = CrudRecord
|
||||
case "use/recovery":
|
||||
*rcv = UseRecovery
|
||||
case "use/sync":
|
||||
*rcv = UseSync
|
||||
case "use/signer":
|
||||
*rcv = UseSigner
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid CapVault`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package capvault
|
||||
|
||||
import "github.com/onsonr/sonr/crypto/ucan"
|
||||
|
||||
func NewCap(ty CapVault) ucan.Capability {
|
||||
return ucan.Capability(ty)
|
||||
}
|
||||
|
||||
func (c CapVault) Contains(b ucan.Capability) bool {
|
||||
return c.String() == b.String()
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
// Package attns implements the UCAN resource and capability types
|
||||
package attns
|
||||
|
||||
import (
|
||||
"github.com/onsonr/sonr/crypto/ucan"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/capaccount"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/capinterchain"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/capvault"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/resaccount"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/resinterchain"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/resvault"
|
||||
)
|
||||
|
||||
// Capability hierarchy for sonr network
|
||||
// -------------------------------------
|
||||
// VAULT (DWN)
|
||||
//
|
||||
// └─ CRUD/ASSET
|
||||
// └─ CRUD/AUTHZGRANT
|
||||
// └─ CRUD/PROFILE
|
||||
// └─ CRUD/RECORD
|
||||
// └─ USE/RECOVERY
|
||||
// └─ USE/SYNC
|
||||
// └─ USE/SIGNER
|
||||
//
|
||||
// ACCOUNT (DID)
|
||||
//
|
||||
// └─ EXEC/BROADCAST
|
||||
// └─ EXEC/QUERY
|
||||
// └─ EXEC/SIMULATE
|
||||
// └─ EXEC/VOTE
|
||||
// └─ EXEC/DELEGATE
|
||||
// └─ EXEC/INVOKE
|
||||
// └─ EXEC/SEND
|
||||
//
|
||||
// INTERCHAIN
|
||||
//
|
||||
// └─ TRANSFER/SWAP
|
||||
// └─ TRANSFER/SEND
|
||||
// └─ TRANSFER/ATOMIC
|
||||
// └─ TRANSFER/BATCH
|
||||
// └─ TRANSFER/P2P
|
||||
// └─ TRANSFER/SEND
|
||||
|
||||
type Capability string
|
||||
|
||||
const (
|
||||
CapExecBroadcast = capaccount.ExecBroadcast
|
||||
CapExecQuery = capaccount.ExecQuery
|
||||
CapExecSimulate = capaccount.ExecSimulate
|
||||
CapExecVote = capaccount.ExecVote
|
||||
CapExecDelegate = capaccount.ExecDelegate
|
||||
CapExecInvoke = capaccount.ExecInvoke
|
||||
CapExecSend = capaccount.ExecSend
|
||||
|
||||
CapTransferSwap = capinterchain.TransferSwap
|
||||
CapTransferSend = capinterchain.TransferSend
|
||||
CapTransferAtomic = capinterchain.TransferAtomic
|
||||
CapTransferBatch = capinterchain.TransferBatch
|
||||
CapTransferP2P = capinterchain.TransferP2p
|
||||
|
||||
CapCrudAsset = capvault.CrudAsset
|
||||
CapCrudAuthzgrant = capvault.CrudAuthzgrant
|
||||
CapCrudProfile = capvault.CrudProfile
|
||||
CapCrudRecord = capvault.CrudRecord
|
||||
CapUseRecovery = capvault.UseRecovery
|
||||
CapUseSync = capvault.UseSync
|
||||
CapUseSigner = capvault.UseSigner
|
||||
)
|
||||
|
||||
type NewCapFunc func(string) ucan.Capability
|
||||
|
||||
type BuildResourceFunc func(string, string) ucan.Resource
|
||||
|
||||
func CreateArray(attns ...ucan.Attenuation) ucan.Attenuations {
|
||||
return ucan.Attenuations(attns)
|
||||
}
|
||||
|
||||
func New(cap ucan.Capability, rsc ucan.Resource) ucan.Attenuation {
|
||||
return ucan.Attenuation{
|
||||
Cap: cap,
|
||||
Rsc: rsc,
|
||||
}
|
||||
}
|
||||
|
||||
// NewAccountCap creates a new account capability
|
||||
func NewAccountCap(ty capaccount.CapAccount) ucan.Capability {
|
||||
return capaccount.NewCap(ty)
|
||||
}
|
||||
|
||||
// NewInterchainCap creates a new interchain capability
|
||||
func NewInterchainCap(ty capinterchain.CapInterchain) ucan.Capability {
|
||||
return capinterchain.NewCap(ty)
|
||||
}
|
||||
|
||||
// NewVaultCap creates a new vault capability
|
||||
func NewVaultCap(ty capvault.CapVault) ucan.Capability {
|
||||
return capvault.NewCap(ty)
|
||||
}
|
||||
|
||||
// BuildAccountResource creates a new account resource
|
||||
func BuildAccountResource(ty resaccount.ResAccount, value string) ucan.Resource {
|
||||
return resaccount.Build(ty, value)
|
||||
}
|
||||
|
||||
// BuildInterchainResource creates a new interchain resource
|
||||
func BuildInterchainResource(ty resinterchain.ResInterchain, value string) ucan.Resource {
|
||||
return resinterchain.Build(ty, value)
|
||||
}
|
||||
|
||||
// BuildVaultResource creates a new vault resource
|
||||
func BuildVaultResource(ty resvault.ResVault, value string) ucan.Resource {
|
||||
return resvault.Build(ty, value)
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package attns
|
||||
|
||||
import "github.com/apple/pkl-go/pkl"
|
||||
|
||||
func init() {
|
||||
pkl.RegisterMapping("sonr.orm.UCAN", UCAN{})
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package resaccount
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ResAccount string
|
||||
|
||||
const (
|
||||
AccSequence ResAccount = "acc/sequence"
|
||||
AccNumber ResAccount = "acc/number"
|
||||
ChainId ResAccount = "chain/id"
|
||||
AssetCode ResAccount = "asset/code"
|
||||
AuthzGrant ResAccount = "authz/grant"
|
||||
)
|
||||
|
||||
// String returns the string representation of ResAccount
|
||||
func (rcv ResAccount) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(ResAccount)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for ResAccount.
|
||||
func (rcv *ResAccount) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "acc/sequence":
|
||||
*rcv = AccSequence
|
||||
case "acc/number":
|
||||
*rcv = AccNumber
|
||||
case "chain/id":
|
||||
*rcv = ChainId
|
||||
case "asset/code":
|
||||
*rcv = AssetCode
|
||||
case "authz/grant":
|
||||
*rcv = AuthzGrant
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid ResAccount`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package resaccount
|
||||
|
||||
import "github.com/onsonr/sonr/crypto/ucan"
|
||||
|
||||
func Build(ty ResAccount, value string) ucan.Resource {
|
||||
return newStringLengthResource(ty.String(), value)
|
||||
}
|
||||
|
||||
type stringLengthRsc struct {
|
||||
t string
|
||||
v string
|
||||
}
|
||||
|
||||
// NewStringLengthResource is a silly implementation of resource to use while
|
||||
// I figure out what an OR filter on strings is. Don't use this.
|
||||
func newStringLengthResource(typ, val string) ucan.Resource {
|
||||
return stringLengthRsc{
|
||||
t: typ,
|
||||
v: val,
|
||||
}
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Type() string {
|
||||
return r.t
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Value() string {
|
||||
return r.v
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Contains(b ucan.Resource) bool {
|
||||
return r.Type() == b.Type() && len(r.Value()) <= len(b.Value())
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package resinterchain
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ResInterchain string
|
||||
|
||||
const (
|
||||
ChannnelPort ResInterchain = "channnel/port"
|
||||
ChainId ResInterchain = "chain/id"
|
||||
ChainName ResInterchain = "chain/name"
|
||||
AccHost ResInterchain = "acc/host"
|
||||
AccController ResInterchain = "acc/controller"
|
||||
)
|
||||
|
||||
// String returns the string representation of ResInterchain
|
||||
func (rcv ResInterchain) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(ResInterchain)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for ResInterchain.
|
||||
func (rcv *ResInterchain) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "channnel/port":
|
||||
*rcv = ChannnelPort
|
||||
case "chain/id":
|
||||
*rcv = ChainId
|
||||
case "chain/name":
|
||||
*rcv = ChainName
|
||||
case "acc/host":
|
||||
*rcv = AccHost
|
||||
case "acc/controller":
|
||||
*rcv = AccController
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid ResInterchain`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package resinterchain
|
||||
|
||||
import "github.com/onsonr/sonr/crypto/ucan"
|
||||
|
||||
func Build(ty ResInterchain, value string) ucan.Resource {
|
||||
return newStringLengthResource(ty.String(), value)
|
||||
}
|
||||
|
||||
type stringLengthRsc struct {
|
||||
t string
|
||||
v string
|
||||
}
|
||||
|
||||
// NewStringLengthResource is a silly implementation of resource to use while
|
||||
// I figure out what an OR filter on strings is. Don't use this.
|
||||
func newStringLengthResource(typ, val string) ucan.Resource {
|
||||
return stringLengthRsc{
|
||||
t: typ,
|
||||
v: val,
|
||||
}
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Type() string {
|
||||
return r.t
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Value() string {
|
||||
return r.v
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Contains(b ucan.Resource) bool {
|
||||
return r.Type() == b.Type() && len(r.Value()) <= len(b.Value())
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package resvault
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ResVault string
|
||||
|
||||
const (
|
||||
KsEnclave ResVault = "ks/enclave"
|
||||
LocCid ResVault = "loc/cid"
|
||||
LocEntity ResVault = "loc/entity"
|
||||
LocIpns ResVault = "loc/ipns"
|
||||
AddrSonr ResVault = "addr/sonr"
|
||||
ChainCode ResVault = "chain/code"
|
||||
)
|
||||
|
||||
// String returns the string representation of ResVault
|
||||
func (rcv ResVault) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(ResVault)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for ResVault.
|
||||
func (rcv *ResVault) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "ks/enclave":
|
||||
*rcv = KsEnclave
|
||||
case "loc/cid":
|
||||
*rcv = LocCid
|
||||
case "loc/entity":
|
||||
*rcv = LocEntity
|
||||
case "loc/ipns":
|
||||
*rcv = LocIpns
|
||||
case "addr/sonr":
|
||||
*rcv = AddrSonr
|
||||
case "chain/code":
|
||||
*rcv = ChainCode
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid ResVault`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package resvault
|
||||
|
||||
import "github.com/onsonr/sonr/crypto/ucan"
|
||||
|
||||
func Build(ty ResVault, value string) ucan.Resource {
|
||||
return newStringLengthResource(ty.String(), value)
|
||||
}
|
||||
|
||||
type stringLengthRsc struct {
|
||||
t string
|
||||
v string
|
||||
}
|
||||
|
||||
// NewStringLengthResource is a silly implementation of resource to use while
|
||||
// I figure out what an OR filter on strings is. Don't use this.
|
||||
func newStringLengthResource(typ, val string) ucan.Resource {
|
||||
return stringLengthRsc{
|
||||
t: typ,
|
||||
v: val,
|
||||
}
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Type() string {
|
||||
return r.t
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Value() string {
|
||||
return r.v
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Contains(b ucan.Resource) bool {
|
||||
return r.Type() == b.Type() && len(r.Value()) <= len(b.Value())
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package ucan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var EmptyAttenuation = Attenuation{
|
||||
Cap: Capability(nil),
|
||||
Rsc: Resource(nil),
|
||||
}
|
||||
|
||||
// Permissions represents the type of attenuation
|
||||
type Permissions string
|
||||
|
||||
const (
|
||||
// AccountPermissions represents the smart account attenuation
|
||||
AccountPermissions = Permissions("account")
|
||||
|
||||
// ServicePermissions represents the service attenuation
|
||||
ServicePermissions = Permissions("service")
|
||||
|
||||
// VaultPermissions represents the vault attenuation
|
||||
VaultPermissions = Permissions("vault")
|
||||
)
|
||||
|
||||
// Cap returns the capability for the given AttenuationPreset
|
||||
func (a Permissions) NewCap(c string) Capability {
|
||||
return a.GetCapabilities().Cap(c)
|
||||
}
|
||||
|
||||
// NestedCapabilities returns the nested capabilities for the given AttenuationPreset
|
||||
func (a Permissions) GetCapabilities() NestedCapabilities {
|
||||
var caps []string
|
||||
switch a {
|
||||
case AccountPermissions:
|
||||
// caps = SmartAccountCapabilities()
|
||||
case VaultPermissions:
|
||||
// caps = VaultCapabilities()
|
||||
}
|
||||
return NewNestedCapabilities(caps...)
|
||||
}
|
||||
|
||||
// Equals returns true if the given AttenuationPreset is equal to the receiver
|
||||
func (a Permissions) Equals(b Permissions) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
// String returns the string representation of the AttenuationPreset
|
||||
func (a Permissions) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
// ParseAttenuationData parses raw attenuation data into a structured format
|
||||
func ParseAttenuationData(data map[string]interface{}) (Permissions, map[string]interface{}, error) {
|
||||
typeRaw, ok := data["preset"]
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("missing preset type in attenuation data")
|
||||
}
|
||||
|
||||
presetType, ok := typeRaw.(string)
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("invalid preset type format")
|
||||
}
|
||||
|
||||
return Permissions(presetType), data, nil
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package ucan
|
||||
|
||||
import (
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
// Proof is a string representing a fact. Expected to be either a raw UCAN token
|
||||
// or the CID of a raw UCAN token
|
||||
type Proof string
|
||||
|
||||
// IsCID returns true if the Proof string is a CID
|
||||
func (prf Proof) IsCID() bool {
|
||||
if _, err := cid.Decode(string(prf)); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
package ucan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
// ErrTokenNotFound is returned by stores that cannot find an access token
|
||||
// for a given key
|
||||
var ErrTokenNotFound = errors.New("access token not found")
|
||||
|
||||
// TokenStore is a store intended for clients, who need to persist jwts.
|
||||
// It deals in raw, string-formatted json web tokens, which are more useful
|
||||
// when working with APIs, but validates the tokens are well-formed when placed
|
||||
// in the store
|
||||
//
|
||||
// implementations of TokenStore must conform to the assertion test defined
|
||||
// in the spec subpackage
|
||||
type TokenStore interface {
|
||||
PutToken(ctx context.Context, key, rawToken string) error
|
||||
RawToken(ctx context.Context, key string) (rawToken string, err error)
|
||||
DeleteToken(ctx context.Context, key string) (err error)
|
||||
ListTokens(ctx context.Context, offset, limit int) (results []RawToken, err error)
|
||||
}
|
||||
|
||||
// RawToken is a struct that binds a key to a raw token string
|
||||
type RawToken struct {
|
||||
Key string
|
||||
Raw string
|
||||
}
|
||||
|
||||
// RawTokens is a list of tokens that implements sorting by keys
|
||||
type RawTokens []RawToken
|
||||
|
||||
func (rts RawTokens) Len() int { return len(rts) }
|
||||
func (rts RawTokens) Less(a, b int) bool { return rts[a].Key < rts[b].Key }
|
||||
func (rts RawTokens) Swap(i, j int) { rts[i], rts[j] = rts[j], rts[i] }
|
||||
|
||||
type memTokenStore struct {
|
||||
toksLk sync.Mutex
|
||||
toks map[string]string
|
||||
}
|
||||
|
||||
var (
|
||||
_ TokenStore = (*memTokenStore)(nil)
|
||||
_ CIDBytesResolver = (*memTokenStore)(nil)
|
||||
)
|
||||
|
||||
// NewMemTokenStore creates an in-memory token store
|
||||
func NewMemTokenStore() TokenStore {
|
||||
return &memTokenStore{
|
||||
toks: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaller interface
|
||||
func (st *memTokenStore) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(st.toRawTokens())
|
||||
}
|
||||
|
||||
func (st *memTokenStore) PutToken(ctx context.Context, key string, raw string) error {
|
||||
p := &jwt.Parser{
|
||||
UseJSONNumber: true,
|
||||
SkipClaimsValidation: false,
|
||||
}
|
||||
if _, _, err := p.ParseUnverified(raw, jwt.MapClaims{}); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrInvalidToken, err)
|
||||
}
|
||||
|
||||
st.toksLk.Lock()
|
||||
defer st.toksLk.Unlock()
|
||||
|
||||
st.toks[key] = raw
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *memTokenStore) ResolveCIDBytes(ctx context.Context, id cid.Cid) ([]byte, error) {
|
||||
rt, err := st.RawToken(ctx, id.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(rt), nil
|
||||
}
|
||||
|
||||
func (st *memTokenStore) RawToken(ctx context.Context, key string) (rawToken string, err error) {
|
||||
t, ok := st.toks[key]
|
||||
if !ok {
|
||||
return "", ErrTokenNotFound
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (st *memTokenStore) DeleteToken(ctx context.Context, key string) (err error) {
|
||||
st.toksLk.Lock()
|
||||
defer st.toksLk.Unlock()
|
||||
|
||||
if _, ok := st.toks[key]; !ok {
|
||||
return ErrTokenNotFound
|
||||
}
|
||||
delete(st.toks, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *memTokenStore) ListTokens(ctx context.Context, offset, limit int) ([]RawToken, error) {
|
||||
var results []RawToken
|
||||
|
||||
toks := st.toRawTokens()
|
||||
for i := 0; i < len(toks); i++ {
|
||||
if offset > 0 {
|
||||
offset--
|
||||
continue
|
||||
}
|
||||
results = append(results, toks[i])
|
||||
if limit > 0 && len(results) == limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (st *memTokenStore) toRawTokens() RawTokens {
|
||||
toks := make(RawTokens, len(st.toks))
|
||||
i := 0
|
||||
for key, t := range st.toks {
|
||||
toks[i] = RawToken{
|
||||
Key: key,
|
||||
Raw: t,
|
||||
}
|
||||
i++
|
||||
}
|
||||
sort.Sort(toks)
|
||||
return toks
|
||||
}
|
||||
@@ -1,400 +0,0 @@
|
||||
// Package ucan implements User-Controlled Authorization Network tokens by
|
||||
// fission:
|
||||
// https://whitepaper.fission.codes/access-control/ucan/ucan-tokens
|
||||
//
|
||||
// From the paper:
|
||||
// The UCAN format is designed as an authenticated digraph in some larger
|
||||
// authorization space. The other way to view this is as a function from a set
|
||||
// of authorizations (“UCAN proofs“) to a subset output (“UCAN capabilities”).
|
||||
package ucan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
"github.com/onsonr/sonr/crypto/keys"
|
||||
)
|
||||
|
||||
// ErrInvalidToken indicates an access token is invalid
|
||||
var ErrInvalidToken = errors.New("invalid access token")
|
||||
|
||||
const (
|
||||
// UCANVersion is the current version of the UCAN spec
|
||||
UCANVersion = "0.7.0"
|
||||
// UCANVersionKey is the key used in version headers for the UCAN spec
|
||||
UCANVersionKey = "ucv"
|
||||
// PrfKey denotes "Proofs" in a UCAN. Stored in JWT Claims
|
||||
PrfKey = "prf"
|
||||
// FctKey denotes "Facts" in a UCAN. Stored in JWT Claims
|
||||
FctKey = "fct"
|
||||
// AttKey denotes "Attenuations" in a UCAN. Stored in JWT Claims
|
||||
AttKey = "att"
|
||||
// CapKey indicates a resource Capability. Used in an attenuation
|
||||
CapKey = "cap"
|
||||
)
|
||||
|
||||
// Token is a JSON Web Token (JWT) that contains special keys that make the
|
||||
// token a UCAN
|
||||
type Token struct {
|
||||
// Entire UCAN as a signed JWT string
|
||||
Raw string
|
||||
Issuer keys.DID
|
||||
Audience keys.DID
|
||||
// the "inputs" to this token, a chain UCAN tokens with broader scopes &
|
||||
// deadlines than this token
|
||||
Proofs []Proof `json:"prf,omitempty"`
|
||||
// the "outputs" of this token, an array of heterogenous resources &
|
||||
// capabilities
|
||||
Attenuations Attenuations `json:"att,omitempty"`
|
||||
// Facts are facts, jack.
|
||||
Facts []Fact `json:"fct,omitempty"`
|
||||
}
|
||||
|
||||
// CID calculates the cid of a UCAN using the default prefix
|
||||
func (t *Token) CID() (cid.Cid, error) {
|
||||
pref := cid.Prefix{
|
||||
Version: 1,
|
||||
Codec: cid.Raw,
|
||||
MhType: mh.SHA2_256,
|
||||
MhLength: -1, // default length
|
||||
}
|
||||
|
||||
return t.PrefixCID(pref)
|
||||
}
|
||||
|
||||
// PrefixCID calculates the CID of a token with a supplied prefix
|
||||
func (t *Token) PrefixCID(pref cid.Prefix) (cid.Cid, error) {
|
||||
return pref.Sum([]byte(t.Raw))
|
||||
}
|
||||
|
||||
// Claims is the claims component of a UCAN token. UCAN claims are expressed
|
||||
// as a standard JWT claims object with additional special fields
|
||||
type Claims struct {
|
||||
*jwt.StandardClaims
|
||||
// the "inputs" to this token, a chain UCAN tokens with broader scopes &
|
||||
// deadlines than this token
|
||||
// Proofs are UCAN chains, leading back to a self-evident origin token
|
||||
Proofs []Proof `json:"prf,omitempty"`
|
||||
// the "outputs" of this token, an array of heterogenous resources &
|
||||
// capabilities
|
||||
Attenuations Attenuations `json:"att,omitempty"`
|
||||
// Facts are facts, jack.
|
||||
Facts []Fact `json:"fct,omitempty"`
|
||||
}
|
||||
|
||||
// Fact is self-evident statement
|
||||
type Fact struct {
|
||||
cidString string
|
||||
value map[string]interface{}
|
||||
}
|
||||
|
||||
// func (fct *Fact) MarshalJSON() (p[])
|
||||
|
||||
// func (fct *Fact) UnmarshalJSON(p []byte) error {
|
||||
// var str string
|
||||
// if json.Unmarshal(p, &str); err == nil {
|
||||
// }
|
||||
// }
|
||||
|
||||
// CIDBytesResolver is a small interface for turning a CID into the bytes
|
||||
// they reference. In practice this may be backed by a network connection that
|
||||
// can fetch CIDs, eg: IPFS.
|
||||
type CIDBytesResolver interface {
|
||||
ResolveCIDBytes(ctx context.Context, id cid.Cid) ([]byte, error)
|
||||
}
|
||||
|
||||
// Source creates tokens, and provides a verification key for all tokens it
|
||||
// creates
|
||||
//
|
||||
// implementations of Source must conform to the assertion test defined in the
|
||||
// spec subpackage
|
||||
type Source interface {
|
||||
NewOriginToken(audienceDID string, att Attenuations, fct []Fact, notBefore, expires time.Time) (*Token, error)
|
||||
NewAttenuatedToken(parent *Token, audienceDID string, att Attenuations, fct []Fact, notBefore, expires time.Time) (*Token, error)
|
||||
}
|
||||
|
||||
type pkSource struct {
|
||||
pk crypto.PrivKey
|
||||
issuerDID string
|
||||
signingMethod jwt.SigningMethod
|
||||
|
||||
verifyKey interface{} // one of: *rsa.PublicKey, *edsa.PublicKey
|
||||
signKey interface{} // one of: *rsa.PrivateKey,
|
||||
}
|
||||
|
||||
// assert pkSource implements tokens at compile time
|
||||
var _ Source = (*pkSource)(nil)
|
||||
|
||||
// NewPrivKeySource creates an authentication interface backed by a single
|
||||
// private key. Intended for a node running as remote, or providing a public API
|
||||
func NewPrivKeySource(privKey crypto.PrivKey) (Source, error) {
|
||||
rawPrivBytes, err := privKey.Raw()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting private key bytes: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
methodStr = ""
|
||||
keyType = privKey.Type()
|
||||
signKey interface{}
|
||||
verifyKey interface{}
|
||||
)
|
||||
|
||||
switch keyType {
|
||||
case crypto.RSA:
|
||||
methodStr = "RS256"
|
||||
// TODO(b5) - detect if key is encoded as PEM block, here we're assuming it is
|
||||
signKey, err = x509.ParsePKCS1PrivateKey(rawPrivBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawPubBytes, err := privKey.GetPublic().Raw()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting raw public key bytes: %w", err)
|
||||
}
|
||||
verifyKeyiface, err := x509.ParsePKIXPublicKey(rawPubBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing public key bytes: %w", err)
|
||||
}
|
||||
var ok bool
|
||||
verifyKey, ok = verifyKeyiface.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("public key is not an RSA key. got type: %T", verifyKeyiface)
|
||||
}
|
||||
case crypto.Ed25519:
|
||||
methodStr = "EdDSA"
|
||||
signKey = ed25519.PrivateKey(rawPrivBytes)
|
||||
rawPubBytes, err := privKey.GetPublic().Raw()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting raw public key bytes: %w", err)
|
||||
}
|
||||
verifyKey = ed25519.PublicKey(rawPubBytes)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported key type for token creation: %q", keyType)
|
||||
}
|
||||
|
||||
issuerDID, err := DIDStringFromPublicKey(privKey.GetPublic())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pkSource{
|
||||
pk: privKey,
|
||||
signingMethod: jwt.GetSigningMethod(methodStr),
|
||||
verifyKey: verifyKey,
|
||||
signKey: signKey,
|
||||
issuerDID: issuerDID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *pkSource) NewOriginToken(audienceDID string, att Attenuations, fct []Fact, nbf, exp time.Time) (*Token, error) {
|
||||
return a.newToken(audienceDID, nil, att, fct, nbf, exp)
|
||||
}
|
||||
|
||||
func (a *pkSource) NewAttenuatedToken(parent *Token, audienceDID string, att Attenuations, fct []Fact, nbf, exp time.Time) (*Token, error) {
|
||||
if !parent.Attenuations.Contains(att) {
|
||||
return nil, fmt.Errorf("scope of ucan attenuations must be less than it's parent")
|
||||
}
|
||||
return a.newToken(audienceDID, append(parent.Proofs, Proof(parent.Raw)), att, fct, nbf, exp)
|
||||
}
|
||||
|
||||
// CreateToken returns a new JWT token
|
||||
func (a *pkSource) newToken(audienceDID string, prf []Proof, att Attenuations, fct []Fact, nbf, exp time.Time) (*Token, error) {
|
||||
// create a signer for rsa 256
|
||||
t := jwt.New(a.signingMethod)
|
||||
|
||||
// if _, err := did.Parse(audienceDID); err != nil {
|
||||
// return nil, fmt.Errorf("invalid audience DID: %w", err)
|
||||
// }
|
||||
|
||||
t.Header[UCANVersionKey] = UCANVersion
|
||||
|
||||
var (
|
||||
nbfUnix int64
|
||||
expUnix int64
|
||||
)
|
||||
|
||||
if !nbf.IsZero() {
|
||||
nbfUnix = nbf.Unix()
|
||||
}
|
||||
if !exp.IsZero() {
|
||||
expUnix = exp.Unix()
|
||||
}
|
||||
|
||||
// set our claims
|
||||
t.Claims = &Claims{
|
||||
StandardClaims: &jwt.StandardClaims{
|
||||
Issuer: a.issuerDID,
|
||||
Audience: audienceDID,
|
||||
NotBefore: nbfUnix,
|
||||
// set the expire time
|
||||
// see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-20#section-4.1.4
|
||||
ExpiresAt: expUnix,
|
||||
},
|
||||
Attenuations: att,
|
||||
Facts: fct,
|
||||
Proofs: prf,
|
||||
}
|
||||
|
||||
raw, err := t.SignedString(a.signKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Token{
|
||||
Raw: raw,
|
||||
Attenuations: att,
|
||||
Facts: fct,
|
||||
Proofs: prf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DIDPubKeyResolver turns did:key Decentralized IDentifiers into a public key,
|
||||
// possibly using a network request
|
||||
type DIDPubKeyResolver interface {
|
||||
ResolveDIDKey(ctx context.Context, did string) (keys.DID, error)
|
||||
}
|
||||
|
||||
// DIDStringFromPublicKey creates a did:key identifier string from a public key
|
||||
func DIDStringFromPublicKey(pub crypto.PubKey) (string, error) {
|
||||
id, err := keys.NewDID(pub)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return id.String(), nil
|
||||
}
|
||||
|
||||
// StringDIDPubKeyResolver implements the DIDPubKeyResolver interface without
|
||||
// any network backing. Works if the key string given contains the public key
|
||||
// itself
|
||||
type StringDIDPubKeyResolver struct{}
|
||||
|
||||
// ResolveDIDKey extracts a public key from a did:key string
|
||||
func (StringDIDPubKeyResolver) ResolveDIDKey(ctx context.Context, didStr string) (keys.DID, error) {
|
||||
return keys.Parse(didStr)
|
||||
}
|
||||
|
||||
// TokenParser parses a raw string into a Token
|
||||
type TokenParser struct {
|
||||
ap AttenuationConstructorFunc
|
||||
cidr CIDBytesResolver
|
||||
didr DIDPubKeyResolver
|
||||
}
|
||||
|
||||
// NewTokenParser constructs a token parser
|
||||
func NewTokenParser(ap AttenuationConstructorFunc, didr DIDPubKeyResolver, cidr CIDBytesResolver) *TokenParser {
|
||||
return &TokenParser{
|
||||
ap: ap,
|
||||
cidr: cidr,
|
||||
didr: didr,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseAndVerify will parse, validate and return a token
|
||||
func (p *TokenParser) ParseAndVerify(ctx context.Context, raw string) (*Token, error) {
|
||||
return p.parseAndVerify(ctx, raw, nil)
|
||||
}
|
||||
|
||||
func (p *TokenParser) parseAndVerify(ctx context.Context, raw string, child *Token) (*Token, error) {
|
||||
tok, err := jwt.Parse(raw, p.matchVerifyKeyFunc(ctx))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing UCAN: %w", err)
|
||||
}
|
||||
|
||||
mc, ok := tok.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("parser fail")
|
||||
}
|
||||
|
||||
var iss keys.DID
|
||||
// TODO(b5): we're double parsing here b/c the jwt lib we're using doesn't expose
|
||||
// an API (that I know of) for storing parsed issuer / audience
|
||||
if issStr, ok := mc["iss"].(string); ok {
|
||||
iss, err = keys.Parse(issStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf(`"iss" key is not in claims`)
|
||||
}
|
||||
|
||||
var aud keys.DID
|
||||
// TODO(b5): we're double parsing here b/c the jwt lib we're using doesn't expose
|
||||
// an API (that I know of) for storing parsed issuer / audience
|
||||
if audStr, ok := mc["aud"].(string); ok {
|
||||
aud, err = keys.Parse(audStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf(`"aud" key is not in claims`)
|
||||
}
|
||||
|
||||
var att Attenuations
|
||||
if acci, ok := mc[AttKey].([]interface{}); ok {
|
||||
for i, a := range acci {
|
||||
if mapv, ok := a.(map[string]interface{}); ok {
|
||||
a, err := p.ap(mapv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
att = append(att, a)
|
||||
} else {
|
||||
return nil, fmt.Errorf(`"att[%d]" is not an object`, i)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf(`"att" key is not an array`)
|
||||
}
|
||||
|
||||
var prf []Proof
|
||||
if prfi, ok := mc[PrfKey].([]interface{}); ok {
|
||||
for i, a := range prfi {
|
||||
if pStr, ok := a.(string); ok {
|
||||
prf = append(prf, Proof(pStr))
|
||||
} else {
|
||||
return nil, fmt.Errorf(`"prf[%d]" is not a string`, i)
|
||||
}
|
||||
}
|
||||
} else if mc[PrfKey] != nil {
|
||||
return nil, fmt.Errorf(`"prf" key is not an array`)
|
||||
}
|
||||
|
||||
return &Token{
|
||||
Raw: raw,
|
||||
Issuer: iss,
|
||||
Audience: aud,
|
||||
Attenuations: att,
|
||||
Proofs: prf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *TokenParser) matchVerifyKeyFunc(ctx context.Context) func(tok *jwt.Token) (interface{}, error) {
|
||||
return func(tok *jwt.Token) (interface{}, error) {
|
||||
mc, ok := tok.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("parser fail")
|
||||
}
|
||||
|
||||
iss, ok := mc["iss"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`"iss" claims key is required`)
|
||||
}
|
||||
|
||||
id, err := p.didr.ResolveDIDKey(ctx, iss)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return id.VerifyKey()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user