2024-09-17 19:12:31 +02:00
|
|
|
package meta
|
|
|
|
|
|
|
|
|
|
import (
|
2024-11-12 15:29:48 +01:00
|
|
|
"errors"
|
2024-09-24 11:40:28 -04:00
|
|
|
"fmt"
|
2024-11-20 18:27:01 +01:00
|
|
|
"iter"
|
|
|
|
|
"sort"
|
2024-10-14 12:19:33 +02:00
|
|
|
"strings"
|
2024-09-17 19:12:31 +02:00
|
|
|
|
|
|
|
|
"github.com/ipld/go-ipld-prime"
|
2024-10-14 12:19:33 +02:00
|
|
|
"github.com/ipld/go-ipld-prime/printer"
|
2024-10-31 18:24:54 +01:00
|
|
|
|
2024-11-12 15:29:48 +01:00
|
|
|
"github.com/ucan-wg/go-ucan/pkg/meta/internal/crypto"
|
2024-11-12 13:05:48 +01:00
|
|
|
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
|
|
|
|
)
|
2024-11-12 15:31:21 +01:00
|
|
|
|
2024-11-20 12:34:24 +01:00
|
|
|
var ErrNotFound = errors.New("key not found in meta")
|
2024-09-24 11:40:28 -04:00
|
|
|
|
2024-10-31 18:24:54 +01:00
|
|
|
var ErrNotEncryptable = errors.New("value of this type cannot be encrypted")
|
2024-09-17 19:12:31 +02:00
|
|
|
|
|
|
|
|
// Meta is a container for meta key-value pairs in a UCAN token.
|
2024-11-12 13:05:48 +01:00
|
|
|
// This also serves as a way to construct the underlying IPLD data with minimum allocations
|
|
|
|
|
// and transformations, while hiding the IPLD complexity from the caller.
|
2024-09-17 19:12:31 +02:00
|
|
|
type Meta struct {
|
2024-11-12 13:05:48 +01:00
|
|
|
// This type must be compatible with the IPLD type represented by the IPLD
|
|
|
|
|
// schema { String : Any }.
|
|
|
|
|
|
2024-09-17 19:12:31 +02:00
|
|
|
Keys []string
|
|
|
|
|
Values map[string]ipld.Node
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewMeta constructs a new Meta.
|
|
|
|
|
func NewMeta() *Meta {
|
|
|
|
|
return &Meta{Values: map[string]ipld.Node{}}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetBool retrieves a value as a bool.
|
|
|
|
|
// Returns ErrNotFound if the given key is missing.
|
|
|
|
|
// Returns datamodel.ErrWrongKind if the value has the wrong type.
|
2024-09-19 10:48:25 +02:00
|
|
|
func (m *Meta) GetBool(key string) (bool, error) {
|
2024-09-17 19:12:31 +02:00
|
|
|
v, ok := m.Values[key]
|
|
|
|
|
if !ok {
|
|
|
|
|
return false, ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
return v.AsBool()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetString retrieves a value as a string.
|
|
|
|
|
// Returns ErrNotFound if the given key is missing.
|
|
|
|
|
// Returns datamodel.ErrWrongKind if the value has the wrong type.
|
2024-09-19 10:48:25 +02:00
|
|
|
func (m *Meta) GetString(key string) (string, error) {
|
2024-09-17 19:12:31 +02:00
|
|
|
v, ok := m.Values[key]
|
|
|
|
|
if !ok {
|
|
|
|
|
return "", ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
return v.AsString()
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-31 18:24:54 +01:00
|
|
|
// GetEncryptedString decorates GetString and decrypt its output with the given symmetric encryption key.
|
|
|
|
|
func (m *Meta) GetEncryptedString(key string, encryptionKey []byte) (string, error) {
|
2024-11-12 16:37:53 +01:00
|
|
|
v, err := m.GetBytes(key)
|
2024-10-31 18:24:54 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-12 16:37:53 +01:00
|
|
|
decrypted, err := crypto.DecryptStringWithAESKey(v, encryptionKey)
|
2024-10-31 18:24:54 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string(decrypted), nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-17 19:12:31 +02:00
|
|
|
// GetInt64 retrieves a value as an int64.
|
|
|
|
|
// Returns ErrNotFound if the given key is missing.
|
|
|
|
|
// Returns datamodel.ErrWrongKind if the value has the wrong type.
|
2024-09-19 10:48:25 +02:00
|
|
|
func (m *Meta) GetInt64(key string) (int64, error) {
|
2024-09-17 19:12:31 +02:00
|
|
|
v, ok := m.Values[key]
|
|
|
|
|
if !ok {
|
|
|
|
|
return 0, ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
return v.AsInt()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetFloat64 retrieves a value as a float64.
|
|
|
|
|
// Returns ErrNotFound if the given key is missing.
|
|
|
|
|
// Returns datamodel.ErrWrongKind if the value has the wrong type.
|
2024-09-19 10:48:25 +02:00
|
|
|
func (m *Meta) GetFloat64(key string) (float64, error) {
|
2024-09-17 19:12:31 +02:00
|
|
|
v, ok := m.Values[key]
|
|
|
|
|
if !ok {
|
|
|
|
|
return 0, ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
return v.AsFloat()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetBytes retrieves a value as a []byte.
|
|
|
|
|
// Returns ErrNotFound if the given key is missing.
|
|
|
|
|
// Returns datamodel.ErrWrongKind if the value has the wrong type.
|
2024-09-19 10:48:25 +02:00
|
|
|
func (m *Meta) GetBytes(key string) ([]byte, error) {
|
2024-09-17 19:12:31 +02:00
|
|
|
v, ok := m.Values[key]
|
|
|
|
|
if !ok {
|
|
|
|
|
return nil, ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
return v.AsBytes()
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-31 18:24:54 +01:00
|
|
|
// GetEncryptedBytes decorates GetBytes and decrypt its output with the given symmetric encryption key.
|
|
|
|
|
func (m *Meta) GetEncryptedBytes(key string, encryptionKey []byte) ([]byte, error) {
|
|
|
|
|
v, err := m.GetBytes(key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decrypted, err := crypto.DecryptStringWithAESKey(v, encryptionKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return decrypted, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-17 19:12:31 +02:00
|
|
|
// GetNode retrieves a value as a raw IPLD node.
|
|
|
|
|
// Returns ErrNotFound if the given key is missing.
|
2024-09-19 10:48:25 +02:00
|
|
|
func (m *Meta) GetNode(key string) (ipld.Node, error) {
|
2024-09-17 19:12:31 +02:00
|
|
|
v, ok := m.Values[key]
|
|
|
|
|
if !ok {
|
|
|
|
|
return nil, ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
return v, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add adds a key/value pair in the meta set.
|
2024-11-12 13:05:48 +01:00
|
|
|
// Accepted types for val are any CBOR compatible type, or directly IPLD values.
|
2024-09-19 10:48:25 +02:00
|
|
|
func (m *Meta) Add(key string, val any) error {
|
2024-11-06 18:06:46 +01:00
|
|
|
if _, ok := m.Values[key]; ok {
|
|
|
|
|
return fmt.Errorf("duplicate key %q", key)
|
|
|
|
|
}
|
2024-11-12 13:05:48 +01:00
|
|
|
|
|
|
|
|
node, err := literal.Any(val)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2024-09-17 19:12:31 +02:00
|
|
|
}
|
2024-11-12 13:05:48 +01:00
|
|
|
|
2024-09-17 19:12:31 +02:00
|
|
|
m.Keys = append(m.Keys, key)
|
2024-11-12 13:05:48 +01:00
|
|
|
m.Values[key] = node
|
|
|
|
|
|
2024-09-17 19:12:31 +02:00
|
|
|
return nil
|
|
|
|
|
}
|
2024-09-24 11:40:28 -04:00
|
|
|
|
2024-10-31 18:24:54 +01:00
|
|
|
// AddEncrypted adds a key/value pair in the meta set.
|
|
|
|
|
// The value is encrypted with the given encryptionKey.
|
|
|
|
|
// Accepted types for the value are: string, []byte.
|
|
|
|
|
func (m *Meta) AddEncrypted(key string, val any, encryptionKey []byte) error {
|
|
|
|
|
var encrypted []byte
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
switch val := val.(type) {
|
|
|
|
|
case string:
|
|
|
|
|
encrypted, err = crypto.EncryptWithAESKey([]byte(val), encryptionKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
case []byte:
|
|
|
|
|
encrypted, err = crypto.EncryptWithAESKey(val, encryptionKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return ErrNotEncryptable
|
|
|
|
|
}
|
2024-11-12 16:37:53 +01:00
|
|
|
|
|
|
|
|
return m.Add(key, encrypted)
|
2024-10-31 18:24:54 +01:00
|
|
|
}
|
|
|
|
|
|
2024-11-20 18:27:01 +01:00
|
|
|
type Iterator interface {
|
|
|
|
|
Iter() iter.Seq2[string, ipld.Node]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Include merges the provided meta into the existing one.
|
|
|
|
|
//
|
|
|
|
|
// If duplicate keys are encountered, the new value is silently dropped
|
|
|
|
|
// without causing an error.
|
|
|
|
|
func (m *Meta) Include(other Iterator) {
|
|
|
|
|
for key, value := range other.Iter() {
|
|
|
|
|
if _, ok := m.Values[key]; ok {
|
|
|
|
|
// don't overwrite
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
m.Values[key] = value
|
|
|
|
|
m.Keys = append(m.Keys, key)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Iter iterates over the meta key/values
|
|
|
|
|
func (m *Meta) Iter() iter.Seq2[string, ipld.Node] {
|
|
|
|
|
return func(yield func(string, ipld.Node) bool) {
|
|
|
|
|
for _, key := range m.Keys {
|
|
|
|
|
if !yield(key, m.Values[key]) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 11:56:32 +02:00
|
|
|
// Equals tells if two Meta hold the same key/values.
|
|
|
|
|
func (m *Meta) Equals(other *Meta) bool {
|
|
|
|
|
if len(m.Keys) != len(other.Keys) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if len(m.Values) != len(other.Values) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
for _, key := range m.Keys {
|
|
|
|
|
if !ipld.DeepEqual(m.Values[key], other.Values[key]) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-14 12:19:33 +02:00
|
|
|
func (m *Meta) String() string {
|
2024-11-20 18:27:01 +01:00
|
|
|
sort.Strings(m.Keys)
|
|
|
|
|
|
2024-10-14 12:19:33 +02:00
|
|
|
buf := strings.Builder{}
|
|
|
|
|
buf.WriteString("{")
|
|
|
|
|
|
|
|
|
|
for key, node := range m.Values {
|
2024-11-20 12:34:24 +01:00
|
|
|
buf.WriteString("\n\t")
|
2024-10-14 12:19:33 +02:00
|
|
|
buf.WriteString(key)
|
2024-11-20 12:34:24 +01:00
|
|
|
buf.WriteString(": ")
|
|
|
|
|
buf.WriteString(strings.ReplaceAll(printer.Sprint(node), "\n", "\n\t"))
|
|
|
|
|
buf.WriteString(",")
|
2024-10-14 12:19:33 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-20 12:34:24 +01:00
|
|
|
if len(m.Values) > 0 {
|
|
|
|
|
buf.WriteString("\n")
|
|
|
|
|
}
|
2024-10-14 12:19:33 +02:00
|
|
|
buf.WriteString("}")
|
2024-11-20 12:34:24 +01:00
|
|
|
|
2024-10-14 12:19:33 +02:00
|
|
|
return buf.String()
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-06 15:17:35 +01:00
|
|
|
// ReadOnly returns a read-only version of Meta.
|
|
|
|
|
func (m *Meta) ReadOnly() ReadOnly {
|
2024-11-20 18:27:01 +01:00
|
|
|
return ReadOnly{meta: m}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clone makes a deep copy.
|
|
|
|
|
func (m *Meta) Clone() *Meta {
|
|
|
|
|
res := &Meta{
|
|
|
|
|
Keys: make([]string, len(m.Keys)),
|
|
|
|
|
Values: make(map[string]ipld.Node, len(m.Values)),
|
|
|
|
|
}
|
|
|
|
|
copy(res.Keys, m.Keys)
|
|
|
|
|
for k, v := range m.Values {
|
|
|
|
|
res.Values[k] = v
|
|
|
|
|
}
|
|
|
|
|
return res
|
2024-11-06 15:17:35 +01:00
|
|
|
}
|