github.com/crewjam/saml@v0.4.14/metadata.go (about) 1 package saml 2 3 import ( 4 "encoding/xml" 5 "fmt" 6 "net/url" 7 "time" 8 9 "github.com/beevik/etree" 10 ) 11 12 // HTTPPostBinding is the official URN for the HTTP-POST binding (transport) 13 const HTTPPostBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" 14 15 // HTTPRedirectBinding is the official URN for the HTTP-Redirect binding (transport) 16 const HTTPRedirectBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" 17 18 // HTTPArtifactBinding is the official URN for the HTTP-Artifact binding (transport) 19 const HTTPArtifactBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" 20 21 // SOAPBinding is the official URN for the SOAP binding (transport) 22 const SOAPBinding = "urn:oasis:names:tc:SAML:2.0:bindings:SOAP" 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 27 // EntitiesDescriptor represents the SAML object of the same name. 28 // 29 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.1 30 type EntitiesDescriptor struct { 31 XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntitiesDescriptor"` 32 ID *string `xml:",attr,omitempty"` 33 ValidUntil *time.Time `xml:"validUntil,attr,omitempty"` 34 CacheDuration *time.Duration `xml:"cacheDuration,attr,omitempty"` 35 Name *string `xml:",attr,omitempty"` 36 Signature *etree.Element 37 EntitiesDescriptors []EntitiesDescriptor `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntitiesDescriptor"` 38 EntityDescriptors []EntityDescriptor `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"` 39 } 40 41 // Metadata as been renamed to EntityDescriptor 42 // 43 // This change was made to be consistent with the rest of the API which uses names 44 // from the SAML specification for types. 45 // 46 // This is a tombstone to help you discover this fact. You should update references 47 // to saml.Metadata to be saml.EntityDescriptor. 48 var Metadata = struct{}{} 49 50 // EntityDescriptor represents the SAML EntityDescriptor object. 51 // 52 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.2 53 type EntityDescriptor struct { 54 XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"` 55 EntityID string `xml:"entityID,attr"` 56 ID string `xml:",attr,omitempty"` 57 ValidUntil time.Time `xml:"validUntil,attr,omitempty"` 58 CacheDuration time.Duration `xml:"cacheDuration,attr,omitempty"` 59 Signature *etree.Element 60 RoleDescriptors []RoleDescriptor `xml:"RoleDescriptor"` 61 IDPSSODescriptors []IDPSSODescriptor `xml:"IDPSSODescriptor"` 62 SPSSODescriptors []SPSSODescriptor `xml:"SPSSODescriptor"` 63 AuthnAuthorityDescriptors []AuthnAuthorityDescriptor `xml:"AuthnAuthorityDescriptor"` 64 AttributeAuthorityDescriptors []AttributeAuthorityDescriptor `xml:"AttributeAuthorityDescriptor"` 65 PDPDescriptors []PDPDescriptor `xml:"PDPDescriptor"` 66 AffiliationDescriptor *AffiliationDescriptor 67 Organization *Organization 68 ContactPerson *ContactPerson 69 AdditionalMetadataLocations []string `xml:"AdditionalMetadataLocation"` 70 } 71 72 // MarshalXML implements xml.Marshaler 73 func (m EntityDescriptor) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { 74 type Alias EntityDescriptor 75 aux := &struct { 76 ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"` 77 CacheDuration Duration `xml:"cacheDuration,attr,omitempty"` 78 *Alias 79 }{ 80 ValidUntil: RelaxedTime(m.ValidUntil), 81 CacheDuration: Duration(m.CacheDuration), 82 Alias: (*Alias)(&m), 83 } 84 return e.Encode(aux) 85 } 86 87 // UnmarshalXML implements xml.Unmarshaler 88 func (m *EntityDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 89 type Alias EntityDescriptor 90 aux := &struct { 91 ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"` 92 CacheDuration Duration `xml:"cacheDuration,attr,omitempty"` 93 *Alias 94 }{ 95 Alias: (*Alias)(m), 96 } 97 if err := d.DecodeElement(aux, &start); err != nil { 98 return err 99 } 100 m.ValidUntil = time.Time(aux.ValidUntil) 101 m.CacheDuration = time.Duration(aux.CacheDuration) 102 return nil 103 } 104 105 // Organization represents the SAML Organization object. 106 // 107 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.2.1 108 type Organization struct { 109 OrganizationNames []LocalizedName `xml:"OrganizationName"` 110 OrganizationDisplayNames []LocalizedName `xml:"OrganizationDisplayName"` 111 OrganizationURLs []LocalizedURI `xml:"OrganizationURL"` 112 } 113 114 // LocalizedName represents the SAML type localizedNameType. 115 // 116 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.4 117 type LocalizedName struct { 118 Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr"` 119 Value string `xml:",chardata"` 120 } 121 122 // LocalizedURI represents the SAML type localizedURIType. 123 // 124 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.5 125 type LocalizedURI struct { 126 Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr"` 127 Value string `xml:",chardata"` 128 } 129 130 // ContactPerson represents the SAML element ContactPerson. 131 // 132 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.2.2 133 type ContactPerson struct { 134 ContactType string `xml:"contactType,attr"` 135 Company string 136 GivenName string 137 SurName string 138 EmailAddresses []string `xml:"EmailAddress"` 139 TelephoneNumbers []string `xml:"TelephoneNumber"` 140 } 141 142 // RoleDescriptor represents the SAML element RoleDescriptor. 143 // 144 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.1 145 type RoleDescriptor struct { 146 ID string `xml:",attr,omitempty"` 147 ValidUntil *time.Time `xml:"validUntil,attr,omitempty"` 148 CacheDuration time.Duration `xml:"cacheDuration,attr,omitempty"` 149 ProtocolSupportEnumeration string `xml:"protocolSupportEnumeration,attr"` 150 ErrorURL string `xml:"errorURL,attr,omitempty"` 151 Signature *etree.Element 152 KeyDescriptors []KeyDescriptor `xml:"KeyDescriptor,omitempty"` 153 Organization *Organization `xml:"Organization,omitempty"` 154 ContactPeople []ContactPerson `xml:"ContactPerson,omitempty"` 155 } 156 157 // KeyDescriptor represents the XMLSEC object of the same name 158 type KeyDescriptor struct { 159 Use string `xml:"use,attr"` 160 KeyInfo KeyInfo `xml:"http://www.w3.org/2000/09/xmldsig# KeyInfo"` 161 EncryptionMethods []EncryptionMethod `xml:"EncryptionMethod"` 162 } 163 164 // EncryptionMethod represents the XMLSEC object of the same name 165 type EncryptionMethod struct { 166 Algorithm string `xml:"Algorithm,attr"` 167 } 168 169 // KeyInfo represents the XMLSEC object of the same name 170 type KeyInfo struct { 171 XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# KeyInfo"` 172 X509Data X509Data `xml:"X509Data"` 173 } 174 175 // X509Data represents the XMLSEC object of the same name 176 type X509Data struct { 177 XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# X509Data"` 178 X509Certificates []X509Certificate `xml:"X509Certificate"` 179 } 180 181 // X509Certificate represents the XMLSEC object of the same name 182 type X509Certificate struct { 183 XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# X509Certificate"` 184 Data string `xml:",chardata"` 185 } 186 187 // Endpoint represents the SAML EndpointType object. 188 // 189 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.2 190 type Endpoint struct { 191 Binding string `xml:"Binding,attr"` 192 Location string `xml:"Location,attr"` 193 ResponseLocation string `xml:"ResponseLocation,attr,omitempty"` 194 } 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 266 // IndexedEndpoint represents the SAML IndexedEndpointType object. 267 // 268 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.3 269 type IndexedEndpoint struct { 270 Binding string `xml:"Binding,attr"` 271 Location string `xml:"Location,attr"` 272 ResponseLocation *string `xml:"ResponseLocation,attr,omitempty"` 273 Index int `xml:"index,attr"` 274 IsDefault *bool `xml:"isDefault,attr"` 275 } 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 309 // SSODescriptor represents the SAML complex type SSODescriptor 310 // 311 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.2 312 type SSODescriptor struct { 313 RoleDescriptor 314 ArtifactResolutionServices []IndexedEndpoint `xml:"ArtifactResolutionService"` 315 SingleLogoutServices []Endpoint `xml:"SingleLogoutService"` 316 ManageNameIDServices []Endpoint `xml:"ManageNameIDService"` 317 NameIDFormats []NameIDFormat `xml:"NameIDFormat"` 318 } 319 320 // IDPSSODescriptor represents the SAML IDPSSODescriptorType object. 321 // 322 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.3 323 type IDPSSODescriptor struct { 324 XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata IDPSSODescriptor"` 325 SSODescriptor 326 WantAuthnRequestsSigned *bool `xml:",attr"` 327 328 SingleSignOnServices []Endpoint `xml:"SingleSignOnService"` 329 ArtifactResolutionServices []Endpoint `xml:"ArtifactResolutionService"` 330 NameIDMappingServices []Endpoint `xml:"NameIDMappingService"` 331 AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"` 332 AttributeProfiles []string `xml:"AttributeProfile"` 333 Attributes []Attribute `xml:"Attribute"` 334 } 335 336 // SPSSODescriptor represents the SAML SPSSODescriptorType object. 337 // 338 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.2 339 type SPSSODescriptor struct { 340 XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata SPSSODescriptor"` 341 SSODescriptor 342 AuthnRequestsSigned *bool `xml:",attr"` 343 WantAssertionsSigned *bool `xml:",attr"` 344 AssertionConsumerServices []IndexedEndpoint `xml:"AssertionConsumerService"` 345 AttributeConsumingServices []AttributeConsumingService `xml:"AttributeConsumingService"` 346 } 347 348 // AttributeConsumingService represents the SAML AttributeConsumingService object. 349 // 350 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.4.1 351 type AttributeConsumingService struct { 352 Index int `xml:"index,attr"` 353 IsDefault *bool `xml:"isDefault,attr"` 354 ServiceNames []LocalizedName `xml:"ServiceName"` 355 ServiceDescriptions []LocalizedName `xml:"ServiceDescription"` 356 RequestedAttributes []RequestedAttribute `xml:"RequestedAttribute"` 357 } 358 359 // RequestedAttribute represents the SAML RequestedAttribute object. 360 // 361 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.4.2 362 type RequestedAttribute struct { 363 Attribute 364 IsRequired *bool `xml:"isRequired,attr"` 365 } 366 367 // AuthnAuthorityDescriptor represents the SAML AuthnAuthorityDescriptor object. 368 // 369 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.5 370 type AuthnAuthorityDescriptor struct { 371 RoleDescriptor 372 AuthnQueryServices []Endpoint `xml:"AuthnQueryService"` 373 AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"` 374 NameIDFormats []NameIDFormat `xml:"NameIDFormat"` 375 } 376 377 // PDPDescriptor represents the SAML PDPDescriptor object. 378 // 379 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.6 380 type PDPDescriptor struct { 381 RoleDescriptor 382 AuthzServices []Endpoint `xml:"AuthzService"` 383 AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"` 384 NameIDFormats []NameIDFormat `xml:"NameIDFormat"` 385 } 386 387 // AttributeAuthorityDescriptor represents the SAML AttributeAuthorityDescriptor object. 388 // 389 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.7 390 type AttributeAuthorityDescriptor struct { 391 RoleDescriptor 392 AttributeServices []Endpoint `xml:"AttributeService"` 393 AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"` 394 NameIDFormats []NameIDFormat `xml:"NameIDFormat"` 395 AttributeProfiles []string `xml:"AttributeProfile"` 396 Attributes []Attribute `xml:"Attribute"` 397 } 398 399 // AffiliationDescriptor represents the SAML AffiliationDescriptor object. 400 // 401 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.5 402 type AffiliationDescriptor struct { 403 AffiliationOwnerID string `xml:"affiliationOwnerID,attr"` 404 ID string `xml:",attr"` 405 ValidUntil time.Time `xml:"validUntil,attr,omitempty"` 406 CacheDuration time.Duration `xml:"cacheDuration,attr"` 407 Signature *etree.Element 408 AffiliateMembers []string `xml:"AffiliateMember"` 409 KeyDescriptors []KeyDescriptor `xml:"KeyDescriptor"` 410 }