Mercurial > gemma
changeset 2181:bd09d6ad4c14
SOAP: Add validating parser (uses 'xmllint') for manual uploads.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Mon, 11 Feb 2019 15:56:59 +0100 |
parents | 20d9b71f4125 |
children | 7ae0b6be0203 |
files | pkg/config/config.go pkg/soap/validate.go |
diffstat | 2 files changed, 156 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/pkg/config/config.go Mon Feb 11 13:04:36 2019 +0100 +++ b/pkg/config/config.go Mon Feb 11 15:56:59 2019 +0100 @@ -166,6 +166,9 @@ return externalURL } +// The root directories where to find schema files. +func SchemaDirs() string { return viper.GetString("schema-dirs") } + // RootCmd is cobra command to be bound th the cobra/viper infrastructure. var RootCmd = &cobra.Command{ Use: "gemma", @@ -246,6 +249,8 @@ str("tmp-dir", "", "Temp directory of gemma server.\n"+ "Defaults to system temp directory.") + + str("schema-dirs", ".", "Directories to find XSD schema files in (recursive).") } var (
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/soap/validate.go Mon Feb 11 15:56:59 2019 +0100 @@ -0,0 +1,151 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package soap + +import ( + "bytes" + "encoding/xml" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + + "golang.org/x/net/html/charset" + + "gemma.intevation.de/gemma/pkg/config" +) + +const linter = "xmllint" + +type ( + ValidationError string + foundError string +) + +func (ef foundError) Error() string { + return string(ef) +} + +func (ve ValidationError) Error() string { + return string(ve) +} + +func FindSchema(name string) (string, error) { + name = strings.ToLower(name) + config.WaitReady() + for _, root := range filepath.SplitList(config.SchemaDirs()) { + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info == nil { + return nil + } + if info.Mode().IsRegular() && strings.ToLower(info.Name()) == name { + return foundError(path) + } + return nil + }) + if path, ok := err.(foundError); ok { + return string(path), nil + } + if err != nil { + return "", err + } + } + return "", nil +} + +func ValidateFile(fname, schema string, dst interface{}) error { + f, err := os.Open(fname) + if err != nil { + return err + } + defer f.Close() + return Validate(f, schema, dst) +} + +func Validate(r io.Reader, schema string, dst interface{}) error { + schemaPath, err := FindSchema(schema) + if err != nil { + return err + } + if schemaPath == "" { + return fmt.Errorf("no schema file '%s' found", schema) + } + linterPath, err := exec.LookPath(linter) + if err != nil { + return err + } + + type envelope struct { + _ xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` + Body *struct { + Inner []byte `xml:",innerxml"` + } `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` + } + + content, err := ioutil.ReadAll(r) + if err != nil { + return err + } + + dec := xml.NewDecoder(bytes.NewReader(content)) + dec.CharsetReader = charset.NewReaderLabel + + var env envelope + if err := dec.Decode(&env); err != nil { + return err + } + + // It has a body -> throw envelope away. + if env.Body != nil && len(env.Body.Inner) > 0 { + content = env.Body.Inner + } + + cmd := exec.Command( + linterPath, + "--schema", schemaPath, + "--noout", + "-") + + var stderr bytes.Buffer + + cmd.Stdin = bytes.NewReader(content) + cmd.Stderr = &stderr + cmd.Stdout = ioutil.Discard + + if err := cmd.Start(); err != nil { + return err + } + + if err := cmd.Wait(); err != nil { + if err2, ok := err.(*exec.ExitError); ok { + if !err2.Success() { + return ValidationError(stderr.String()) + } + } + return err + } + + // Validation successful -> Deserialize. + + dec = xml.NewDecoder(bytes.NewReader(content)) + dec.CharsetReader = charset.NewReaderLabel + + return dec.Decode(dst) +}