Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interact: simple validation input #921

Merged
merged 1 commit into from
May 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/api/handlers/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"net/http"

"github.com/baking-bad/bcdhub/internal/bcd/ast"
"github.com/baking-bad/bcdhub/internal/logger"
"github.com/baking-bad/bcdhub/internal/models"
sentrygin "github.com/getsentry/sentry-go/gin"
Expand Down Expand Up @@ -39,6 +40,9 @@ func getErrorCode(err error, repo models.GeneralRepository) int {
if repo.IsRecordNotFound(err) {
return http.StatusNotFound
}
if errors.Is(err, ast.ErrValidation) {
return http.StatusBadRequest
}
return http.StatusInternalServerError
}

Expand Down
3 changes: 1 addition & 2 deletions internal/bcd/ast/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,7 @@ func (a *Address) Distinguish(x Distinguishable) (*MiguelNode, error) {

// FromJSONSchema -
func (a *Address) FromJSONSchema(data map[string]interface{}) error {
setOptimizedJSONSchema(&a.Default, data, forge.UnforgeContract)
return nil
return setOptimizedJSONSchema(&a.Default, data, forge.UnforgeContract, AddressValidator)
}

// FindByName -
Expand Down
3 changes: 1 addition & 2 deletions internal/bcd/ast/baker_hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ func (s *BakerHash) Distinguish(x Distinguishable) (*MiguelNode, error) {

// FromJSONSchema -
func (s *BakerHash) FromJSONSchema(data map[string]interface{}) error {
setOptimizedJSONSchema(&s.Default, data, forge.UnforgeBakerHash)
return nil
return setOptimizedJSONSchema(&s.Default, data, forge.UnforgeBakerHash, BakerHashValidator)
}

// FindByName -
Expand Down
3 changes: 1 addition & 2 deletions internal/bcd/ast/chain_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@ func (c *ChainID) Distinguish(x Distinguishable) (*MiguelNode, error) {

// FromJSONSchema -
func (c *ChainID) FromJSONSchema(data map[string]interface{}) error {
setOptimizedJSONSchema(&c.Default, data, forge.UnforgeChainID)
return nil
return setOptimizedJSONSchema(&c.Default, data, forge.UnforgeChainID, ChainIDValidator)
}

// FindByName -
Expand Down
11 changes: 5 additions & 6 deletions internal/bcd/ast/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,11 @@ func (d *Default) ToJSONSchema() (*JSONSchema, error) {

// FromJSONSchema -
func (d *Default) FromJSONSchema(data map[string]interface{}) error {
for key := range data {
if key == d.GetName() {
d.Value = data[key]
d.ValueKind = valueKindString
break
}
key := d.GetName()

if value, ok := data[key]; ok {
d.Value = value
d.ValueKind = valueKindString
}
return nil
}
Expand Down
59 changes: 35 additions & 24 deletions internal/bcd/ast/jsonschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ type JSONSchema struct {
XOptions map[string]interface{} `json:"x-options,omitempty"`
}

// Optimizer -
type Optimizer func(string) (string, error)

func getStringJSONSchema(d Default) *JSONSchema {
return wrapObject(&JSONSchema{
Prim: d.Prim,
Expand Down Expand Up @@ -70,49 +73,57 @@ func getAddressJSONSchema(d Default) *JSONSchema {
}

func setIntJSONSchema(d *Default, data map[string]interface{}) {
for key := range data {
if key == d.GetName() {
switch v := data[key].(type) {
case float64:
i := big.NewInt(0)
i, _ = big.NewFloat(v).Int(i)
d.Value = &types.BigInt{Int: i}
case string:
d.Value = types.NewBigIntFromString(v)
}
d.ValueKind = valueKindInt
break
key := d.GetName()
if value, ok := data[key]; ok {
switch v := value.(type) {
case float64:
i := big.NewInt(0)
i, _ = big.NewFloat(v).Int(i)
d.Value = &types.BigInt{Int: i}
case string:
d.Value = types.NewBigIntFromString(v)
}
d.ValueKind = valueKindInt
}
}

func setBytesJSONSchema(d *Default, data map[string]interface{}) error {
for key := range data {
if key == d.GetName() {
if _, err := hex.DecodeString(data[key].(string)); err != nil {
return errors.Errorf("invalid bytes string: %s=%v", key, data[key])
key := d.GetName()
if value, ok := data[key]; ok {
if str, ok := value.(string); ok {
if err := BytesValidator(str); err != nil {
return err
}
if _, err := hex.DecodeString(str); err != nil {
return errors.Errorf("bytes decoding error: %s=%v", key, value)
}

d.Value = data[key]
d.Value = value
d.ValueKind = valueKindBytes
return nil
}
}
return nil
}

func setOptimizedJSONSchema(d *Default, data map[string]interface{}, optimizer func(string) (string, error)) {
for key, value := range data {
if key == d.GetName() {
val, err := optimizer(value.(string))
func setOptimizedJSONSchema(d *Default, data map[string]interface{}, optimizer Optimizer, validator Validator[string]) error {
key := d.GetName()
if value, ok := data[key]; ok {
if str, ok := value.(string); ok {
if validator != nil {
if err := validator(str); err != nil {
return err
}
}

val, err := optimizer(str)
if err != nil {
val = value.(string)
val = str
}
d.ValueKind = valueKindString
d.Value = val
break
}
}
return nil
}

type mergeFields struct {
Expand Down
3 changes: 1 addition & 2 deletions internal/bcd/ast/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ func (k *Key) Distinguish(x Distinguishable) (*MiguelNode, error) {

// FromJSONSchema -
func (k *Key) FromJSONSchema(data map[string]interface{}) error {
setOptimizedJSONSchema(&k.Default, data, forge.UnforgePublicKey)
return nil
return setOptimizedJSONSchema(&k.Default, data, forge.UnforgePublicKey, PublicKeyValidator)
}

// FindByName -
Expand Down
3 changes: 1 addition & 2 deletions internal/bcd/ast/key_hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ func (k *KeyHash) Distinguish(x Distinguishable) (*MiguelNode, error) {

// FromJSONSchema -
func (k *KeyHash) FromJSONSchema(data map[string]interface{}) error {
setOptimizedJSONSchema(&k.Default, data, forge.UnforgeAddress)
return nil
return setOptimizedJSONSchema(&k.Default, data, forge.UnforgeAddress, nil)
}

// FindByName -
Expand Down
3 changes: 1 addition & 2 deletions internal/bcd/ast/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ func (s *Signature) Distinguish(x Distinguishable) (*MiguelNode, error) {

// FromJSONSchema -
func (s *Signature) FromJSONSchema(data map[string]interface{}) error {
setOptimizedJSONSchema(&s.Default, data, forge.UnforgeSignature)
return nil
return setOptimizedJSONSchema(&s.Default, data, forge.UnforgeSignature, SignatureValidator)
}

// FindByName -
Expand Down
25 changes: 12 additions & 13 deletions internal/bcd/ast/timestamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,20 @@ func (t *Timestamp) ToBaseNode(optimized bool) (*base.Node, error) {

// FromJSONSchema -
func (t *Timestamp) FromJSONSchema(data map[string]interface{}) error {
for key := range data {
if key == t.GetName() {
t.ValueKind = valueKindInt
switch val := data[key].(type) {
case string:
ts, err := time.Parse(time.RFC3339, val)
if err != nil {
return err
}
t.Value = types.NewBigInt(ts.UTC().Unix())
case float64:
t.Value = types.NewBigInt(int64(val))
key := t.GetName()
if value, ok := data[key]; ok {
t.ValueKind = valueKindInt
switch val := value.(type) {
case string:
ts, err := time.Parse(time.RFC3339, val)
if err != nil {
return errors.Wrapf(ErrValidation, "time should be in RFC3339 %s=%s", key, val)
}
break
t.Value = types.NewBigInt(ts.UTC().Unix())
case float64:
t.Value = types.NewBigInt(int64(val))
}

}
return nil
}
Expand Down
136 changes: 136 additions & 0 deletions internal/bcd/ast/validators.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package ast

import (
"regexp"
"strings"

"github.com/baking-bad/bcdhub/internal/bcd"
"github.com/baking-bad/bcdhub/internal/bcd/encoding"
"github.com/pkg/errors"
)

// errors
var (
ErrValidation = errors.New("validation error")
)

// ValidatorConstraint -
type ValidatorConstraint interface {
~string
}

// Validator -
type Validator[T ValidatorConstraint] func(T) error

var (
hexRegex = regexp.MustCompile("^[0-9a-fA-F]+$")
)

// AddressValidator -
func AddressValidator(value string) error {
switch len(value) {
case 44, 42:
if !hexRegex.MatchString(value) {
return errors.Wrapf(ErrValidation, "address '%s' should be hexademical without prefixes", value)
}
case 36:
if !bcd.IsAddressLazy(value) {
return errors.Wrapf(ErrValidation, "invalid address '%s'", value)
}
if !bcd.IsAddress(value) {
return errors.Wrapf(ErrValidation, "invalid address '%s'", value)
}
default:
return errors.Wrap(ErrValidation, "invalid address length")
}

return nil
}

// BakerHashValidator -
func BakerHashValidator(value string) error {
switch len(value) {
case 40:
if !hexRegex.MatchString(value) {
return errors.Wrapf(ErrValidation, "baker hash '%s' should be hexademical without prefixes", value)
}
case 36:
if !bcd.IsBakerHash(value) {
return errors.Wrapf(ErrValidation, "invalid baker hash '%s'", value)
}
default:
return errors.Wrap(ErrValidation, "invalid baker hash length")
}

return nil
}

// PublicKeyValidator -
func PublicKeyValidator(value string) error {
switch len(value) {
case 68, 66:
if !hexRegex.MatchString(value) {
return errors.Wrapf(ErrValidation, "public key '%s' should be hexademical without prefixes", value)
}
case 55, 54:
if strings.HasPrefix(value, encoding.PrefixED25519PublicKey) ||
strings.HasPrefix(value, encoding.PrefixP256PublicKey) ||
strings.HasPrefix(value, encoding.PrefixSecp256k1PublicKey) {
return nil
}
return errors.Wrapf(ErrValidation, "invalid public key '%s'", value)
default:
return errors.Wrap(ErrValidation, "invalid public key length")
}

return nil
}

// BytesValidator -
func BytesValidator(value string) error {
if len(value)%2 > 0 {
return errors.Wrapf(ErrValidation, "invalid bytes in hex length '%s'", value)
}
if !hexRegex.MatchString(value) {
return errors.Wrapf(ErrValidation, "bytes '%s' should be hexademical without prefixes", value)
}
return nil
}

// ChainIDValidator -
func ChainIDValidator(value string) error {
switch len(value) {
case 8:
if !hexRegex.MatchString(value) {
return errors.Wrapf(ErrValidation, "chain id '%s' should be hexademical without prefixes", value)
}
case 15:
if strings.HasPrefix(value, encoding.PrefixChainID) {
return nil
}
return errors.Wrapf(ErrValidation, "invalid chain id '%s'", value)
default:
return errors.Wrap(ErrValidation, "invalid chain id length")
}

return nil
}

// SignatureValidator -
func SignatureValidator(value string) error {
switch len(value) {
case 128:
if !hexRegex.MatchString(value) {
return errors.Wrapf(ErrValidation, "signature '%s' should be hexademical without prefixes", value)
}
case 96:
if strings.HasPrefix(value, encoding.PrefixGenericSignature) {
return nil
}
return errors.Wrapf(ErrValidation, "invalid signature '%s'", value)
default:
return errors.Wrap(ErrValidation, "invalid signature length")
}

return nil
}
Loading