@@ -19,8 +19,8 @@ type SOAPEnvelope struct {
1919
2020type 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
2626type 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+
4284type BasicAuth struct {
43- Login string
85+ Login string
4486 Password string
4587}
4688
4789type 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
54135func (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
120201func (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