generated from TrueCloudLab/basic
Add chain serializers #42
17 changed files with 1986 additions and 73 deletions
2
.gitattributes
vendored
2
.gitattributes
vendored
|
@ -1,2 +1,4 @@
|
|||
/**/*.pb.go -diff -merge
|
||||
/**/*.pb.go linguist-generated=true
|
||||
/**/*_easyjson.go -diff -merge
|
||||
/**/*_easyjson.go linguist-generated=true
|
14
Makefile
14
Makefile
|
@ -5,6 +5,8 @@ TMP_DIR := .cache
|
|||
OUTPUT_LINT_DIR ?= $(shell pwd)/bin
|
||||
LINT_VERSION ?= 1.55.1
|
||||
LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT_VERSION)
|
||||
EASYJSON_VERSION ?= $(shell go list -f '{{.Version}}' -m github.com/mailru/easyjson)
|
||||
EASYJSON_DIR ?= $(shell pwd)/bin/easyjson-$(EASYJSON_VERSION)
|
||||
|
||||
# Run all code formatters
|
||||
fmts: fmt imports
|
||||
|
@ -60,3 +62,15 @@ staticcheck-install:
|
|||
# Run staticcheck
|
||||
staticcheck-run:
|
||||
@staticcheck ./...
|
||||
|
||||
easyjson-install:
|
||||
@rm -rf $(EASYJSON_DIR)
|
||||
@mkdir -p $(EASYJSON_DIR)
|
||||
@GOBIN=$(EASYJSON_DIR) go install github.com/mailru/easyjson/...@$(EASYJSON_VERSION)
|
||||
|
||||
generate:
|
||||
@if [ ! -d "$(EASYJSON_DIR)" ]; then \
|
||||
make easyjson-install; \
|
||||
fi
|
||||
find ./ -name "_easyjson.go" -exec rm -rf {} \;
|
||||
$(EASYJSON_DIR)/easyjson ./pkg/chain/chain.go
|
4
go.mod
4
go.mod
|
@ -4,6 +4,8 @@ go 1.20
|
|||
|
||||
require (
|
||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.18.1-0.20231129062201-a1b61d394958
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/mailru/easyjson v0.7.7
|
||||
github.com/nspcc-dev/neo-go v0.103.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
|
||||
|
@ -12,8 +14,8 @@ require (
|
|||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.6.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 // indirect
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -11,6 +11,10 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4=
|
||||
github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg=
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
|
@ -11,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
// ID is the ID of rule chain.
|
||||
type ID string
|
||||
type ID []byte
|
||||
|
||||
// MatchType is the match type for chain rules.
|
||||
type MatchType uint8
|
||||
|
@ -23,6 +22,7 @@ const (
|
|||
MatchTypeFirstMatch MatchType = 1
|
||||
)
|
||||
|
||||
//easyjson:json
|
||||
type Chain struct {
|
||||
ID ID
|
||||
|
||||
|
@ -31,21 +31,8 @@ type Chain struct {
|
|||
MatchType MatchType
|
||||
}
|
||||
|
||||
func (id ID) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal([]byte(id))
|
||||
}
|
||||
|
||||
func (id *ID) UnmarshalJSON(data []byte) error {
|
||||
var idRaw []byte
|
||||
if err := json.Unmarshal(data, &idRaw); err != nil {
|
||||
return err
|
||||
}
|
||||
*id = ID(idRaw)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Chain) Bytes() []byte {
|
||||
data, err := json.Marshal(c)
|
||||
data, err := c.MarshalBinary()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -53,7 +40,7 @@ func (c *Chain) Bytes() []byte {
|
|||
}
|
||||
|
||||
func (c *Chain) DecodeBytes(b []byte) error {
|
||||
return json.Unmarshal(b, c)
|
||||
return c.UnmarshalBinary(b)
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
|
@ -123,45 +110,36 @@ const (
|
|||
CondSliceContains
|
||||
)
|
||||
|
||||
var condToStr = []struct {
|
||||
ct ConditionType
|
||||
str string
|
||||
}{
|
||||
{CondStringEquals, "StringEquals"},
|
||||
{CondStringNotEquals, "StringNotEquals"},
|
||||
{CondStringEqualsIgnoreCase, "StringEqualsIgnoreCase"},
|
||||
{CondStringNotEqualsIgnoreCase, "StringNotEqualsIgnoreCase"},
|
||||
{CondStringLike, "StringLike"},
|
||||
{CondStringNotLike, "StringNotLike"},
|
||||
{CondStringLessThan, "StringLessThan"},
|
||||
{CondStringLessThanEquals, "StringLessThanEquals"},
|
||||
{CondStringGreaterThan, "StringGreaterThan"},
|
||||
{CondStringGreaterThanEquals, "StringGreaterThanEquals"},
|
||||
{CondNumericEquals, "NumericEquals"},
|
||||
{CondNumericNotEquals, "NumericNotEquals"},
|
||||
{CondNumericLessThan, "NumericLessThan"},
|
||||
{CondNumericLessThanEquals, "NumericLessThanEquals"},
|
||||
{CondNumericGreaterThan, "NumericGreaterThan"},
|
||||
{CondNumericGreaterThanEquals, "NumericGreaterThanEquals"},
|
||||
{CondSliceContains, "SliceContains"},
|
||||
}
|
||||
|
||||
func (c ConditionType) String() string {
|
||||
switch c {
|
||||
case CondStringEquals:
|
||||
return "StringEquals"
|
||||
case CondStringNotEquals:
|
||||
return "StringNotEquals"
|
||||
case CondStringEqualsIgnoreCase:
|
||||
return "StringEqualsIgnoreCase"
|
||||
case CondStringNotEqualsIgnoreCase:
|
||||
return "StringNotEqualsIgnoreCase"
|
||||
case CondStringLike:
|
||||
return "StringLike"
|
||||
case CondStringNotLike:
|
||||
return "StringNotLike"
|
||||
case CondStringLessThan:
|
||||
return "StringLessThan"
|
||||
case CondStringLessThanEquals:
|
||||
return "StringLessThanEquals"
|
||||
case CondStringGreaterThan:
|
||||
return "StringGreaterThan"
|
||||
case CondStringGreaterThanEquals:
|
||||
return "StringGreaterThanEquals"
|
||||
case CondNumericEquals:
|
||||
return "NumericEquals"
|
||||
case CondNumericNotEquals:
|
||||
return "NumericNotEquals"
|
||||
case CondNumericLessThan:
|
||||
return "NumericLessThan"
|
||||
case CondNumericLessThanEquals:
|
||||
return "NumericLessThanEquals"
|
||||
case CondNumericGreaterThan:
|
||||
return "NumericGreaterThan"
|
||||
case CondNumericGreaterThanEquals:
|
||||
return "NumericGreaterThanEquals"
|
||||
case CondSliceContains:
|
||||
return "SliceContains"
|
||||
default:
|
||||
return "unknown condition type"
|
||||
for _, v := range condToStr {
|
||||
if v.ct == c {
|
||||
return v.str
|
||||
}
|
||||
}
|
||||
return "unknown condition type"
|
||||
}
|
||||
|
||||
const condSliceContainsDelimiter = "\x00"
|
||||
|
|
463
pkg/chain/chain_easyjson.go
generated
Normal file
463
pkg/chain/chain_easyjson.go
generated
Normal file
|
@ -0,0 +1,463 @@
|
|||
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
|
||||
|
||||
package chain
|
||||
|
||||
import (
|
||||
json "encoding/json"
|
||||
easyjson "github.com/mailru/easyjson"
|
||||
jlexer "github.com/mailru/easyjson/jlexer"
|
||||
jwriter "github.com/mailru/easyjson/jwriter"
|
||||
)
|
||||
|
||||
// suppress unused package warning
|
||||
var (
|
||||
_ *json.RawMessage
|
||||
_ *jlexer.Lexer
|
||||
_ *jwriter.Writer
|
||||
_ easyjson.Marshaler
|
||||
)
|
||||
|
||||
func easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain(in *jlexer.Lexer, out *Chain) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "ID":
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
out.ID = nil
|
||||
} else {
|
||||
out.ID = in.Bytes()
|
||||
}
|
||||
case "Rules":
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
out.Rules = nil
|
||||
} else {
|
||||
in.Delim('[')
|
||||
if out.Rules == nil {
|
||||
if !in.IsDelim(']') {
|
||||
out.Rules = make([]Rule, 0, 0)
|
||||
} else {
|
||||
out.Rules = []Rule{}
|
||||
}
|
||||
} else {
|
||||
out.Rules = (out.Rules)[:0]
|
||||
}
|
||||
for !in.IsDelim(']') {
|
||||
var v2 Rule
|
||||
easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain1(in, &v2)
|
||||
out.Rules = append(out.Rules, v2)
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim(']')
|
||||
}
|
||||
case "MatchType":
|
||||
(out.MatchType).UnmarshalEasyJSON(in)
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain(out *jwriter.Writer, in Chain) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
{
|
||||
const prefix string = ",\"ID\":"
|
||||
out.RawString(prefix[1:])
|
||||
out.Base64Bytes(in.ID)
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"Rules\":"
|
||||
out.RawString(prefix)
|
||||
if in.Rules == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
|
||||
out.RawString("null")
|
||||
} else {
|
||||
out.RawByte('[')
|
||||
for v5, v6 := range in.Rules {
|
||||
if v5 > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain1(out, v6)
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"MatchType\":"
|
||||
out.RawString(prefix)
|
||||
(in.MatchType).MarshalEasyJSON(out)
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
|
||||
// MarshalJSON supports json.Marshaler interface
|
||||
func (v Chain) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain(&w, v)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
|
||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
||||
func (v Chain) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain(w, v)
|
||||
}
|
||||
|
||||
// UnmarshalJSON supports json.Unmarshaler interface
|
||||
func (v *Chain) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain(&r, v)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
||||
func (v *Chain) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain(l, v)
|
||||
}
|
||||
func easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain1(in *jlexer.Lexer, out *Rule) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "Status":
|
||||
(out.Status).UnmarshalEasyJSON(in)
|
||||
case "Actions":
|
||||
easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain2(in, &out.Actions)
|
||||
case "Resources":
|
||||
easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain3(in, &out.Resources)
|
||||
case "Any":
|
||||
out.Any = bool(in.Bool())
|
||||
case "Condition":
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
out.Condition = nil
|
||||
} else {
|
||||
in.Delim('[')
|
||||
if out.Condition == nil {
|
||||
if !in.IsDelim(']') {
|
||||
out.Condition = make([]Condition, 0, 1)
|
||||
} else {
|
||||
out.Condition = []Condition{}
|
||||
}
|
||||
} else {
|
||||
out.Condition = (out.Condition)[:0]
|
||||
}
|
||||
for !in.IsDelim(']') {
|
||||
var v7 Condition
|
||||
easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain4(in, &v7)
|
||||
out.Condition = append(out.Condition, v7)
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim(']')
|
||||
}
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain1(out *jwriter.Writer, in Rule) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
{
|
||||
const prefix string = ",\"Status\":"
|
||||
out.RawString(prefix[1:])
|
||||
(in.Status).MarshalEasyJSON(out)
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"Actions\":"
|
||||
out.RawString(prefix)
|
||||
easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain2(out, in.Actions)
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"Resources\":"
|
||||
out.RawString(prefix)
|
||||
easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain3(out, in.Resources)
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"Any\":"
|
||||
out.RawString(prefix)
|
||||
out.Bool(bool(in.Any))
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"Condition\":"
|
||||
out.RawString(prefix)
|
||||
if in.Condition == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
|
||||
out.RawString("null")
|
||||
} else {
|
||||
out.RawByte('[')
|
||||
for v8, v9 := range in.Condition {
|
||||
if v8 > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain4(out, v9)
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
func easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain4(in *jlexer.Lexer, out *Condition) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "Op":
|
||||
(out.Op).UnmarshalEasyJSON(in)
|
||||
case "Object":
|
||||
(out.Object).UnmarshalEasyJSON(in)
|
||||
case "Key":
|
||||
out.Key = string(in.String())
|
||||
case "Value":
|
||||
out.Value = string(in.String())
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain4(out *jwriter.Writer, in Condition) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
{
|
||||
const prefix string = ",\"Op\":"
|
||||
out.RawString(prefix[1:])
|
||||
(in.Op).MarshalEasyJSON(out)
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"Object\":"
|
||||
out.RawString(prefix)
|
||||
(in.Object).MarshalEasyJSON(out)
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"Key\":"
|
||||
out.RawString(prefix)
|
||||
out.String(string(in.Key))
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"Value\":"
|
||||
out.RawString(prefix)
|
||||
out.String(string(in.Value))
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
func easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain3(in *jlexer.Lexer, out *Resources) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "Inverted":
|
||||
out.Inverted = bool(in.Bool())
|
||||
case "Names":
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
out.Names = nil
|
||||
} else {
|
||||
in.Delim('[')
|
||||
if out.Names == nil {
|
||||
if !in.IsDelim(']') {
|
||||
out.Names = make([]string, 0, 4)
|
||||
} else {
|
||||
out.Names = []string{}
|
||||
}
|
||||
} else {
|
||||
out.Names = (out.Names)[:0]
|
||||
}
|
||||
for !in.IsDelim(']') {
|
||||
var v10 string
|
||||
v10 = string(in.String())
|
||||
out.Names = append(out.Names, v10)
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim(']')
|
||||
}
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain3(out *jwriter.Writer, in Resources) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
{
|
||||
const prefix string = ",\"Inverted\":"
|
||||
out.RawString(prefix[1:])
|
||||
out.Bool(bool(in.Inverted))
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"Names\":"
|
||||
out.RawString(prefix)
|
||||
if in.Names == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
|
||||
out.RawString("null")
|
||||
} else {
|
||||
out.RawByte('[')
|
||||
for v11, v12 := range in.Names {
|
||||
if v11 > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
out.String(string(v12))
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
func easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain2(in *jlexer.Lexer, out *Actions) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "Inverted":
|
||||
out.Inverted = bool(in.Bool())
|
||||
case "Names":
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
out.Names = nil
|
||||
} else {
|
||||
in.Delim('[')
|
||||
if out.Names == nil {
|
||||
if !in.IsDelim(']') {
|
||||
out.Names = make([]string, 0, 4)
|
||||
} else {
|
||||
out.Names = []string{}
|
||||
}
|
||||
} else {
|
||||
out.Names = (out.Names)[:0]
|
||||
}
|
||||
for !in.IsDelim(']') {
|
||||
var v13 string
|
||||
v13 = string(in.String())
|
||||
out.Names = append(out.Names, v13)
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim(']')
|
||||
}
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain2(out *jwriter.Writer, in Actions) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
{
|
||||
const prefix string = ",\"Inverted\":"
|
||||
out.RawString(prefix[1:])
|
||||
out.Bool(bool(in.Inverted))
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"Names\":"
|
||||
out.RawString(prefix)
|
||||
if in.Names == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
|
||||
out.RawString("null")
|
||||
} else {
|
||||
out.RawByte('[')
|
||||
for v14, v15 := range in.Names {
|
||||
if v14 > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
out.String(string(v15))
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
257
pkg/chain/marshal_binary.go
Normal file
257
pkg/chain/marshal_binary.go
Normal file
|
@ -0,0 +1,257 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/marshal"
|
||||
)
|
||||
|
||||
const (
|
||||
ChainMarshalVersion uint8 = 0 // increase if breaking change
|
||||
)
|
||||
|
||||
var (
|
||||
_ encoding.BinaryMarshaler = (*Chain)(nil)
|
||||
_ encoding.BinaryUnmarshaler = (*Chain)(nil)
|
||||
)
|
||||
|
||||
func (c *Chain) MarshalBinary() ([]byte, error) {
|
||||
s := marshal.UInt8Size // Marshaller version
|
||||
s += marshal.UInt8Size // Chain version
|
||||
s += marshal.SliceSize(c.ID, func(byte) int { return marshal.ByteSize })
|
||||
s += marshal.SliceSize(c.Rules, ruleSize)
|
||||
s += marshal.UInt8Size // MatchType
|
||||
|
||||
buf := make([]byte, s)
|
||||
var offset int
|
||||
var err error
|
||||
offset, err = marshal.UInt8Marshal(buf, offset, marshal.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
offset, err = marshal.UInt8Marshal(buf, offset, ChainMarshalVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
offset, err = marshal.SliceMarshal(buf, offset, c.ID, marshal.ByteMarshal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
offset, err = marshal.SliceMarshal(buf, offset, c.Rules, marshalRule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
offset, err = marshal.UInt8Marshal(buf, offset, uint8(c.MatchType))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := marshal.VerifyMarshal(buf, offset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (c *Chain) UnmarshalBinary(data []byte) error {
|
||||
var offset int
|
||||
|
||||
marshallerVersion, offset, err := marshal.UInt8Unmarshal(data, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if marshallerVersion != marshal.Version {
|
||||
return fmt.Errorf("unsupported marshaller version %d", marshallerVersion)
|
||||
}
|
||||
|
||||
chainVersion, offset, err := marshal.UInt8Unmarshal(data, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if chainVersion != ChainMarshalVersion {
|
||||
return fmt.Errorf("unsupported chain version %d", chainVersion)
|
||||
}
|
||||
|
||||
idBytes, offset, err := marshal.SliceUnmarshal(data, offset, marshal.ByteUnmarshal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.ID = ID(idBytes)
|
||||
|
||||
c.Rules, offset, err = marshal.SliceUnmarshal(data, offset, unmarshalRule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
matchTypeV, offset, err := marshal.UInt8Unmarshal(data, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.MatchType = MatchType(matchTypeV)
|
||||
|
||||
return marshal.VerifyUnmarshal(data, offset)
|
||||
}
|
||||
|
||||
func ruleSize(r Rule) int {
|
||||
s := marshal.ByteSize // Status
|
||||
s += actionsSize(r.Actions)
|
||||
s += resourcesSize(r.Resources)
|
||||
s += marshal.BoolSize // Any
|
||||
s += marshal.SliceSize(r.Condition, conditionSize)
|
||||
return s
|
||||
}
|
||||
|
||||
func marshalRule(buf []byte, offset int, r Rule) (int, error) {
|
||||
offset, err := marshal.ByteMarshal(buf, offset, byte(r.Status))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset, err = marshalActions(buf, offset, r.Actions)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset, err = marshalResources(buf, offset, r.Resources)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset, err = marshal.BoolMarshal(buf, offset, r.Any)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return marshal.SliceMarshal(buf, offset, r.Condition, marshalCondition)
|
||||
}
|
||||
|
||||
func unmarshalRule(buf []byte, offset int) (Rule, int, error) {
|
||||
var r Rule
|
||||
statusV, offset, err := marshal.ByteUnmarshal(buf, offset)
|
||||
if err != nil {
|
||||
return Rule{}, 0, err
|
||||
}
|
||||
r.Status = Status(statusV)
|
||||
|
||||
r.Actions, offset, err = unmarshalActions(buf, offset)
|
||||
if err != nil {
|
||||
return Rule{}, 0, err
|
||||
}
|
||||
|
||||
r.Resources, offset, err = unmarshalResources(buf, offset)
|
||||
if err != nil {
|
||||
return Rule{}, 0, err
|
||||
}
|
||||
|
||||
r.Any, offset, err = marshal.BoolUnmarshal(buf, offset)
|
||||
if err != nil {
|
||||
return Rule{}, 0, err
|
||||
}
|
||||
|
||||
r.Condition, offset, err = marshal.SliceUnmarshal(buf, offset, unmarshalCondition)
|
||||
if err != nil {
|
||||
return Rule{}, 0, err
|
||||
}
|
||||
|
||||
return r, offset, nil
|
||||
}
|
||||
|
||||
func actionsSize(a Actions) int {
|
||||
return marshal.BoolSize + // Inverted
|
||||
marshal.SliceSize(a.Names, marshal.StringSize)
|
||||
}
|
||||
|
||||
func marshalActions(buf []byte, offset int, a Actions) (int, error) {
|
||||
offset, err := marshal.BoolMarshal(buf, offset, a.Inverted)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return marshal.SliceMarshal(buf, offset, a.Names, marshal.StringMarshal)
|
||||
}
|
||||
|
||||
func unmarshalActions(buf []byte, offset int) (Actions, int, error) {
|
||||
var a Actions
|
||||
var err error
|
||||
a.Inverted, offset, err = marshal.BoolUnmarshal(buf, offset)
|
||||
if err != nil {
|
||||
return Actions{}, 0, err
|
||||
}
|
||||
a.Names, offset, err = marshal.SliceUnmarshal(buf, offset, marshal.StringUnmarshal)
|
||||
if err != nil {
|
||||
return Actions{}, 0, err
|
||||
}
|
||||
return a, offset, nil
|
||||
}
|
||||
|
||||
func resourcesSize(r Resources) int {
|
||||
return marshal.BoolSize + // Inverted
|
||||
marshal.SliceSize(r.Names, marshal.StringSize)
|
||||
}
|
||||
|
||||
func marshalResources(buf []byte, offset int, r Resources) (int, error) {
|
||||
offset, err := marshal.BoolMarshal(buf, offset, r.Inverted)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return marshal.SliceMarshal(buf, offset, r.Names, marshal.StringMarshal)
|
||||
}
|
||||
|
||||
func unmarshalResources(buf []byte, offset int) (Resources, int, error) {
|
||||
var r Resources
|
||||
var err error
|
||||
r.Inverted, offset, err = marshal.BoolUnmarshal(buf, offset)
|
||||
if err != nil {
|
||||
return Resources{}, 0, err
|
||||
}
|
||||
r.Names, offset, err = marshal.SliceUnmarshal(buf, offset, marshal.StringUnmarshal)
|
||||
if err != nil {
|
||||
return Resources{}, 0, err
|
||||
}
|
||||
return r, offset, nil
|
||||
}
|
||||
|
||||
func conditionSize(c Condition) int {
|
||||
return marshal.ByteSize + // Op
|
||||
marshal.ByteSize + // Object
|
||||
marshal.StringSize(c.Key) +
|
||||
marshal.StringSize(c.Value)
|
||||
}
|
||||
|
||||
func marshalCondition(buf []byte, offset int, c Condition) (int, error) {
|
||||
offset, err := marshal.ByteMarshal(buf, offset, byte(c.Op))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset, err = marshal.ByteMarshal(buf, offset, byte(c.Object))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset, err = marshal.StringMarshal(buf, offset, c.Key)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return marshal.StringMarshal(buf, offset, c.Value)
|
||||
}
|
||||
|
||||
func unmarshalCondition(buf []byte, offset int) (Condition, int, error) {
|
||||
var c Condition
|
||||
opV, offset, err := marshal.ByteUnmarshal(buf, offset)
|
||||
if err != nil {
|
||||
return Condition{}, 0, err
|
||||
}
|
||||
c.Op = ConditionType(opV)
|
||||
|
||||
obV, offset, err := marshal.ByteUnmarshal(buf, offset)
|
||||
if err != nil {
|
||||
return Condition{}, 0, err
|
||||
}
|
||||
c.Object = ObjectType(obV)
|
||||
|
||||
c.Key, offset, err = marshal.StringUnmarshal(buf, offset)
|
||||
if err != nil {
|
||||
return Condition{}, 0, err
|
||||
}
|
||||
|
||||
c.Value, offset, err = marshal.StringUnmarshal(buf, offset)
|
||||
if err != nil {
|
||||
return Condition{}, 0, err
|
||||
}
|
||||
|
||||
return c, offset, nil
|
||||
}
|
272
pkg/chain/marshal_binary_test.go
Normal file
272
pkg/chain/marshal_binary_test.go
Normal file
|
@ -0,0 +1,272 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestChainMarshalling(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, id := range generateTestIDs() {
|
||||
for _, rules := range generateTestRules() {
|
||||
for _, matchType := range generateTestMatchTypes() {
|
||||
performMarshalTest(t, id, rules, matchType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidChainData(t *testing.T) {
|
||||
var ch Chain
|
||||
require.Error(t, ch.UnmarshalBinary(nil))
|
||||
require.Error(t, ch.UnmarshalBinary([]byte{}))
|
||||
require.Error(t, ch.UnmarshalBinary([]byte{1, 2, 3}))
|
||||
require.Error(t, ch.UnmarshalBinary([]byte("\x00\x00:aws:iam::namespace:group/so\x82\x82\x82\x82\x82\x82u\x82")))
|
||||
}
|
||||
|
||||
func FuzzUnmarshal(f *testing.F) {
|
||||
for _, id := range generateTestIDs() {
|
||||
for _, rules := range generateTestRules() {
|
||||
for _, matchType := range generateTestMatchTypes() {
|
||||
|
||||
chain := Chain{
|
||||
ID: id,
|
||||
Rules: rules,
|
||||
MatchType: matchType,
|
||||
}
|
||||
data, err := chain.MarshalBinary()
|
||||
require.NoError(f, err)
|
||||
f.Add(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
var ch Chain
|
||||
require.NotPanics(t, func() {
|
||||
_ = ch.UnmarshalBinary(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func performMarshalTest(t *testing.T, id ID, r []Rule, mt MatchType) {
|
||||
chain := Chain{
|
||||
ID: id,
|
||||
Rules: r,
|
||||
MatchType: mt,
|
||||
}
|
||||
data, err := chain.MarshalBinary()
|
||||
require.NoError(t, err)
|
||||
|
||||
var unmarshalledChain Chain
|
||||
require.NoError(t, unmarshalledChain.UnmarshalBinary(data))
|
||||
|
||||
require.Equal(t, chain, unmarshalledChain)
|
||||
}
|
||||
|
||||
func generateTestIDs() []ID {
|
||||
return []ID{
|
||||
ID(""),
|
||||
ID(uuid.New().String()),
|
||||
ID("*::/"),
|
||||
ID("avada kedavra"),
|
||||
ID("arn:aws:iam::namespace:group/some_group"),
|
||||
ID("$Object:homomorphicHash"),
|
||||
ID("native:container/ns/9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J"),
|
||||
}
|
||||
}
|
||||
|
||||
func generateTestRules() [][]Rule {
|
||||
result := [][]Rule{
|
||||
nil,
|
||||
{},
|
||||
{},
|
||||
}
|
||||
|
||||
for _, st := range generateTestStatuses() {
|
||||
for _, act := range generateTestActions() {
|
||||
for _, res := range generateTestResources() {
|
||||
for _, cond := range generateTestConditions() {
|
||||
result[2] = append(result[2], Rule{
|
||||
Status: st,
|
||||
Actions: act,
|
||||
Resources: res,
|
||||
Condition: cond,
|
||||
Any: true,
|
||||
})
|
||||
result[2] = append(result[2], Rule{
|
||||
Status: st,
|
||||
Actions: act,
|
||||
Resources: res,
|
||||
Condition: cond,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func generateTestStatuses() []Status {
|
||||
return []Status{
|
||||
Allow,
|
||||
NoRuleFound,
|
||||
AccessDenied,
|
||||
QuotaLimitReached,
|
||||
}
|
||||
}
|
||||
|
||||
func generateTestActions() []Actions {
|
||||
return []Actions{
|
||||
{
|
||||
Inverted: true,
|
||||
Names: nil,
|
||||
},
|
||||
{
|
||||
Names: nil,
|
||||
},
|
||||
{
|
||||
Inverted: true,
|
||||
Names: []string{},
|
||||
},
|
||||
{
|
||||
Names: []string{},
|
||||
},
|
||||
{
|
||||
Inverted: true,
|
||||
Names: []string{native.MethodPutObject},
|
||||
},
|
||||
{
|
||||
Names: []string{native.MethodPutObject},
|
||||
},
|
||||
{
|
||||
Inverted: true,
|
||||
Names: []string{native.MethodPutObject, native.MethodDeleteContainer, native.MethodDeleteObject},
|
||||
},
|
||||
{
|
||||
Names: []string{native.MethodPutObject, native.MethodDeleteContainer, native.MethodDeleteObject},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func generateTestResources() []Resources {
|
||||
return []Resources{
|
||||
{
|
||||
Inverted: true,
|
||||
Names: nil,
|
||||
},
|
||||
{
|
||||
Names: nil,
|
||||
},
|
||||
{
|
||||
Inverted: true,
|
||||
Names: []string{},
|
||||
},
|
||||
{
|
||||
Names: []string{},
|
||||
},
|
||||
{
|
||||
Inverted: true,
|
||||
Names: []string{native.ResourceFormatAllObjects},
|
||||
},
|
||||
{
|
||||
Names: []string{native.ResourceFormatAllObjects},
|
||||
},
|
||||
{
|
||||
Inverted: true,
|
||||
Names: []string{
|
||||
native.ResourceFormatAllObjects,
|
||||
fmt.Sprintf(native.ResourceFormatRootContainer, "9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Names: []string{
|
||||
native.ResourceFormatAllObjects,
|
||||
fmt.Sprintf(native.ResourceFormatRootContainer, "9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func generateTestConditions() [][]Condition {
|
||||
result := [][]Condition{
|
||||
nil,
|
||||
{},
|
||||
{},
|
||||
}
|
||||
|
||||
for _, ct := range generateTestConditionTypes() {
|
||||
for _, ot := range generateObjectTypes() {
|
||||
result[2] = append(result[2], Condition{
|
||||
Op: ct,
|
||||
Object: ot,
|
||||
Key: "",
|
||||
Value: "",
|
||||
})
|
||||
|
||||
result[2] = append(result[2], Condition{
|
||||
Op: ct,
|
||||
Object: ot,
|
||||
Key: "key",
|
||||
Value: "",
|
||||
})
|
||||
|
||||
result[2] = append(result[2], Condition{
|
||||
Op: ct,
|
||||
Object: ot,
|
||||
Key: "",
|
||||
Value: "value",
|
||||
})
|
||||
|
||||
result[2] = append(result[2], Condition{
|
||||
Op: ct,
|
||||
Object: ot,
|
||||
Key: "key",
|
||||
Value: "value",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func generateTestConditionTypes() []ConditionType {
|
||||
return []ConditionType{
|
||||
CondStringEquals,
|
||||
CondStringNotEquals,
|
||||
CondStringEqualsIgnoreCase,
|
||||
CondStringNotEqualsIgnoreCase,
|
||||
CondStringLike,
|
||||
CondStringNotLike,
|
||||
CondStringLessThan,
|
||||
CondStringLessThanEquals,
|
||||
CondStringGreaterThan,
|
||||
CondStringGreaterThanEquals,
|
||||
CondNumericEquals,
|
||||
CondNumericNotEquals,
|
||||
CondNumericLessThan,
|
||||
CondNumericLessThanEquals,
|
||||
CondNumericGreaterThan,
|
||||
CondNumericGreaterThanEquals,
|
||||
CondSliceContains,
|
||||
}
|
||||
}
|
||||
|
||||
func generateObjectTypes() []ObjectType {
|
||||
return []ObjectType{
|
||||
ObjectResource,
|
||||
ObjectRequest,
|
||||
}
|
||||
}
|
||||
|
||||
func generateTestMatchTypes() []MatchType {
|
||||
return []MatchType{
|
||||
MatchTypeDenyPriority,
|
||||
MatchTypeFirstMatch,
|
||||
}
|
||||
}
|
145
pkg/chain/marshal_json.go
Normal file
145
pkg/chain/marshal_json.go
Normal file
|
@ -0,0 +1,145 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
jlexer "github.com/mailru/easyjson/jlexer"
|
||||
jwriter "github.com/mailru/easyjson/jwriter"
|
||||
)
|
||||
|
||||
// Run `make generate`` if types added or changed
|
||||
|
||||
var matchTypeToJSONValue = []struct {
|
||||
mt MatchType
|
||||
str string
|
||||
}{
|
||||
{MatchTypeDenyPriority, "DenyPriority"},
|
||||
{MatchTypeFirstMatch, "FirstMatch"},
|
||||
}
|
||||
|
||||
var statusToJSONValue = []struct {
|
||||
s Status
|
||||
str string
|
||||
}{
|
||||
{Allow, "Allow"},
|
||||
{NoRuleFound, "NoRuleFound"},
|
||||
{AccessDenied, "AccessDenied"},
|
||||
{QuotaLimitReached, "QuotaLimitReached"},
|
||||
}
|
||||
|
||||
var objectTypeToJSONValue = []struct {
|
||||
t ObjectType
|
||||
str string
|
||||
}{
|
||||
{ObjectRequest, "Request"},
|
||||
{ObjectResource, "Resource"},
|
||||
}
|
||||
|
||||
func (mt MatchType) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
for _, p := range matchTypeToJSONValue {
|
||||
if p.mt == mt {
|
||||
w.String(p.str)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.String(strconv.FormatUint(uint64(mt), 10))
|
||||
}
|
||||
|
||||
func (mt *MatchType) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
str := l.String()
|
||||
for _, p := range matchTypeToJSONValue {
|
||||
if p.str == str {
|
||||
*mt = p.mt
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
v, err := strconv.ParseUint(str, 10, 8)
|
||||
if err != nil {
|
||||
l.AddError(fmt.Errorf("failed to parse match type: %w", err))
|
||||
return
|
||||
}
|
||||
*mt = MatchType(v)
|
||||
}
|
||||
|
||||
func (st Status) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
for _, p := range statusToJSONValue {
|
||||
if p.s == st {
|
||||
w.String(p.str)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.String(strconv.FormatUint(uint64(st), 10))
|
||||
}
|
||||
|
||||
func (st *Status) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
str := l.String()
|
||||
for _, p := range statusToJSONValue {
|
||||
if p.str == str {
|
||||
*st = p.s
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
v, err := strconv.ParseUint(str, 10, 8)
|
||||
if err != nil {
|
||||
l.AddError(fmt.Errorf("failed to parse status: %w", err))
|
||||
return
|
||||
}
|
||||
*st = Status(v)
|
||||
}
|
||||
|
||||
func (ot ObjectType) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
for _, p := range objectTypeToJSONValue {
|
||||
if p.t == ot {
|
||||
w.String(p.str)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.String(strconv.FormatUint(uint64(ot), 10))
|
||||
}
|
||||
|
||||
func (ot *ObjectType) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
str := l.String()
|
||||
for _, p := range objectTypeToJSONValue {
|
||||
if p.str == str {
|
||||
*ot = p.t
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
v, err := strconv.ParseUint(str, 10, 8)
|
||||
if err != nil {
|
||||
l.AddError(fmt.Errorf("failed to parse object type: %w", err))
|
||||
return
|
||||
}
|
||||
*ot = ObjectType(v)
|
||||
}
|
||||
|
||||
func (ct ConditionType) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
for _, p := range condToStr {
|
||||
if p.ct == ct {
|
||||
w.String(p.str)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.String(strconv.FormatUint(uint64(ct), 10))
|
||||
}
|
||||
|
||||
func (ct *ConditionType) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
str := l.String()
|
||||
for _, p := range condToStr {
|
||||
if p.str == str {
|
||||
*ct = p.ct
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
v, err := strconv.ParseUint(str, 10, 8)
|
||||
if err != nil {
|
||||
l.AddError(fmt.Errorf("failed to parse condition type: %w", err))
|
||||
return
|
||||
}
|
||||
*ct = ConditionType(v)
|
||||
}
|
121
pkg/chain/marshal_json_test.go
Normal file
121
pkg/chain/marshal_json_test.go
Normal file
|
@ -0,0 +1,121 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestID(t *testing.T) {
|
||||
key, err := keys.NewPrivateKeyFromWIF("L5eVx6HcHaFpQpvjQ3fy29uKDZ8rQ34bfMVx4XfZMm52EqafpNMg") // s3-gw key
|
||||
require.NoError(t, err)
|
||||
|
||||
chain1 := &Chain{ID: ID(key.PublicKey().GetScriptHash().BytesBE())}
|
||||
data := chain1.Bytes()
|
||||
|
||||
var chain2 Chain
|
||||
require.NoError(t, chain2.DecodeBytes(data))
|
||||
|
||||
require.Equal(t, chain1.ID, chain2.ID)
|
||||
|
||||
data, err = chain1.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, chain2.UnmarshalJSON(data))
|
||||
|
||||
require.Equal(t, chain1.ID, chain2.ID)
|
||||
}
|
||||
|
||||
func TestMatchTypeJson(t *testing.T) {
|
||||
for _, mt := range []MatchType{MatchTypeDenyPriority, MatchTypeFirstMatch, MatchType(100)} {
|
||||
var chain Chain
|
||||
chain.MatchType = mt
|
||||
|
||||
data, err := chain.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
if mt == MatchTypeDenyPriority {
|
||||
require.Equal(t, []byte("{\"ID\":null,\"Rules\":null,\"MatchType\":\"DenyPriority\"}"), data)
|
||||
} else if mt == MatchTypeFirstMatch {
|
||||
require.Equal(t, []byte("{\"ID\":null,\"Rules\":null,\"MatchType\":\"FirstMatch\"}"), data)
|
||||
} else {
|
||||
require.Equal(t, []byte(fmt.Sprintf("{\"ID\":null,\"Rules\":null,\"MatchType\":\"%d\"}", mt)), data)
|
||||
}
|
||||
|
||||
var parsed Chain
|
||||
require.NoError(t, parsed.UnmarshalJSON(data))
|
||||
require.Equal(t, chain, parsed)
|
||||
|
||||
require.Error(t, parsed.UnmarshalJSON([]byte("{\"ID\":\"\",\"Rules\":null,\"MatchType\":\"NotValid\"}")))
|
||||
}
|
||||
}
|
||||
|
||||
func TestJsonEnums(t *testing.T) {
|
||||
chain := Chain{
|
||||
ID: []byte("2cca5ae7-cee8-428d-b45f-567fb1d03f01"), // will be encoded to base64
|
||||
MatchType: MatchTypeFirstMatch,
|
||||
Rules: []Rule{
|
||||
{
|
||||
Status: AccessDenied,
|
||||
Actions: Actions{
|
||||
Names: []string{native.MethodDeleteObject, native.MethodGetContainer},
|
||||
},
|
||||
Resources: Resources{
|
||||
Names: []string{native.ResourceFormatAllObjects},
|
||||
},
|
||||
Condition: []Condition{
|
||||
{
|
||||
Op: CondStringEquals,
|
||||
Object: ObjectRequest,
|
||||
Key: native.PropertyKeyActorRole,
|
||||
Value: native.PropertyValueContainerRoleOthers,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Status: QuotaLimitReached,
|
||||
Actions: Actions{
|
||||
Inverted: true,
|
||||
Names: []string{native.MethodPutObject},
|
||||
},
|
||||
Resources: Resources{
|
||||
Names: []string{fmt.Sprintf(native.ResourceFormatRootContainerObjects, "9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J")},
|
||||
},
|
||||
Any: true,
|
||||
Condition: []Condition{
|
||||
{
|
||||
Op: CondStringNotLike,
|
||||
Object: ObjectResource,
|
||||
Key: native.PropertyKeyObjectType,
|
||||
Value: "regular",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Status: Status(100),
|
||||
Condition: []Condition{
|
||||
{
|
||||
Op: ConditionType(255),
|
||||
Object: ObjectType(128),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
data, err := chain.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
|
||||
var parsed Chain
|
||||
require.NoError(t, parsed.UnmarshalJSON(data))
|
||||
require.Equal(t, chain, parsed)
|
||||
|
||||
expected, err := os.ReadFile("./testdata/test_status_json.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, parsed.UnmarshalJSON(expected))
|
||||
require.Equal(t, chain, parsed)
|
||||
}
|
75
pkg/chain/testdata/test_status_json.json
vendored
Normal file
75
pkg/chain/testdata/test_status_json.json
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"ID": "MmNjYTVhZTctY2VlOC00MjhkLWI0NWYtNTY3ZmIxZDAzZjAx",
|
||||
"Rules": [
|
||||
{
|
||||
"Status": "AccessDenied",
|
||||
"Actions": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"DeleteObject",
|
||||
"GetContainer"
|
||||
]
|
||||
},
|
||||
"Resources": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"native:object/*"
|
||||
]
|
||||
},
|
||||
"Any": false,
|
||||
"Condition": [
|
||||
{
|
||||
"Op": "StringEquals",
|
||||
"Object": "Request",
|
||||
"Key": "$Actor:role",
|
||||
"Value": "others"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Status": "QuotaLimitReached",
|
||||
"Actions": {
|
||||
"Inverted": true,
|
||||
"Names": [
|
||||
"PutObject"
|
||||
]
|
||||
},
|
||||
"Resources": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"native:object//9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J/*"
|
||||
]
|
||||
},
|
||||
"Any": true,
|
||||
"Condition": [
|
||||
{
|
||||
"Op": "StringNotLike",
|
||||
"Object": "Resource",
|
||||
"Key": "$Object:objectType",
|
||||
"Value": "regular"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Status": "100",
|
||||
"Actions": {
|
||||
"Inverted": false,
|
||||
"Names": null
|
||||
},
|
||||
"Resources": {
|
||||
"Inverted": false,
|
||||
"Names": null
|
||||
},
|
||||
"Any": false,
|
||||
"Condition": [
|
||||
{
|
||||
"Op": "255",
|
||||
"Object": "128",
|
||||
"Key": "",
|
||||
"Value": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"MatchType": "FirstMatch"
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package inmemory
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
@ -14,14 +15,14 @@ import (
|
|||
type targetToChain map[engine.Target][]*chain.Chain
|
||||
|
||||
type inmemoryLocalStorage struct {
|
||||
usedChainID map[chain.ID]struct{}
|
||||
usedChainID map[string]struct{}
|
||||
nameToResourceChains map[chain.Name]targetToChain
|
||||
guard *sync.RWMutex
|
||||
}
|
||||
|
||||
func NewInmemoryLocalStorage() engine.LocalOverrideStorage {
|
||||
return &inmemoryLocalStorage{
|
||||
usedChainID: map[chain.ID]struct{}{},
|
||||
usedChainID: map[string]struct{}{},
|
||||
nameToResourceChains: make(map[chain.Name]targetToChain),
|
||||
guard: &sync.RWMutex{},
|
||||
}
|
||||
|
@ -35,12 +36,13 @@ func (s *inmemoryLocalStorage) generateChainID(name chain.Name, target engine.Ta
|
|||
sid = strings.ReplaceAll(sid, "*", "")
|
||||
sid = strings.ReplaceAll(sid, "/", ":")
|
||||
sid = strings.ReplaceAll(sid, "::", ":")
|
||||
id = chain.ID(sid)
|
||||
_, ok := s.usedChainID[id]
|
||||
_, ok := s.usedChainID[sid]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
s.usedChainID[id] = struct{}{}
|
||||
s.usedChainID[sid] = struct{}{}
|
||||
|
||||
id = chain.ID(sid)
|
||||
break
|
||||
}
|
||||
return id
|
||||
|
@ -51,7 +53,7 @@ func (s *inmemoryLocalStorage) AddOverride(name chain.Name, target engine.Target
|
|||
defer s.guard.Unlock()
|
||||
|
||||
// AddOverride assigns generated chain ID if it has not been assigned.
|
||||
if c.ID == "" {
|
||||
if len(c.ID) == 0 {
|
||||
c.ID = s.generateChainID(name, target)
|
||||
}
|
||||
if s.nameToResourceChains[name] == nil {
|
||||
|
@ -59,7 +61,7 @@ func (s *inmemoryLocalStorage) AddOverride(name chain.Name, target engine.Target
|
|||
}
|
||||
rc := s.nameToResourceChains[name]
|
||||
for i := range rc[target] {
|
||||
if rc[target][i].ID == c.ID {
|
||||
if bytes.Equal(rc[target][i].ID, c.ID) {
|
||||
rc[target][i] = c
|
||||
return c.ID, nil
|
||||
}
|
||||
|
@ -80,7 +82,7 @@ func (s *inmemoryLocalStorage) GetOverride(name chain.Name, target engine.Target
|
|||
return nil, engine.ErrResourceNotFound
|
||||
}
|
||||
for _, c := range chains {
|
||||
if c.ID == chainID {
|
||||
if bytes.Equal(c.ID, chainID) {
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +101,7 @@ func (s *inmemoryLocalStorage) RemoveOverride(name chain.Name, target engine.Tar
|
|||
return engine.ErrResourceNotFound
|
||||
}
|
||||
for i, c := range chains {
|
||||
if c.ID == chainID {
|
||||
if bytes.Equal(c.ID, chainID) {
|
||||
s.nameToResourceChains[name][target] = append(chains[:i], chains[i+1:]...)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -14,9 +14,7 @@ const (
|
|||
nonExistChainId = "ingress:LxGyWyL"
|
||||
)
|
||||
|
||||
var (
|
||||
resrc = engine.ContainerTarget(container)
|
||||
)
|
||||
var resrc = engine.ContainerTarget(container)
|
||||
|
||||
func testInmemLocalStorage() *inmemoryLocalStorage {
|
||||
return NewInmemoryLocalStorage().(*inmemoryLocalStorage)
|
||||
|
@ -210,12 +208,12 @@ func TestGenerateID(t *testing.T) {
|
|||
}
|
||||
|
||||
func hasDuplicates(ids []chain.ID) bool {
|
||||
seen := make(map[chain.ID]bool)
|
||||
seen := make(map[string]bool)
|
||||
for _, id := range ids {
|
||||
if seen[id] {
|
||||
if seen[string(id)] {
|
||||
return true
|
||||
}
|
||||
seen[id] = true
|
||||
seen[string(id)] = true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
267
pkg/marshal/marshal.go
Normal file
267
pkg/marshal/marshal.go
Normal file
|
@ -0,0 +1,267 @@
|
|||
package marshal
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
Version byte = 0 // increase if breaking change
|
||||
|
||||
ByteSize int = 1
|
||||
UInt8Size int = ByteSize
|
||||
BoolSize int = ByteSize
|
||||
|
||||
nilSlice int64 = -1
|
||||
nilSliceSize int = 1
|
||||
|
||||
byteTrue uint8 = 1
|
||||
byteFalse uint8 = 0
|
||||
|
||||
// maxSliceLen taken from https://github.com/neo-project/neo/blob/38218bbee5bbe8b33cd8f9453465a19381c9a547/src/Neo/IO/Helper.cs#L77
|
||||
maxSliceLen = 0x1000000
|
||||
)
|
||||
|
||||
type MarshallerError struct {
|
||||
errMsg string
|
||||
offset int
|
||||
}
|
||||
|
||||
func (e *MarshallerError) Error() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
if e.offset < 0 {
|
||||
return e.errMsg
|
||||
}
|
||||
return fmt.Sprintf("%s (offset: %d)", e.errMsg, e.offset)
|
||||
}
|
||||
|
||||
func errBufTooSmall(t string, marshal bool, offset int) error {
|
||||
action := "unmarshal"
|
||||
if marshal {
|
||||
action = "marshal"
|
||||
}
|
||||
return &MarshallerError{
|
||||
errMsg: fmt.Sprintf("not enough bytes left to %s value of type '%s'", action, t),
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
func VerifyMarshal(buf []byte, lastOffset int) error {
|
||||
if len(buf) != lastOffset {
|
||||
return &MarshallerError{
|
||||
errMsg: "actual data size differs from expected",
|
||||
offset: -1,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyUnmarshal(buf []byte, lastOffset int) error {
|
||||
if len(buf) != lastOffset {
|
||||
return &MarshallerError{
|
||||
errMsg: "unmarshalled bytes left",
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SliceSize[T any](slice []T, sizeOf func(T) int) int {
|
||||
if slice == nil {
|
||||
return nilSliceSize
|
||||
}
|
||||
s := Int64Size(int64(len(slice)))
|
||||
for _, v := range slice {
|
||||
s += sizeOf(v)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func SliceMarshal[T any](buf []byte, offset int, slice []T, marshalT func([]byte, int, T) (int, error)) (int, error) {
|
||||
if slice == nil {
|
||||
return Int64Marshal(buf, offset, nilSlice)
|
||||
}
|
||||
if len(slice) > maxSliceLen {
|
||||
return 0, &MarshallerError{
|
||||
errMsg: fmt.Sprintf("slice size if too big: '%d'", len(slice)),
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
offset, err := Int64Marshal(buf, offset, int64(len(slice)))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, v := range slice {
|
||||
offset, err = marshalT(buf, offset, v)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
func SliceUnmarshal[T any](buf []byte, offset int, unmarshalT func(buf []byte, offset int) (T, int, error)) ([]T, int, error) {
|
||||
size, offset, err := Int64Unmarshal(buf, offset)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if size == nilSlice {
|
||||
return nil, offset, nil
|
||||
}
|
||||
if size > maxSliceLen {
|
||||
return nil, 0, &MarshallerError{
|
||||
errMsg: fmt.Sprintf("slice size if too big: '%d'", size),
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
return nil, 0, &MarshallerError{
|
||||
errMsg: fmt.Sprintf("invalid slice size: '%d'", size),
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
result := make([]T, size)
|
||||
for idx := 0; idx < len(result); idx++ {
|
||||
result[idx], offset, err = unmarshalT(buf, offset)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
return result, offset, nil
|
||||
}
|
||||
|
||||
func Int64Size(v int64) int {
|
||||
// https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=92;drc=dac9b9ddbd5160c5f4552410f5f8281bd5eed38c
|
||||
// and
|
||||
// https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=41;drc=dac9b9ddbd5160c5f4552410f5f8281bd5eed38c
|
||||
ux := uint64(v) << 1
|
||||
if v < 0 {
|
||||
ux = ^ux
|
||||
}
|
||||
s := 0
|
||||
for ux >= 0x80 {
|
||||
s++
|
||||
ux >>= 7
|
||||
}
|
||||
return s + 1
|
||||
}
|
||||
|
||||
func Int64Marshal(buf []byte, offset int, v int64) (int, error) {
|
||||
if len(buf)-offset < Int64Size(v) {
|
||||
return 0, errBufTooSmall("int64", true, offset)
|
||||
}
|
||||
return offset + binary.PutVarint(buf[offset:], v), nil
|
||||
}
|
||||
|
||||
func Int64Unmarshal(buf []byte, offset int) (int64, int, error) {
|
||||
v, read := binary.Varint(buf[offset:])
|
||||
if read == 0 {
|
||||
return 0, 0, errBufTooSmall("int64", false, offset)
|
||||
}
|
||||
if read < 0 {
|
||||
return 0, 0, &MarshallerError{
|
||||
errMsg: "int64 unmarshal overflow",
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
return v, offset + read, nil
|
||||
}
|
||||
|
||||
func StringSize(s string) int {
|
||||
return Int64Size(int64(len(s))) + len(s)
|
||||
}
|
||||
|
||||
func StringMarshal(buf []byte, offset int, s string) (int, error) {
|
||||
if len(s) > maxSliceLen {
|
||||
return 0, &MarshallerError{
|
||||
errMsg: fmt.Sprintf("string is too long: '%d'", len(s)),
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
if len(buf)-offset < Int64Size(int64(len(s)))+len(s) {
|
||||
return 0, errBufTooSmall("string", true, offset)
|
||||
}
|
||||
|
||||
offset, err := Int64Marshal(buf, offset, int64(len(s)))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if s == "" {
|
||||
return offset, nil
|
||||
}
|
||||
return offset + copy(buf[offset:], s), nil
|
||||
}
|
||||
|
||||
func StringUnmarshal(buf []byte, offset int) (string, int, error) {
|
||||
size, offset, err := Int64Unmarshal(buf, offset)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
if size == 0 {
|
||||
return "", offset, nil
|
||||
}
|
||||
if size > maxSliceLen {
|
||||
return "", 0, &MarshallerError{
|
||||
errMsg: fmt.Sprintf("string is too long: '%d'", size),
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
return "", 0, &MarshallerError{
|
||||
errMsg: fmt.Sprintf("invalid string size: '%d'", size),
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
if len(buf)-offset < int(size) {
|
||||
return "", 0, errBufTooSmall("string", false, offset)
|
||||
}
|
||||
return string(buf[offset : offset+int(size)]), offset + int(size), nil
|
||||
}
|
||||
|
||||
func UInt8Marshal(buf []byte, offset int, value uint8) (int, error) {
|
||||
if len(buf)-offset < 1 {
|
||||
return 0, errBufTooSmall("uint8", true, offset)
|
||||
}
|
||||
buf[offset] = value
|
||||
return offset + 1, nil
|
||||
}
|
||||
|
||||
func UInt8Unmarshal(buf []byte, offset int) (uint8, int, error) {
|
||||
if len(buf)-offset < 1 {
|
||||
return 0, 0, errBufTooSmall("uint8", false, offset)
|
||||
}
|
||||
return buf[offset], offset + 1, nil
|
||||
}
|
||||
|
||||
func ByteMarshal(buf []byte, offset int, value byte) (int, error) {
|
||||
return UInt8Marshal(buf, offset, value)
|
||||
}
|
||||
|
||||
func ByteUnmarshal(buf []byte, offset int) (byte, int, error) {
|
||||
return UInt8Unmarshal(buf, offset)
|
||||
}
|
||||
|
||||
func BoolMarshal(buf []byte, offset int, value bool) (int, error) {
|
||||
if value {
|
||||
return UInt8Marshal(buf, offset, byteTrue)
|
||||
}
|
||||
return UInt8Marshal(buf, offset, byteFalse)
|
||||
}
|
||||
|
||||
func BoolUnmarshal(buf []byte, offset int) (bool, int, error) {
|
||||
v, offset, err := UInt8Unmarshal(buf, offset)
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
if v == byteTrue {
|
||||
return true, offset, nil
|
||||
}
|
||||
if v == byteFalse {
|
||||
return false, offset, nil
|
||||
}
|
||||
return false, 0, &MarshallerError{
|
||||
errMsg: fmt.Sprintf("invalid marshalled value for bool: %d", v),
|
||||
offset: offset - BoolSize,
|
||||
}
|
||||
}
|
313
pkg/marshal/marshal_test.go
Normal file
313
pkg/marshal/marshal_test.go
Normal file
|
@ -0,0 +1,313 @@
|
|||
package marshal
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMarshalling(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("slice", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("nil slice", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var int64s []int64
|
||||
expectedSize := SliceSize(int64s, Int64Size)
|
||||
require.Equal(t, 1, expectedSize)
|
||||
buf := make([]byte, expectedSize)
|
||||
offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyMarshal(buf, offset))
|
||||
|
||||
result, offset, err := SliceUnmarshal(buf, 0, Int64Unmarshal)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||
require.Nil(t, result)
|
||||
})
|
||||
|
||||
t.Run("empty slice", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
int64s := make([]int64, 0)
|
||||
expectedSize := SliceSize(int64s, Int64Size)
|
||||
require.Equal(t, 1, expectedSize)
|
||||
buf := make([]byte, expectedSize)
|
||||
offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyMarshal(buf, offset))
|
||||
|
||||
result, offset, err := SliceUnmarshal(buf, 0, Int64Unmarshal)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||
require.NotNil(t, result)
|
||||
require.Len(t, result, 0)
|
||||
})
|
||||
|
||||
t.Run("non empty slice", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
int64s := make([]int64, 100)
|
||||
for i := range int64s {
|
||||
int64s[i] = int64(i)
|
||||
}
|
||||
expectedSize := SliceSize(int64s, Int64Size)
|
||||
buf := make([]byte, expectedSize)
|
||||
offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyMarshal(buf, offset))
|
||||
|
||||
result, offset, err := SliceUnmarshal(buf, 0, Int64Unmarshal)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||
require.Equal(t, int64s, result)
|
||||
})
|
||||
|
||||
t.Run("corrupted slice size", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
int64s := make([]int64, 100)
|
||||
for i := range int64s {
|
||||
int64s[i] = int64(i)
|
||||
}
|
||||
expectedSize := SliceSize(int64s, Int64Size)
|
||||
buf := make([]byte, expectedSize)
|
||||
offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyMarshal(buf, offset))
|
||||
|
||||
for i := 0; i < binary.MaxVarintLen64; i++ {
|
||||
buf[i] = 129
|
||||
}
|
||||
|
||||
_, _, err = SliceUnmarshal(buf, 0, Int64Unmarshal)
|
||||
var mErr *MarshallerError
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
|
||||
for i := 0; i < binary.MaxVarintLen64; i++ {
|
||||
buf[i] = 127
|
||||
}
|
||||
_, _, err = SliceUnmarshal(buf, 0, Int64Unmarshal)
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
})
|
||||
|
||||
t.Run("corrupted slice item", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
int64s := make([]int64, 100)
|
||||
for i := range int64s {
|
||||
int64s[i] = int64(i)
|
||||
}
|
||||
expectedSize := SliceSize(int64s, Int64Size)
|
||||
buf := make([]byte, expectedSize)
|
||||
offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyMarshal(buf, offset))
|
||||
|
||||
for i := 2; i < binary.MaxVarintLen64+2; i++ {
|
||||
buf[i] = 129
|
||||
}
|
||||
|
||||
_, _, err = SliceUnmarshal(buf, 0, Int64Unmarshal)
|
||||
var mErr *MarshallerError
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
})
|
||||
|
||||
t.Run("small buffer", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
int64s := make([]int64, 100)
|
||||
for i := range int64s {
|
||||
int64s[i] = int64(i)
|
||||
}
|
||||
buf := make([]byte, 1)
|
||||
_, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||
var mErr *MarshallerError
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
|
||||
buf = make([]byte, 10)
|
||||
_, err = SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Equal(t, 1, Int64Size(0))
|
||||
require.Equal(t, binary.MaxVarintLen64, Int64Size(math.MaxInt64))
|
||||
require.Equal(t, binary.MaxVarintLen64, Int64Size(math.MinInt64))
|
||||
|
||||
for _, v := range []int64{0, math.MinInt64, math.MaxInt64} {
|
||||
size := Int64Size(v)
|
||||
buf := make([]byte, size)
|
||||
offset, err := Int64Marshal(buf, 0, v)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyMarshal(buf, offset))
|
||||
|
||||
uv, offset, err := Int64Unmarshal(buf, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||
require.Equal(t, v, uv)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid buffer", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var mErr *MarshallerError
|
||||
|
||||
_, err := Int64Marshal([]byte{}, 0, 100500)
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
|
||||
_, _, err = Int64Unmarshal(nil, 0)
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
})
|
||||
|
||||
t.Run("overflow", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var mErr *MarshallerError
|
||||
|
||||
var v int64 = math.MaxInt64
|
||||
buf := make([]byte, Int64Size(v))
|
||||
_, err := Int64Marshal(buf, 0, v)
|
||||
require.NoError(t, err)
|
||||
|
||||
buf[9] = 2
|
||||
|
||||
_, _, err = Int64Unmarshal(buf, 0)
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("string", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, v := range []string{
|
||||
"", "arn:aws:iam::namespace:group/some_group", "$Object:homomorphicHash",
|
||||
"native:container/ns/9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J",
|
||||
} {
|
||||
size := StringSize(v)
|
||||
buf := make([]byte, size)
|
||||
offset, err := StringMarshal(buf, 0, v)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyMarshal(buf, offset))
|
||||
|
||||
uv, offset, err := StringUnmarshal(buf, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||
require.Equal(t, v, uv)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid buffer", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
str := "avada kedavra"
|
||||
|
||||
var mErr *MarshallerError
|
||||
_, err := StringMarshal(nil, 0, str)
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
|
||||
_, _, err = StringUnmarshal(nil, 0)
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
|
||||
buf := make([]byte, StringSize(str))
|
||||
offset, err := StringMarshal(buf, 0, str)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyMarshal(buf, offset))
|
||||
buf = buf[:len(buf)-1]
|
||||
_, _, err = StringUnmarshal(buf, 0)
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("uint8, byte", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, v := range []byte{0, 8, 16, 32, 64, 128, 255} {
|
||||
buf := make([]byte, ByteSize)
|
||||
offset, err := ByteMarshal(buf, 0, v)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyMarshal(buf, offset))
|
||||
|
||||
ub, offset, err := ByteUnmarshal(buf, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||
require.Equal(t, v, ub)
|
||||
|
||||
buf = make([]byte, UInt8Size)
|
||||
offset, err = UInt8Marshal(buf, 0, v)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyMarshal(buf, offset))
|
||||
|
||||
uu, offset, err := UInt8Unmarshal(buf, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||
require.Equal(t, v, uu)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("bool", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, v := range []bool{false, true} {
|
||||
buf := make([]byte, BoolSize)
|
||||
offset, err := BoolMarshal(buf, 0, v)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyMarshal(buf, offset))
|
||||
|
||||
ub, offset, err := BoolUnmarshal(buf, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||
require.Equal(t, v, ub)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid value", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
buf := make([]byte, BoolSize)
|
||||
offset, err := BoolMarshal(buf, 0, true)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, VerifyMarshal(buf, offset))
|
||||
|
||||
buf[0] = 2
|
||||
|
||||
_, _, err = BoolUnmarshal(buf, 0)
|
||||
var mErr *MarshallerError
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
})
|
||||
|
||||
t.Run("invalid buffer", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var mErr *MarshallerError
|
||||
|
||||
_, err := BoolMarshal(nil, 0, true)
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
|
||||
buf := append(make([]byte, BoolSize), 100)
|
||||
offset, err := BoolMarshal(buf, 0, true)
|
||||
require.NoError(t, err)
|
||||
require.ErrorAs(t, VerifyMarshal(buf, offset), &mErr)
|
||||
|
||||
v, offset, err := BoolUnmarshal(buf, 0)
|
||||
require.NoError(t, err)
|
||||
require.True(t, v)
|
||||
require.ErrorAs(t, VerifyUnmarshal(buf, offset), &mErr)
|
||||
|
||||
_, _, err = BoolUnmarshal(nil, 0)
|
||||
require.ErrorAs(t, err, &mErr)
|
||||
})
|
||||
})
|
||||
}
|
|
@ -44,7 +44,7 @@ func NewContractStorageWithSimpleActor(rpcActor actor.RPCActor, acc *wallet.Acco
|
|||
}
|
||||
|
||||
func (s *ContractStorage) AddMorphRuleChain(name chain.Name, target engine.Target, c *chain.Chain) (txHash util.Uint256, vub uint32, err error) {
|
||||
if c.ID == "" {
|
||||
if len(c.ID) == 0 {
|
||||
err = ErrEmptyChainID
|
||||
return
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ func (s *ContractStorage) AddMorphRuleChain(name chain.Name, target engine.Targe
|
|||
}
|
||||
|
||||
func (s *ContractStorage) RemoveMorphRuleChain(name chain.Name, target engine.Target, chainID chain.ID) (txHash util.Uint256, vub uint32, err error) {
|
||||
if chainID == "" {
|
||||
if len(chainID) == 0 {
|
||||
err = ErrEmptyChainID
|
||||
return
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ const (
|
|||
|
||||
PropertyKeyContainerOwnerID = "$Container:ownerID"
|
||||
|
||||
PropertyValueContainerRoleOwner = "onwer"
|
||||
PropertyValueContainerRoleOwner = "owner"
|
||||
PropertyValueContainerRoleIR = "ir"
|
||||
PropertyValueContainerRoleContainer = "container"
|
||||
PropertyValueContainerRoleOthers = "others"
|
||||
|
|
Loading…
Add table
Reference in a new issue