From 3f280377ede30c03bf5866483a0a06b002dbca48 Mon Sep 17 00:00:00 2001 From: Yuli Date: Sun, 14 Jun 2020 11:47:57 +0300 Subject: [PATCH] add schema check for locked values --- src/schema.go | 144 ++++++++++++++++++++++++++++++++++++++++++++++++ src/users_db.go | 2 +- 2 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 src/schema.go diff --git a/src/schema.go b/src/schema.go new file mode 100644 index 0000000..3f571cc --- /dev/null +++ b/src/schema.go @@ -0,0 +1,144 @@ +package main + +import ( + "context" + "errors" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/paranoidguy/jsonschema" + jsonpatch "github.com/evanphx/json-patch" + jptr "github.com/qri-io/jsonpointer" +) + +var userSchema *jsonschema.Schema + +// our custom validator +type IsLocked bool + +func loadUserSchema(cfg Config, confFile *string) error { + fileSchema := cfg.Generic.UserRecordSchema + parentDir := "" + if confFile != nil && len(*confFile) > 0 { + parentDir = filepath.Base(*confFile) + if parentDir != "." { + parentDir = "" + } + } + if len(fileSchema) == 0 { + return nil + } + if strings.HasPrefix(fileSchema, "./") { + _, err := os.Stat(cfg.Generic.UserRecordSchema) + if os.IsNotExist(err) && confFile != nil { + fileSchema = parentDir + fileSchema[2:] + } + } else { + fileSchema = parentDir + fileSchema + } + _, err := os.Stat(fileSchema) + if os.IsNotExist(err) { + return err + } + schemaData, err := ioutil.ReadFile(fileSchema) + if err != nil { + return err + } + rs := &jsonschema.Schema{} + jsonschema.LoadDraft2019_09() + jsonschema.RegisterKeyword("locked", newIsLocked) + err = rs.UnmarshalJSON(schemaData) + if err != nil { + return err + } + userSchema = rs + return nil +} + +func ValidateUserEnabled() bool { + if userSchema == nil { + return false + } + return true +} + +func ValidateUserRecord(record []byte) error { + if userSchema == nil { + return nil + } + var doc interface{} + if err := json.Unmarshal(record, &doc); err != nil { + return err + } + result := userSchema.Validate(nil, doc) + if len(*result.Errs) > 0 { + return (*result.Errs)[0] + } + return nil +} + +func ValidateUserRecordChange(oldRecord []byte, newRecord []byte) error { + if userSchema == nil { + return nil + } + var oldDoc interface{} + var newDoc interface{} + if err := json.Unmarshal(oldRecord, &oldDoc); err != nil { + return err + } + if err := json.Unmarshal(newRecord, &newDoc); err != nil { + return err + } + result := userSchema.Validate(nil, newDoc) + //if len(*result.Errs) > 0 { + // return (*result.Errs)[0] + //} + result2 := userSchema.Validate(nil, oldDoc) + if len(*result2.Errs) > 0 { + return (*result.Errs)[0] + } + for _, r := range *result.ExtendedResults { + fmt.Printf("path: %s key: %s data: %v\n", r.PropertyPath, r.Key, r.Value) + if r.Key == "locked" { + pointer, _ := jptr.Parse(r.PropertyPath) + data1, _ := pointer.Eval(oldDoc) + data1Binary, _ := json.Marshal(data1) + data2, _ := pointer.Eval(newDoc) + data2Binary, _ := json.Marshal(data2) + if !jsonpatch.Equal(data1Binary, data2Binary) { + fmt.Printf("Locked value changed. Old: %s New %s\n", data1Binary, data2Binary) + return errors.New("User schema check error. Locked value changed: "+r.PropertyPath) + } + } + } + + return nil +} +func newIsLocked() jsonschema.Keyword { + return new(IsLocked) +} + +// Validate implements jsonschema.Keyword +func (f *IsLocked) Validate(propPath string, data interface{}, errs *[]jsonschema.KeyError) { + fmt.Printf("Validate: %s -> %v\n", propPath, data) +} + +// Register implements jsonschema.Keyword +func (f *IsLocked) Register(uri string, registry *jsonschema.SchemaRegistry) { + fmt.Printf("Register %s\n", uri) +} + +// Resolve implements jsonschema.Keyword +func (f *IsLocked) Resolve(pointer jptr.Pointer, uri string) *jsonschema.Schema { + fmt.Printf("Resolve %s\n", uri) + return nil +} + +func (f *IsLocked) ValidateKeyword(ctx context.Context, currentState *jsonschema.ValidationState, data interface{}) { + fmt.Printf("ValidateKeyword locked %s => %v\n", currentState.InstanceLocation.String(), data) + currentState.AddExtendedResult("locked", data) +} diff --git a/src/users_db.go b/src/users_db.go index 1fb3a7a..fff2259 100644 --- a/src/users_db.go +++ b/src/users_db.go @@ -117,7 +117,7 @@ func (dbobj dbcon) validateUserRecordChange(jsonDataPatch []byte, userTOKEN stri return err } fmt.Printf("result: %s\n", newJSON) - err = ValidateUserRecord(newJSON) + err = ValidateUserRecordChange(decrypted, newJSON) if err != nil { return err }