mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-01 03:16:31 +02:00
* chore(deps): bump github.com/golangci/golangci-lint Bumps [github.com/golangci/golangci-lint](https://github.com/golangci/golangci-lint) from 1.48.0 to 1.50.0. - [Release notes](https://github.com/golangci/golangci-lint/releases) - [Changelog](https://github.com/golangci/golangci-lint/blob/master/CHANGELOG.md) - [Commits](https://github.com/golangci/golangci-lint/compare/v1.48.0...v1.50.0) --- updated-dependencies: - dependency-name: github.com/golangci/golangci-lint dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * lint Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Caleb Doxsey <cdoxsey@pomerium.com>
323 lines
6.1 KiB
Go
323 lines
6.1 KiB
Go
package parser
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
|
|
"github.com/open-policy-agent/opa/ast"
|
|
)
|
|
|
|
// A Value is a JSON value. Either an object, array, string, number, boolean or null.
|
|
type Value interface {
|
|
isValue()
|
|
Clone() Value
|
|
RegoValue() ast.Value
|
|
}
|
|
|
|
// ParseValue parses JSON into a value.
|
|
func ParseValue(r io.Reader) (Value, error) {
|
|
dec := json.NewDecoder(r)
|
|
dec.UseNumber()
|
|
|
|
tok, err := dec.Token()
|
|
if errors.Is(err, io.EOF) {
|
|
return nil, io.ErrUnexpectedEOF
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
v, err := parseValue(dec, tok)
|
|
if err != nil {
|
|
return v, err
|
|
}
|
|
if dec.More() {
|
|
return nil, fmt.Errorf("unexpected additional json value: offset=%d", dec.InputOffset())
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
func parseValue(dec *json.Decoder, tok json.Token) (Value, error) {
|
|
if d, ok := tok.(json.Delim); ok {
|
|
switch d {
|
|
case '[':
|
|
return parseArray(dec)
|
|
case '{':
|
|
return parseObject(dec)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported json delimiter: %s", string(d))
|
|
}
|
|
}
|
|
|
|
return parseSimple(tok)
|
|
}
|
|
|
|
func parseArray(dec *json.Decoder) (Value, error) {
|
|
var a Array
|
|
for {
|
|
tok, err := dec.Token()
|
|
if errors.Is(err, io.EOF) {
|
|
return nil, io.ErrUnexpectedEOF
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if d, ok := tok.(json.Delim); ok && d == ']' {
|
|
return a, nil
|
|
}
|
|
|
|
v, err := parseValue(dec, tok)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
a = append(a, v)
|
|
}
|
|
}
|
|
|
|
func parseObject(dec *json.Decoder) (Value, error) {
|
|
o := make(Object)
|
|
k := ""
|
|
for i := 0; ; i++ {
|
|
tok, err := dec.Token()
|
|
if errors.Is(err, io.EOF) {
|
|
return nil, io.ErrUnexpectedEOF
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if d, ok := tok.(json.Delim); ok && d == '}' {
|
|
return o, nil
|
|
}
|
|
|
|
v, err := parseValue(dec, tok)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// if we're handling a key
|
|
if i%2 == 0 {
|
|
s, ok := v.(String)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unsupported object key type: %T", v)
|
|
}
|
|
k = string(s)
|
|
} else {
|
|
o[k] = v
|
|
}
|
|
}
|
|
}
|
|
|
|
func parseSimple(tok json.Token) (Value, error) {
|
|
switch t := tok.(type) {
|
|
case bool:
|
|
return Boolean(t), nil
|
|
case json.Number:
|
|
return Number(t), nil
|
|
case string:
|
|
return String(t), nil
|
|
case nil:
|
|
return Null{}, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("unsupported json token type: %T", tok)
|
|
}
|
|
|
|
// An Object is a map of strings to values.
|
|
type Object map[string]Value
|
|
|
|
func (Object) isValue() {}
|
|
|
|
// Clone clones the Object.
|
|
func (o Object) Clone() Value {
|
|
no := make(Object)
|
|
for k, v := range o {
|
|
no[k] = v
|
|
}
|
|
return no
|
|
}
|
|
|
|
// Falsy returns true if the value is considered Javascript falsy:
|
|
//
|
|
// https://developer.mozilla.org/en-US/docs/Glossary/Falsy.
|
|
//
|
|
// If the field is not found in the object it is *not* falsy.
|
|
func (o Object) Falsy(field string) bool {
|
|
v, ok := o[field]
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
switch v := v.(type) {
|
|
case Boolean:
|
|
return !bool(v)
|
|
case Number:
|
|
return v.Float64() == 0 || math.IsNaN(v.Float64())
|
|
case String:
|
|
return v == ""
|
|
case Null:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// RegoValue returns the Object as a rego Value.
|
|
func (o Object) RegoValue() ast.Value {
|
|
kvps := make([][2]*ast.Term, 0, len(o))
|
|
for k, v := range o {
|
|
if v == nil {
|
|
v = Null{}
|
|
}
|
|
kvps = append(kvps, [2]*ast.Term{
|
|
ast.StringTerm(k),
|
|
ast.NewTerm(v.RegoValue()),
|
|
})
|
|
}
|
|
return ast.NewObject(kvps...)
|
|
}
|
|
|
|
// String returns the JSON representation of the Object.
|
|
func (o Object) String() string {
|
|
bs, _ := json.Marshal(o)
|
|
return string(bs)
|
|
}
|
|
|
|
// Truthy returns the opposite of Falsy, however if the field is not found in the object it is neither truthy nor falsy.
|
|
func (o Object) Truthy(field string) bool {
|
|
_, ok := o[field]
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return !o.Falsy(field)
|
|
}
|
|
|
|
// An Array is a slice of values.
|
|
type Array []Value
|
|
|
|
func (Array) isValue() {}
|
|
|
|
// Clone clones the array.
|
|
func (a Array) Clone() Value {
|
|
na := make(Array, len(a))
|
|
copy(na, a)
|
|
return na
|
|
}
|
|
|
|
// RegoValue returns the Array as a rego Value.
|
|
func (a Array) RegoValue() ast.Value {
|
|
var vs []*ast.Term
|
|
for _, v := range a {
|
|
vs = append(vs, ast.NewTerm(v.RegoValue()))
|
|
}
|
|
return ast.NewArray(vs...)
|
|
}
|
|
|
|
// String returns the JSON representation of the Array.
|
|
func (a Array) String() string {
|
|
bs, _ := json.Marshal(a)
|
|
return string(bs)
|
|
}
|
|
|
|
// A String is a wrapper around a string.
|
|
type String string
|
|
|
|
func (String) isValue() {}
|
|
|
|
// Clone clones the string.
|
|
func (s String) Clone() Value {
|
|
return s
|
|
}
|
|
|
|
// RegoValue returns the String as a rego Value.
|
|
func (s String) RegoValue() ast.Value {
|
|
return ast.String(s)
|
|
}
|
|
|
|
// String returns the JSON representation of the String.
|
|
func (s String) String() string {
|
|
bs, _ := json.Marshal(s)
|
|
return string(bs)
|
|
}
|
|
|
|
// A Number is an integer or a floating point value stored in string representation.
|
|
type Number string
|
|
|
|
func (Number) isValue() {}
|
|
|
|
// Clone clones the number.
|
|
func (n Number) Clone() Value {
|
|
return n
|
|
}
|
|
|
|
// Float64 returns the number as a float64.
|
|
func (n Number) Float64() float64 {
|
|
v, _ := json.Number(n).Float64()
|
|
return v
|
|
}
|
|
|
|
// Int64 returns the number as an int64.
|
|
func (n Number) Int64() int64 {
|
|
v, _ := json.Number(n).Int64()
|
|
return v
|
|
}
|
|
|
|
// RegoValue returns the Number as a rego Value.
|
|
func (n Number) RegoValue() ast.Value {
|
|
return ast.Number(n)
|
|
}
|
|
|
|
// String returns the JSON representation of the Number.
|
|
func (n Number) String() string {
|
|
return string(n)
|
|
}
|
|
|
|
// MarshalJSON marshals the number as JSON.
|
|
func (n Number) MarshalJSON() ([]byte, error) {
|
|
return []byte(n), nil
|
|
}
|
|
|
|
// A Boolean is either true or false.
|
|
type Boolean bool
|
|
|
|
func (Boolean) isValue() {}
|
|
|
|
// Clone clones the boolean.
|
|
func (b Boolean) Clone() Value {
|
|
return b
|
|
}
|
|
|
|
// RegoValue returns the Boolean as a rego Value.
|
|
func (b Boolean) RegoValue() ast.Value {
|
|
return ast.Boolean(b)
|
|
}
|
|
|
|
// String returns the JSON representation of the Boolean.
|
|
func (b Boolean) String() string {
|
|
if b {
|
|
return "true"
|
|
}
|
|
return "false"
|
|
}
|
|
|
|
// A Null is the nil value.
|
|
type Null struct{}
|
|
|
|
func (Null) isValue() {}
|
|
|
|
// Clone clones the null.
|
|
func (Null) Clone() Value {
|
|
return Null{}
|
|
}
|
|
|
|
// RegoValue returns the Null as a rego Value.
|
|
func (Null) RegoValue() ast.Value {
|
|
return ast.Null{}
|
|
}
|
|
|
|
// String returns JSON null.
|
|
func (Null) String() string {
|
|
return "null"
|
|
}
|