Skip to content

Commit a165afc

Browse files
committed
Added basic support for WSS Security header: new structs WSSSecurityHeader, 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) ...
1 parent 21c7151 commit a165afc

File tree

3 files changed

+108
-22
lines changed

3 files changed

+108
-22
lines changed

header_tmpl.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ var headerTmpl = `
88
package {{.}}
99
1010
import (
11+
"bytes"
12+
"crypto/tls"
1113
"encoding/xml"
12-
"time"
13-
"bytes"
14-
"crypto/tls"
15-
"encoding/xml"
16-
"io/ioutil"
17-
"net/http"
14+
"io/ioutil"
1815
"log"
16+
"math/rand"
1917
"net"
18+
"net/http"
19+
"time"
2020
2121
{{/*range .Imports*/}}
2222
{{/*.*/}}

operations_tmpl.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,13 @@ var opsTmpl = `
2222
}
2323
}
2424
25+
func (service *{{$portType}}) AddHeader(header interface{}) {
26+
service.client.AddHeader(header)
27+
}
28+
29+
// Backwards-compatible function: use AddHeader instead
2530
func (service *{{$portType}}) SetHeader(header interface{}) {
26-
service.client.SetHeader(header)
31+
service.client.AddHeader(header)
2732
}
2833
2934
{{range .Operations}}

soap_tmpl.go

Lines changed: 96 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ type SOAPEnvelope struct {
1919
2020
type SOAPHeader struct {
2121
XMLName xml.Name ` + "`" + `xml:"http://schemas.xmlsoap.org/soap/envelope/ Header"` + "`" + `
22-
23-
Header interface{}
22+
23+
Items []interface{} ` + "`" + `xml:",omitempty"` + "`" + `
2424
}
2525
2626
type SOAPBody struct {
@@ -39,16 +39,97 @@ type SOAPFault struct {
3939
Detail string ` + "`" + `xml:"detail,omitempty"` + "`" + `
4040
}
4141
42+
const (
43+
// Predefined WSS namespaces to be used in
44+
WssNsWSSE string = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
45+
WssNsWSU string = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
46+
WssNsType string = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
47+
)
48+
49+
type WSSSecurityHeader struct {
50+
XMLName xml.Name ` + "`" + `xml:"http://schemas.xmlsoap.org/soap/envelope/ wsse:Security"` + "`" + `
51+
XmlNSWsse string ` + "`" + `xml:"xmlns:wsse,attr"` + "`" + `
52+
53+
MustUnderstand string ` + "`" + `xml:"mustUnderstand,attr,omitempty"` + "`" + `
54+
55+
Token *WSSUsernameToken ` + "`" + `xml:",omitempty"` + "`" + `
56+
}
57+
58+
type WSSUsernameToken struct {
59+
XMLName xml.Name ` + "`" + `xml:"wsse:UsernameToken"` + "`" + `
60+
XmlNSWsu string ` + "`" + `xml:"xmlns:wsu,attr"` + "`" + `
61+
XmlNSWsse string ` + "`" + `xml:"xmlns:wsse,attr"` + "`" + `
62+
63+
Id string ` + "`" + `xml:"wsu:Id,attr,omitempty"` + "`" + `
64+
65+
Username *WSSUsername ` + "`" + `xml:",omitempty"` + "`" + `
66+
Password *WSSPassword ` + "`" + `xml:",omitempty"` + "`" + `
67+
}
68+
69+
type WSSUsername struct {
70+
XMLName xml.Name ` + "`" + `xml:"wsse:Username"` + "`" + `
71+
XmlNSWsse string ` + "`" + `xml:"xmlns:wsse,attr"` + "`" + `
72+
73+
Data string ` + "`" + `xml:",chardata"` + "`" + `
74+
}
75+
76+
type WSSPassword struct {
77+
XMLName xml.Name ` + "`" + `xml:"wsse:Password"` + "`" + `
78+
XmlNSWsse string ` + "`" + `xml:"xmlns:wsse,attr"` + "`" + `
79+
XmlNSType string ` + "`" + `xml:"Type,attr"` + "`" + `
80+
81+
Data string ` + "`" + `xml:",chardata"` + "`" + `
82+
}
83+
4284
type BasicAuth struct {
43-
Login string
85+
Login string
4486
Password string
4587
}
4688
4789
type SOAPClient struct {
48-
url string
49-
tls bool
50-
auth *BasicAuth
51-
header interface{}
90+
url string
91+
tls bool
92+
auth *BasicAuth
93+
headers []interface{}
94+
}
95+
96+
// **********
97+
// Accepted solution from http://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang
98+
// Author: Icza - http://stackoverflow.com/users/1705598/icza
99+
100+
const (
101+
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
102+
letterIdxBits = 6 // 6 bits to represent a letter index
103+
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
104+
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
105+
)
106+
107+
func randStringBytesMaskImprSrc(n int) string {
108+
src := rand.NewSource(time.Now().UnixNano())
109+
b := make([]byte, n)
110+
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
111+
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
112+
if remain == 0 {
113+
cache, remain = src.Int63(), letterIdxMax
114+
}
115+
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
116+
b[i] = letterBytes[idx]
117+
i--
118+
}
119+
cache >>= letterIdxBits
120+
remain--
121+
}
122+
return string(b)
123+
}
124+
125+
// **********
126+
127+
func NewWSSSecurityHeader(user, pass, mustUnderstand string) *WSSSecurityHeader {
128+
hdr := &WSSSecurityHeader{XmlNSWsse: WssNsWSSE, MustUnderstand: mustUnderstand}
129+
hdr.Token = &WSSUsernameToken{XmlNSWsu: WssNsWSU, XmlNSWsse: WssNsWSSE, Id: "UsernameToken-" + randStringBytesMaskImprSrc(9)}
130+
hdr.Token.Username = &WSSUsername{XmlNSWsse: WssNsWSSE, Data: user}
131+
hdr.Token.Password = &WSSPassword{XmlNSWsse: WssNsWSSE, XmlNSType: WssNsType, Data: pass}
132+
return hdr
52133
}
53134
54135
func (b *SOAPBody) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
@@ -113,15 +194,17 @@ func NewSOAPClient(url string, tls bool, auth *BasicAuth) *SOAPClient {
113194
}
114195
}
115196
116-
func (s *SOAPClient) SetHeader(header interface{}) {
117-
s.header = header
197+
func (s *SOAPClient) AddHeader(header interface{}) {
198+
s.headers = append(s.headers, header)
118199
}
119200
120201
func (s *SOAPClient) Call(soapAction string, request, response interface{}) error {
121202
envelope := SOAPEnvelope{}
122203
123-
if s.header != nil {
124-
envelope.Header = &SOAPHeader{Header: s.header}
204+
if s.headers != nil && len(s.headers) > 0 {
205+
soapHeader := &SOAPHeader{Items: make([]interface{}, len(s.headers))}
206+
copy(soapHeader.Items, s.headers)
207+
envelope.Header = soapHeader
125208
}
126209
127210
envelope.Body.Content = request
@@ -149,10 +232,8 @@ func (s *SOAPClient) Call(soapAction string, request, response interface{}) erro
149232
}
150233
151234
req.Header.Add("Content-Type", "text/xml; charset=\"utf-8\"")
152-
if soapAction != "" {
153-
req.Header.Add("SOAPAction", soapAction)
154-
}
155-
235+
req.Header.Add("SOAPAction", soapAction)
236+
156237
req.Header.Set("User-Agent", "gowsdl/0.1")
157238
req.Close = true
158239

0 commit comments

Comments
 (0)