Skip to content

Commit

Permalink
Added basic support for WSS Security header: new structs WSSSecurityH…
Browse files Browse the repository at this point in the history
…eader, WSSUsernameToken and its child structs WSSUsername and WSSPassword.

XML namespace problems are dealt with by using vania-pooh's workaround:  golang/go#9519 (comment)

Convenience function "NewWSSSecurityHeader" to ease header creation (the header is not automatically added to the SOAP client).

"SetHeader" method is kept for compatibility. There is a new "AddHeader" method to add headers to the SOAP call. There is now support for several headers instead of only one.

Cosmetic changes in header_tmpl.go to sort import names alphabetically.

Sample code:

   ...
   hdr := myservice.NewWSSSecurityHeader("sampleuser", "samplepassword", "1")  // "1" is for 'mustUnderstand' attribute; empty if not specified
   myserviceClient.AddHeader(hdr)
   ...
  • Loading branch information
dmlambea committed May 16, 2017
1 parent 21c7151 commit a165afc
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 22 deletions.
12 changes: 6 additions & 6 deletions header_tmpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ var headerTmpl = `
package {{.}}
import (
"bytes"
"crypto/tls"
"encoding/xml"
"time"
"bytes"
"crypto/tls"
"encoding/xml"
"io/ioutil"
"net/http"
"io/ioutil"
"log"
"math/rand"
"net"
"net/http"
"time"
{{/*range .Imports*/}}
{{/*.*/}}
Expand Down
7 changes: 6 additions & 1 deletion operations_tmpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@ var opsTmpl = `
}
}
func (service *{{$portType}}) AddHeader(header interface{}) {
service.client.AddHeader(header)
}
// Backwards-compatible function: use AddHeader instead
func (service *{{$portType}}) SetHeader(header interface{}) {
service.client.SetHeader(header)
service.client.AddHeader(header)
}
{{range .Operations}}
Expand Down
111 changes: 96 additions & 15 deletions soap_tmpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ type SOAPEnvelope struct {
type SOAPHeader struct {
XMLName xml.Name ` + "`" + `xml:"http://schemas.xmlsoap.org/soap/envelope/ Header"` + "`" + `
Header interface{}
Items []interface{} ` + "`" + `xml:",omitempty"` + "`" + `
}
type SOAPBody struct {
Expand All @@ -39,16 +39,97 @@ type SOAPFault struct {
Detail string ` + "`" + `xml:"detail,omitempty"` + "`" + `
}
const (
// Predefined WSS namespaces to be used in
WssNsWSSE string = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
WssNsWSU string = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
WssNsType string = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
)
type WSSSecurityHeader struct {
XMLName xml.Name ` + "`" + `xml:"http://schemas.xmlsoap.org/soap/envelope/ wsse:Security"` + "`" + `
XmlNSWsse string ` + "`" + `xml:"xmlns:wsse,attr"` + "`" + `
MustUnderstand string ` + "`" + `xml:"mustUnderstand,attr,omitempty"` + "`" + `
Token *WSSUsernameToken ` + "`" + `xml:",omitempty"` + "`" + `
}
type WSSUsernameToken struct {
XMLName xml.Name ` + "`" + `xml:"wsse:UsernameToken"` + "`" + `
XmlNSWsu string ` + "`" + `xml:"xmlns:wsu,attr"` + "`" + `
XmlNSWsse string ` + "`" + `xml:"xmlns:wsse,attr"` + "`" + `
Id string ` + "`" + `xml:"wsu:Id,attr,omitempty"` + "`" + `
Username *WSSUsername ` + "`" + `xml:",omitempty"` + "`" + `
Password *WSSPassword ` + "`" + `xml:",omitempty"` + "`" + `
}
type WSSUsername struct {
XMLName xml.Name ` + "`" + `xml:"wsse:Username"` + "`" + `
XmlNSWsse string ` + "`" + `xml:"xmlns:wsse,attr"` + "`" + `
Data string ` + "`" + `xml:",chardata"` + "`" + `
}
type WSSPassword struct {
XMLName xml.Name ` + "`" + `xml:"wsse:Password"` + "`" + `
XmlNSWsse string ` + "`" + `xml:"xmlns:wsse,attr"` + "`" + `
XmlNSType string ` + "`" + `xml:"Type,attr"` + "`" + `
Data string ` + "`" + `xml:",chardata"` + "`" + `
}
type BasicAuth struct {
Login string
Login string
Password string
}
type SOAPClient struct {
url string
tls bool
auth *BasicAuth
header interface{}
url string
tls bool
auth *BasicAuth
headers []interface{}
}
// **********
// Accepted solution from http://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang
// Author: Icza - http://stackoverflow.com/users/1705598/icza
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
func randStringBytesMaskImprSrc(n int) string {
src := rand.NewSource(time.Now().UnixNano())
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b)
}
// **********
func NewWSSSecurityHeader(user, pass, mustUnderstand string) *WSSSecurityHeader {
hdr := &WSSSecurityHeader{XmlNSWsse: WssNsWSSE, MustUnderstand: mustUnderstand}
hdr.Token = &WSSUsernameToken{XmlNSWsu: WssNsWSU, XmlNSWsse: WssNsWSSE, Id: "UsernameToken-" + randStringBytesMaskImprSrc(9)}
hdr.Token.Username = &WSSUsername{XmlNSWsse: WssNsWSSE, Data: user}
hdr.Token.Password = &WSSPassword{XmlNSWsse: WssNsWSSE, XmlNSType: WssNsType, Data: pass}
return hdr
}
func (b *SOAPBody) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
Expand Down Expand Up @@ -113,15 +194,17 @@ func NewSOAPClient(url string, tls bool, auth *BasicAuth) *SOAPClient {
}
}
func (s *SOAPClient) SetHeader(header interface{}) {
s.header = header
func (s *SOAPClient) AddHeader(header interface{}) {
s.headers = append(s.headers, header)
}
func (s *SOAPClient) Call(soapAction string, request, response interface{}) error {
envelope := SOAPEnvelope{}
if s.header != nil {
envelope.Header = &SOAPHeader{Header: s.header}
if s.headers != nil && len(s.headers) > 0 {
soapHeader := &SOAPHeader{Items: make([]interface{}, len(s.headers))}
copy(soapHeader.Items, s.headers)
envelope.Header = soapHeader
}
envelope.Body.Content = request
Expand Down Expand Up @@ -149,10 +232,8 @@ func (s *SOAPClient) Call(soapAction string, request, response interface{}) erro
}
req.Header.Add("Content-Type", "text/xml; charset=\"utf-8\"")
if soapAction != "" {
req.Header.Add("SOAPAction", soapAction)
}
req.Header.Add("SOAPAction", soapAction)
req.Header.Set("User-Agent", "gowsdl/0.1")
req.Close = true
Expand Down

0 comments on commit a165afc

Please sign in to comment.