@@ -2,6 +2,8 @@ package saml
2
2
3
3
import (
4
4
"encoding/xml"
5
+ "fmt"
6
+ "net/url"
5
7
"time"
6
8
7
9
"github.com/beevik/etree"
@@ -19,6 +21,9 @@ const HTTPArtifactBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
19
21
// SOAPBinding is the official URN for the SOAP binding (transport)
20
22
const SOAPBinding = "urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
21
23
24
+ // SOAPBindingV1 is the URN for the SOAP binding in SAML 1.0
25
+ const SOAPBindingV1 = "urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding"
26
+
22
27
// EntitiesDescriptor represents the SAML object of the same name.
23
28
//
24
29
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.1
@@ -188,6 +193,76 @@ type Endpoint struct {
188
193
ResponseLocation string `xml:"ResponseLocation,attr,omitempty"`
189
194
}
190
195
196
+ func checkEndpointLocation (binding string , location string ) (string , error ) {
197
+ // Within the SAML standard, the complex type EndpointType describes a
198
+ // SAML protocol binding endpoint at which a SAML entity can be sent
199
+ // protocol messages. In particular, the location of an endpoint type is
200
+ // defined as follows in the Metadata for the OASIS Security Assertion
201
+ // Markup Language (SAML) V2.0 - 2.2.2 Complex Type EndpointType:
202
+ //
203
+ // Location [Required] A required URI attribute that specifies the
204
+ // location of the endpoint. The allowable syntax of this URI depends
205
+ // on the protocol binding.
206
+ switch binding {
207
+ case HTTPPostBinding ,
208
+ HTTPRedirectBinding ,
209
+ HTTPArtifactBinding ,
210
+ SOAPBinding ,
211
+ SOAPBindingV1 :
212
+ locationURL , err := url .Parse (location )
213
+ if err != nil {
214
+ return "" , fmt .Errorf ("invalid url %q: %w" , location , err )
215
+ }
216
+ switch locationURL .Scheme {
217
+ case "http" , "https" :
218
+ // ok
219
+ default :
220
+ return "" , fmt .Errorf ("invalid url scheme %q for binding %q" ,
221
+ locationURL .Scheme , binding )
222
+ }
223
+ default :
224
+ // We don't know what form location should take, but the protocol
225
+ // requires that we validate its syntax.
226
+ //
227
+ // In practice, lots of metadata contains random bindings, for example
228
+ // "urn:mace:shibboleth:1.0:profiles:AuthnRequest" from our own test suite.
229
+ //
230
+ // We can't fail, but we also can't allow a location parameter whose syntax we
231
+ // cannot verify. The least-bad course of action here is to set location to
232
+ // and empty string, and hope the caller doesn't care need it.
233
+ location = ""
234
+ }
235
+
236
+ return location , nil
237
+ }
238
+
239
+ // UnmarshalXML implements xml.Unmarshaler
240
+ func (m * Endpoint ) UnmarshalXML (d * xml.Decoder , start xml.StartElement ) error {
241
+ type Alias Endpoint
242
+ aux := & struct {
243
+ * Alias
244
+ }{
245
+ Alias : (* Alias )(m ),
246
+ }
247
+ if err := d .DecodeElement (aux , & start ); err != nil {
248
+ return err
249
+ }
250
+
251
+ var err error
252
+ m .Location , err = checkEndpointLocation (m .Binding , m .Location )
253
+ if err != nil {
254
+ return err
255
+ }
256
+ if m .ResponseLocation != "" {
257
+ m .ResponseLocation , err = checkEndpointLocation (m .Binding , m .ResponseLocation )
258
+ if err != nil {
259
+ return err
260
+ }
261
+ }
262
+
263
+ return nil
264
+ }
265
+
191
266
// IndexedEndpoint represents the SAML IndexedEndpointType object.
192
267
//
193
268
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.3
@@ -199,6 +274,38 @@ type IndexedEndpoint struct {
199
274
IsDefault * bool `xml:"isDefault,attr"`
200
275
}
201
276
277
+ // UnmarshalXML implements xml.Unmarshaler
278
+ func (m * IndexedEndpoint ) UnmarshalXML (d * xml.Decoder , start xml.StartElement ) error {
279
+ type Alias IndexedEndpoint
280
+ aux := & struct {
281
+ * Alias
282
+ }{
283
+ Alias : (* Alias )(m ),
284
+ }
285
+ if err := d .DecodeElement (aux , & start ); err != nil {
286
+ return err
287
+ }
288
+
289
+ var err error
290
+ m .Location , err = checkEndpointLocation (m .Binding , m .Location )
291
+ if err != nil {
292
+ return err
293
+ }
294
+ if m .ResponseLocation != nil {
295
+ responseLocation , err := checkEndpointLocation (m .Binding , * m .ResponseLocation )
296
+ if err != nil {
297
+ return err
298
+ }
299
+ if responseLocation != "" {
300
+ m .ResponseLocation = & responseLocation
301
+ } else {
302
+ m .ResponseLocation = nil
303
+ }
304
+ }
305
+
306
+ return nil
307
+ }
308
+
202
309
// SSODescriptor represents the SAML complex type SSODescriptor
203
310
//
204
311
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.2
0 commit comments