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:
Prad Nukala
2024-12-24 16:10:20 +00:00
committed by GitHub
parent 0ec2f7d86a
commit 47c3a53080
356 changed files with 402 additions and 1613 deletions
-17
View File
@@ -1,17 +0,0 @@
# UCAN Tokens in Go
![UCAN](https://img.shields.io/badge/UCAN-v0.7.0-blue)
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
![](https://ipfs.runfission.com/ipfs/QmRFXjMjVNwnYki8jGwFBh3zcY5m7zo5oAcNoyS1PSgzAY/ucan-gopher.png)
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)
-142
View File
@@ -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
}
-36
View File
@@ -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
}
-11
View File
@@ -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
}
-11
View File
@@ -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
}
-11
View File
@@ -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()
}
-114
View File
@@ -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)
}
-8
View File
@@ -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
}
-33
View File
@@ -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
}
-33
View File
@@ -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())
}
-66
View File
@@ -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
}
-17
View File
@@ -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
}
-141
View File
@@ -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
}
-400
View File
@@ -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()
}
}