From 073f7ef3ae45c91a63b408c7cda00ac6dd8a8a3f Mon Sep 17 00:00:00 2001 From: Edward Date: Wed, 29 Apr 2020 10:52:58 +0800 Subject: [PATCH] add gosoap function --- gosoap/README.md | 1 + gosoap/soap-builder.go | 304 +++++++++++++++++++++++++++++++++++++++++ gosoap/ws-action.go | 43 ++++++ gosoap/ws-security.go | 93 +++++++++++++ 4 files changed, 441 insertions(+) create mode 100644 gosoap/README.md create mode 100644 gosoap/soap-builder.go create mode 100644 gosoap/ws-action.go create mode 100644 gosoap/ws-security.go diff --git a/gosoap/README.md b/gosoap/README.md new file mode 100644 index 0000000..9273df3 --- /dev/null +++ b/gosoap/README.md @@ -0,0 +1 @@ +# gosoap diff --git a/gosoap/soap-builder.go b/gosoap/soap-builder.go new file mode 100644 index 0000000..d3ac554 --- /dev/null +++ b/gosoap/soap-builder.go @@ -0,0 +1,304 @@ +package gosoap + +import ( + "encoding/xml" + "log" + + "github.com/beevik/etree" +) + +//SoapMessage type from string +type SoapMessage string + +// NewEmptySOAP return new SoapMessage +func NewEmptySOAP() SoapMessage { + doc := buildSoapRoot() + //doc.IndentTabs() + + res, _ := doc.WriteToString() + + return SoapMessage(res) +} + +//NewSOAP Get a new soap message +func NewSOAP(headContent []*etree.Element, bodyContent []*etree.Element, namespaces map[string]string) SoapMessage { + doc := buildSoapRoot() + //doc.IndentTabs() + + res, _ := doc.WriteToString() + + return SoapMessage(res) +} + +func (msg SoapMessage) String() string { + return string(msg) +} + +//StringIndent handle indent +func (msg SoapMessage) StringIndent() string { + doc := etree.NewDocument() + + if err := doc.ReadFromString(msg.String()); err != nil { + log.Println(err.Error()) + } + + doc.IndentTabs() + res, _ := doc.WriteToString() + + return res +} + +//Body return body from Envelope +func (msg SoapMessage) Body() string { + + doc := etree.NewDocument() + + if err := doc.ReadFromString(msg.String()); err != nil { + log.Println(err.Error()) + } + bodyTag := doc.Root().SelectElement("Body").ChildElements()[0] + doc.SetRoot(bodyTag) + doc.IndentTabs() + + res, _ := doc.WriteToString() + + return res +} + +//AddStringBodyContent for Envelope +func (msg *SoapMessage) AddStringBodyContent(data string) { + doc := etree.NewDocument() + + if err := doc.ReadFromString(data); err != nil { + log.Println(err.Error()) + } + + element := doc.Root() + + doc = etree.NewDocument() + if err := doc.ReadFromString(msg.String()); err != nil { + log.Println(err.Error()) + } + //doc.FindElement("./Envelope/Body").AddChild(element) + bodyTag := doc.Root().SelectElement("Body") + bodyTag.AddChild(element) + + //doc.IndentTabs() + res, _ := doc.WriteToString() + + *msg = SoapMessage(res) +} + +//AddBodyContent for Envelope +func (msg *SoapMessage) AddBodyContent(element *etree.Element) { + doc := etree.NewDocument() + if err := doc.ReadFromString(msg.String()); err != nil { + log.Println(err.Error()) + } + //doc.FindElement("./Envelope/Body").AddChild(element) + bodyTag := doc.Root().SelectElement("Body") + bodyTag.AddChild(element) + + //doc.IndentTabs() + res, _ := doc.WriteToString() + + *msg = SoapMessage(res) +} + +//AddBodyContents for Envelope body +func (msg *SoapMessage) AddBodyContents(elements []*etree.Element) { + doc := etree.NewDocument() + if err := doc.ReadFromString(msg.String()); err != nil { + log.Println(err.Error()) + } + + bodyTag := doc.Root().SelectElement("Body") + + if len(elements) != 0 { + for _, j := range elements { + bodyTag.AddChild(j) + } + } + + //doc.IndentTabs() + res, _ := doc.WriteToString() + + *msg = SoapMessage(res) +} + +//AddStringHeaderContent for Envelope body +func (msg *SoapMessage) AddStringHeaderContent(data string) error { + doc := etree.NewDocument() + + if err := doc.ReadFromString(data); err != nil { + //log.Println(err.Error()) + return err + } + + element := doc.Root() + + doc = etree.NewDocument() + if err := doc.ReadFromString(msg.String()); err != nil { + //log.Println(err.Error()) + return err + } + + bodyTag := doc.Root().SelectElement("Header") + bodyTag.AddChild(element) + + //doc.IndentTabs() + res, _ := doc.WriteToString() + + *msg = SoapMessage(res) + + return nil +} + +//AddHeaderContent for Envelope body +func (msg *SoapMessage) AddHeaderContent(element *etree.Element) { + doc := etree.NewDocument() + if err := doc.ReadFromString(msg.String()); err != nil { + log.Println(err.Error()) + } + + bodyTag := doc.Root().SelectElement("Header") + bodyTag.AddChild(element) + + //doc.IndentTabs() + res, _ := doc.WriteToString() + + *msg = SoapMessage(res) +} + +//AddHeaderContents for Envelope body +func (msg *SoapMessage) AddHeaderContents(elements []*etree.Element) { + doc := etree.NewDocument() + if err := doc.ReadFromString(msg.String()); err != nil { + log.Println(err.Error()) + } + + headerTag := doc.Root().SelectElement("Header") + + if len(elements) != 0 { + for _, j := range elements { + headerTag.AddChild(j) + } + } + + //doc.IndentTabs() + res, _ := doc.WriteToString() + + *msg = SoapMessage(res) +} + +//AddRootNamespace for Envelope body +func (msg *SoapMessage) AddRootNamespace(key, value string) { + doc := etree.NewDocument() + if err := doc.ReadFromString(msg.String()); err != nil { + log.Println(err.Error()) + } + doc.Root().CreateAttr("xmlns:"+key, value) + //doc.IndentTabs() + res, _ := doc.WriteToString() + + *msg = SoapMessage(res) +} + +//AddRootNamespaces for Envelope body +func (msg *SoapMessage) AddRootNamespaces(namespaces map[string]string) { + for key, value := range namespaces { + msg.AddRootNamespace(key, value) + } + + /* + doc := etree.NewDocument() + if err := doc.ReadFromString(msg.String()); err != nil { + //log.Println(err.Error()) + return err + } + + for key, value := range namespaces { + doc.Root().CreateAttr("xmlns:" + key, value) + } + + doc.IndentTabs() + res, _ := doc.WriteToString() + + *msg = SoapMessage(res)*/ +} + +func buildSoapRoot() *etree.Document { + doc := etree.NewDocument() + + doc.CreateProcInst("xml", `version="1.0" encoding="UTF-8"`) + + env := doc.CreateElement("soap-env:Envelope") + env.CreateElement("soap-env:Header") + env.CreateElement("soap-env:Body") + + env.CreateAttr("xmlns:soap-env", "http://www.w3.org/2003/05/soap-envelope") + env.CreateAttr("xmlns:soap-enc", "http://www.w3.org/2003/05/soap-encoding") + + return doc +} + +//AddWSSecurity Header for soapMessage +func (msg *SoapMessage) AddWSSecurity(username, password string) { + //doc := etree.NewDocument() + //if err := doc.ReadFromString(msg.String()); err != nil { + // log.Println(err.Error()) + //} + /* + Getting an WS-Security struct representation + */ + auth := NewSecurity(username, password) + + /* + Adding WS-Security namespaces to root element of SOAP message + */ + //msg.AddRootNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext1.0.xsd") + //msg.AddRootNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility1.0.xsd") + + soapReq, err := xml.MarshalIndent(auth, "", " ") + if err != nil { + //log.Printf("error: %v\n", err.Error()) + panic(err) + } + + /* + Adding WS-Security struct to SOAP header + */ + msg.AddStringHeaderContent(string(soapReq)) + + //doc.IndentTabs() + //res, _ := doc.WriteToString() + // + //*msg = SoapMessage(res) +} + +//AddAction Header handling for soapMessage +func (msg *SoapMessage) AddAction() { + + doc := etree.NewDocument() + if err := doc.ReadFromString(msg.String()); err != nil { + log.Println(err.Error()) + } + // opetaionTag := doc.Root().SelectElement("Body") + + // firstElemnt := opetaionTag.Child[0] + + // soapReq, err := xml.MarshalIndent(action, "", " ") + // if err != nil { + // //log.Printf("error: %v\n", err.Error()) + // panic(err) + // } + // /* + // Adding WS-Security struct to SOAP header + // */ + // msg.AddStringHeaderContent(string(soapReq)) + + // //doc.IndentTabs() + // //res, _ := doc.WriteToString() + // // + // //*msg = SoapMessage(res) +} diff --git a/gosoap/ws-action.go b/gosoap/ws-action.go new file mode 100644 index 0000000..f5a9e3d --- /dev/null +++ b/gosoap/ws-action.go @@ -0,0 +1,43 @@ +package gosoap + +import ( + "encoding/xml" +) + +//Xlmns XML Scheam +var actionHeaders = map[string]string{ + "wsnt:Subscribe": "http://docs.oasis-open.org/wsn/bw-2/NotificationProducer/SubscribeRequest", + "ResumeSubscription": "http://docs.oasis-open.org/wsn/bw-2/PausableSubscriptionManager/ResumeSubscriptionRequest", + "PauseSubscription": "http://docs.oasis-open.org/wsn/bw-2/PausableSubscriptionManager/PauseSubscriptionRequest", + "Unsubscribe": "http://docs.oasis-open.org/wsn/bw-2/PausableSubscriptionManager/UnsubscribeRequest", + "RenewRequest": "http://docs.oasis-open.org/wsn/bw-2/PausableSubscriptionManager/RenewRequest", +} + +/************************* + Action Type in Header +*************************/ + +//Action type +type Action struct { + //XMLName xml.Name `xml:"wsse:Security"` + XMLName xml.Name `xml:"wsa:Action"` + Operation string `xml:",chardata"` +} + +/* + + http://docs.oasis-open.org/wsn/bw-2/NotificationProducer/SubscribeRequest + +*/ + +//NewAction get a new Action Section +func NewAction(key, value string) Action { + + /** Generating Nonce sequence **/ + auth := Action{ + + // Created: time.Now().UTC().Format(time.RFC3339Nano), + } + + return auth +} diff --git a/gosoap/ws-security.go b/gosoap/ws-security.go new file mode 100644 index 0000000..900ba09 --- /dev/null +++ b/gosoap/ws-security.go @@ -0,0 +1,93 @@ +package gosoap + +import ( + "crypto/sha1" + "encoding/base64" + "encoding/xml" + "time" + + "github.com/elgs/gostrgen" +) + +/************************* + WS-Security types +*************************/ +const ( + passwordType = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest" + encodingType = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" +) + +//Security type :XMLName xml.Name `xml:"http://purl.org/rss/1.0/modules/content/ encoded"` +type Security struct { + //XMLName xml.Name `xml:"wsse:Security"` + XMLName xml.Name `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd Security"` + Auth wsAuth +} + +type password struct { + //XMLName xml.Name `xml:"wsse:Password"` + Type string `xml:"Type,attr"` + Password string `xml:",chardata"` +} + +type nonce struct { + //XMLName xml.Name `xml:"wsse:Nonce"` + Type string `xml:"EncodingType,attr"` + Nonce string `xml:",chardata"` +} + +type wsAuth struct { + XMLName xml.Name `xml:"UsernameToken"` + Username string `xml:"Username"` + Password password `xml:"Password"` + Nonce nonce `xml:"Nonce"` + Created string `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd Created"` +} + +/* + + + admin + edBuG+qVavQKLoWuGWQdPab4IBE= + S7wO1ZFTh0KXv2CR7bd2ZXkLAAAAAA== + 2018-04-10T18:04:25.836Z + + +*/ + +//NewSecurity get a new security +func NewSecurity(username, passwd string) Security { + /** Generating Nonce sequence **/ + charsToGenerate := 32 + charSet := gostrgen.Lower | gostrgen.Digit + + nonceSeq, _ := gostrgen.RandGen(charsToGenerate, charSet, "", "") + auth := Security{ + Auth: wsAuth{ + Username: username, + Password: password{ + Type: passwordType, + Password: generateToken(username, nonceSeq, time.Now().UTC(), passwd), + }, + Nonce: nonce{ + Type: encodingType, + Nonce: nonceSeq, + }, + Created: time.Now().UTC().Format(time.RFC3339Nano), + }, + } + + return auth +} + +//Digest = B64ENCODE( SHA1( B64DECODE( Nonce ) + Date + Password ) ) +func generateToken(Username string, Nonce string, Created time.Time, Password string) string { + + sDec, _ := base64.StdEncoding.DecodeString(Nonce) + + hasher := sha1.New() + //hasher.Write([]byte((base64.StdEncoding.EncodeToString([]byte(Nonce)) + Created.Format(time.RFC3339) + Password))) + hasher.Write([]byte(string(sDec) + Created.Format(time.RFC3339Nano) + Password)) + + return base64.StdEncoding.EncodeToString(hasher.Sum(nil)) +}