Initial QSfera import
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
||||
load("@rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "json",
|
||||
srcs = [
|
||||
"json.go",
|
||||
"registry.go",
|
||||
"stdlib.go",
|
||||
],
|
||||
importpath = "github.com/lestrrat-go/jwx/v3/internal/json",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = ["//internal/base64"],
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "go_default_library",
|
||||
actual = ":json",
|
||||
visibility = ["//:__subpackages__"],
|
||||
)
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
//go:build jwx_goccy
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
)
|
||||
|
||||
type Decoder = json.Decoder
|
||||
type Delim = json.Delim
|
||||
type Encoder = json.Encoder
|
||||
type Marshaler = json.Marshaler
|
||||
type Number = json.Number
|
||||
type RawMessage = json.RawMessage
|
||||
type Unmarshaler = json.Unmarshaler
|
||||
|
||||
func Engine() string {
|
||||
return "github.com/goccy/go-json"
|
||||
}
|
||||
|
||||
// NewDecoder respects the values specified in DecoderSettings,
|
||||
// and creates a Decoder that has certain features turned on/off
|
||||
func NewDecoder(r io.Reader) *json.Decoder {
|
||||
dec := json.NewDecoder(r)
|
||||
|
||||
if UseNumber() {
|
||||
dec.UseNumber()
|
||||
}
|
||||
|
||||
return dec
|
||||
}
|
||||
|
||||
// NewEncoder is just a proxy for "encoding/json".NewEncoder
|
||||
func NewEncoder(w io.Writer) *json.Encoder {
|
||||
return json.NewEncoder(w)
|
||||
}
|
||||
|
||||
// Marshal is just a proxy for "encoding/json".Marshal
|
||||
func Marshal(v any) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
// MarshalIndent is just a proxy for "encoding/json".MarshalIndent
|
||||
func MarshalIndent(v any, prefix, indent string) ([]byte, error) {
|
||||
return json.MarshalIndent(v, prefix, indent)
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/base64"
|
||||
)
|
||||
|
||||
var useNumber uint32 // TODO: at some point, change to atomic.Bool
|
||||
|
||||
func UseNumber() bool {
|
||||
return atomic.LoadUint32(&useNumber) == 1
|
||||
}
|
||||
|
||||
// Sets the global configuration for json decoding
|
||||
func DecoderSettings(inUseNumber bool) {
|
||||
var val uint32
|
||||
if inUseNumber {
|
||||
val = 1
|
||||
}
|
||||
atomic.StoreUint32(&useNumber, val)
|
||||
}
|
||||
|
||||
// Unmarshal respects the values specified in DecoderSettings,
|
||||
// and uses a Decoder that has certain features turned on/off
|
||||
func Unmarshal(b []byte, v any) error {
|
||||
dec := NewDecoder(bytes.NewReader(b))
|
||||
return dec.Decode(v)
|
||||
}
|
||||
|
||||
func AssignNextBytesToken(dst *[]byte, dec *Decoder) error {
|
||||
var val string
|
||||
if err := dec.Decode(&val); err != nil {
|
||||
return fmt.Errorf(`error reading next value: %w`, err)
|
||||
}
|
||||
|
||||
buf, err := base64.DecodeString(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`expected base64 encoded []byte (%T)`, val)
|
||||
}
|
||||
*dst = buf
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReadNextStringToken(dec *Decoder) (string, error) {
|
||||
var val string
|
||||
if err := dec.Decode(&val); err != nil {
|
||||
return "", fmt.Errorf(`error reading next value: %w`, err)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func AssignNextStringToken(dst **string, dec *Decoder) error {
|
||||
val, err := ReadNextStringToken(dec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = &val
|
||||
return nil
|
||||
}
|
||||
|
||||
// FlattenAudience is a flag to specify if we should flatten the "aud"
|
||||
// entry to a string when there's only one entry.
|
||||
// In jwx < 1.1.8 we just dumped everything as an array of strings,
|
||||
// but apparently AWS Cognito doesn't handle this well.
|
||||
//
|
||||
// So now we have the ability to dump "aud" as a string if there's
|
||||
// only one entry, but we need to retain the old behavior so that
|
||||
// we don't accidentally break somebody else's code. (e.g. messing
|
||||
// up how signatures are calculated)
|
||||
var FlattenAudience uint32
|
||||
|
||||
func MarshalAudience(aud []string, flatten bool) ([]byte, error) {
|
||||
var val any
|
||||
if len(aud) == 1 && flatten {
|
||||
val = aud[0]
|
||||
} else {
|
||||
val = aud
|
||||
}
|
||||
return Marshal(val)
|
||||
}
|
||||
|
||||
func EncodeAudience(enc *Encoder, aud []string, flatten bool) error {
|
||||
var val any
|
||||
if len(aud) == 1 && flatten {
|
||||
val = aud[0]
|
||||
} else {
|
||||
val = aud
|
||||
}
|
||||
return enc.Encode(val)
|
||||
}
|
||||
|
||||
// DecodeCtx is an interface for objects that needs that extra something
|
||||
// when decoding JSON into an object.
|
||||
type DecodeCtx interface {
|
||||
Registry() *Registry
|
||||
}
|
||||
|
||||
// DecodeCtxContainer is used to differentiate objects that can carry extra
|
||||
// decoding hints and those who can't.
|
||||
type DecodeCtxContainer interface {
|
||||
DecodeCtx() DecodeCtx
|
||||
SetDecodeCtx(DecodeCtx)
|
||||
}
|
||||
|
||||
// stock decodeCtx. should cover 80% of the cases
|
||||
type decodeCtx struct {
|
||||
registry *Registry
|
||||
}
|
||||
|
||||
func NewDecodeCtx(r *Registry) DecodeCtx {
|
||||
return &decodeCtx{registry: r}
|
||||
}
|
||||
|
||||
func (dc *decodeCtx) Registry() *Registry {
|
||||
return dc.registry
|
||||
}
|
||||
|
||||
func Dump(v any) {
|
||||
enc := NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
//nolint:errchkjson
|
||||
_ = enc.Encode(v)
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// CustomDecoder is the interface we expect from RegisterCustomField in jws, jwe, jwk, and jwt packages.
|
||||
type CustomDecoder interface {
|
||||
// Decode takes a JSON encoded byte slice and returns the desired
|
||||
// decoded value,which will be used as the value for that field
|
||||
// registered through RegisterCustomField
|
||||
Decode([]byte) (any, error)
|
||||
}
|
||||
|
||||
// CustomDecodeFunc is a stateless, function-based implementation of CustomDecoder
|
||||
type CustomDecodeFunc func([]byte) (any, error)
|
||||
|
||||
func (fn CustomDecodeFunc) Decode(data []byte) (any, error) {
|
||||
return fn(data)
|
||||
}
|
||||
|
||||
type objectTypeDecoder struct {
|
||||
typ reflect.Type
|
||||
name string
|
||||
}
|
||||
|
||||
func (dec *objectTypeDecoder) Decode(data []byte) (any, error) {
|
||||
ptr := reflect.New(dec.typ).Interface()
|
||||
if err := Unmarshal(data, ptr); err != nil {
|
||||
return nil, fmt.Errorf(`failed to decode field %s: %w`, dec.name, err)
|
||||
}
|
||||
return reflect.ValueOf(ptr).Elem().Interface(), nil
|
||||
}
|
||||
|
||||
type Registry struct {
|
||||
mu *sync.RWMutex
|
||||
ctrs map[string]CustomDecoder
|
||||
}
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
mu: &sync.RWMutex{},
|
||||
ctrs: make(map[string]CustomDecoder),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registry) Register(name string, object any) {
|
||||
if object == nil {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
delete(r.ctrs, name)
|
||||
return
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if ctr, ok := object.(CustomDecoder); ok {
|
||||
r.ctrs[name] = ctr
|
||||
} else {
|
||||
r.ctrs[name] = &objectTypeDecoder{
|
||||
typ: reflect.TypeOf(object),
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registry) Decode(dec *Decoder, name string) (any, error) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
if ctr, ok := r.ctrs[name]; ok {
|
||||
var raw RawMessage
|
||||
if err := dec.Decode(&raw); err != nil {
|
||||
return nil, fmt.Errorf(`failed to decode field %s: %w`, name, err)
|
||||
}
|
||||
v, err := ctr.Decode([]byte(raw))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to decode field %s: %w`, name, err)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
var decoded any
|
||||
if err := dec.Decode(&decoded); err != nil {
|
||||
return nil, fmt.Errorf(`failed to decode field %s: %w`, name, err)
|
||||
}
|
||||
return decoded, nil
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
//go:build !jwx_goccy
|
||||
|
||||
//nolint:revive
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Decoder = json.Decoder
|
||||
type Delim = json.Delim
|
||||
type Encoder = json.Encoder
|
||||
type Marshaler = json.Marshaler
|
||||
type Number = json.Number
|
||||
type RawMessage = json.RawMessage
|
||||
type Unmarshaler = json.Unmarshaler
|
||||
|
||||
func Engine() string {
|
||||
return "encoding/json"
|
||||
}
|
||||
|
||||
// NewDecoder respects the values specified in DecoderSettings,
|
||||
// and creates a Decoder that has certain features turned on/off
|
||||
func NewDecoder(r io.Reader) *json.Decoder {
|
||||
dec := json.NewDecoder(r)
|
||||
|
||||
if UseNumber() {
|
||||
dec.UseNumber()
|
||||
}
|
||||
|
||||
return dec
|
||||
}
|
||||
|
||||
func NewEncoder(w io.Writer) *json.Encoder {
|
||||
return json.NewEncoder(w)
|
||||
}
|
||||
|
||||
// Marshal is just a proxy for "encoding/json".Marshal
|
||||
func Marshal(v any) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
// MarshalIndent is just a proxy for "encoding/json".MarshalIndent
|
||||
func MarshalIndent(v any, prefix, indent string) ([]byte, error) {
|
||||
return json.MarshalIndent(v, prefix, indent)
|
||||
}
|
||||
Reference in New Issue
Block a user