Skip to content

Commit

Permalink
BREAKING: update schema to more accurately reflect the standard
Browse files Browse the repository at this point in the history
This commit adds missing fields and structures. 

This commit turns some fields from pointers to a single optional struct into 
the more correct slice of struct. In those cases the field name also changes 
to plural.

This commit also improves the way XML signatures are modeled. It turns out that
some libraries (and maybe the actual SAML standard) require that elements appear
in a particular order, including <ds:Signature> elements. To model this we add
Signature fields as an *etree.Element. These structs also grow an Element() 
method that serializes the struct into a *etree.Element. This is required to 
correctly compute digests of the XML as specified by xmldsig.
  • Loading branch information
crewjam committed May 23, 2017
1 parent 05bca43 commit b595cb6
Show file tree
Hide file tree
Showing 15 changed files with 1,794 additions and 524 deletions.
392 changes: 257 additions & 135 deletions identity_provider.go

Large diffs are not rendered by default.

445 changes: 354 additions & 91 deletions identity_provider_test.go

Large diffs are not rendered by default.

250 changes: 204 additions & 46 deletions metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,66 @@ package saml
import (
"encoding/xml"
"time"

"github.com/beevik/etree"
)

// HTTPPostBinding is the official URN for the HTTP-POST binding (transport)
const HTTPPostBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
var HTTPPostBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"

// HTTPRedirectBinding is the official URN for the HTTP-Redirect binding (transport)
const HTTPRedirectBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
var HTTPRedirectBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"

// EntitiesDescriptor represents the SAML object of the same name.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf section 2.3.1
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.1
type EntitiesDescriptor struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntitiesDescriptor"`
EntityDescriptor []*Metadata `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"`
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntitiesDescriptor"`
ID *string `xml:",attr,omitempty"`
ValidUntil *time.Time `xml:"validUntil,attr,omitempty"`
CacheDuration *time.Duration `xml:"cacheDuration,attr,omitempty"`
Name *string `xml:",attr,omitempty"`
Signature *etree.Element
EntitiesDescriptors []EntitiesDescriptor `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntitiesDescriptor"`
EntityDescriptors []EntityDescriptor `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"`
}

// Metadata represents the SAML EntityDescriptor object.
// Metadata as been renamed to EntityDescriptor
//
// This change was made to be consistent with the rest of the API which uses names
// from the SAML specification for types.
//
// This is a tombstone to help you discover this fact. You should update references
// to saml.Metadata to be saml.EntityDescriptor.
var Metadata = struct{}{}

// EntityDescriptor represents the SAML EntityDescriptor object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf section 2.3.2
type Metadata struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"`
ValidUntil time.Time `xml:"validUntil,attr"`
CacheDuration time.Duration `xml:"cacheDuration,attr,omitempty"`
EntityID string `xml:"entityID,attr"`
SPSSODescriptor *SPSSODescriptor `xml:"SPSSODescriptor"`
IDPSSODescriptor *IDPSSODescriptor `xml:"IDPSSODescriptor"`
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.2
type EntityDescriptor struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"`
EntityID string `xml:"entityID,attr"`
ID string `xml:",attr,omitempty"`
ValidUntil time.Time `xml:"validUntil,attr,omitempty"`
CacheDuration time.Duration `xml:"cacheDuration,attr,omitempty"`
Signature *etree.Element
RoleDescriptors []RoleDescriptor `xml:"RoleDescriptor"`
IDPSSODescriptors []IDPSSODescriptor `xml:"IDPSSODescriptor"`
SPSSODescriptors []SPSSODescriptor `xml:"SPSSODescriptor"`
AuthnAuthorityDescriptors []AuthnAuthorityDescriptor `xml:"AuthnAuthorityDescriptor"`
AttributeAuthorityDescriptors []AttributeAuthorityDescriptor `xml:"AttributeAuthorityDescriptor"`
PDPDescriptors []PDPDescriptor `xml:"PDPDescriptor"`
AffiliationDescriptor *AffiliationDescriptor
Organization *Organization
ContactPerson *ContactPerson
AdditionalMetadataLocations []string `xml:"AdditionalMetadataLocation"`
}

func (m *Metadata) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias Metadata
// MarshalXML implements xml.Marshaler
func (m *EntityDescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias EntityDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr"`
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
*Alias
}{
ValidUntil: RelaxedTime(m.ValidUntil),
Expand All @@ -43,10 +71,11 @@ func (m *Metadata) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.Encode(aux)
}

func (m *Metadata) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias Metadata
// UnmarshalXML implements xml.Unmarshaler
func (m *EntityDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias EntityDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr"`
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
*Alias
}{
Alias: (*Alias)(m),
Expand All @@ -58,6 +87,58 @@ func (m *Metadata) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
return nil
}

// Organization represents the SAML Organization object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.2.1
type Organization struct {
OrganizationNames []LocalizedName `xml:"OrganizationName"`
OrganizationDisplayNames []LocalizedName `xml:"OrganizationDisplayName"`
OrganizationURLs []LocalizedURI `xml:"OrganizationURL"`
}

// LocalizedName represents the SAML type localizedNameType.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.4
type LocalizedName struct {
Lang string `xml:"xml lang,attr"`
Value string `xml:",chardata"`
}

// LocalizedURI represents the SAML type localizedURIType.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.5
type LocalizedURI struct {
Lang string `xml:"xml lang,attr"`
Value string `xml:",chardata"`
}

// ContactPerson represents the SAML element ContactPerson.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.2.2
type ContactPerson struct {
ContactType string `xml:"contactType,attr"`
Company string
GivenName string
SurName string
EmailAddresses []string `xml:"EmailAddress"`
TelephoneNumbers []string `xml:"TelephoneNumber"`
}

// RoleDescriptor represents the SAML element RoleDescriptor.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.1
type RoleDescriptor struct {
ID string `xml:",attr,omitempty"`
ValidUntil time.Time `xml:"validUntil,attr,omitempty"`
CacheDuration time.Duration `xml:"cacheDuration,attr,omitempty"`
ProtocolSupportEnumeration string `xml:"protocolSupportEnumeration,attr"`
ErrorURL string `xml:"errorURL,attr,omitempty"`
Signature *etree.Element
KeyDescriptors []KeyDescriptor `xml:"KeyDescriptor,omitempty"`
Organization *Organization `xml:"Organization,omitempty"`
ContactPeople []ContactPerson `xml:"ContactPerson,omitempty"`
}

// KeyDescriptor represents the XMLSEC object of the same name
type KeyDescriptor struct {
Use string `xml:"use,attr"`
Expand All @@ -71,14 +152,16 @@ type EncryptionMethod struct {
}

// KeyInfo represents the XMLSEC object of the same name
//
// TODO(ross): revisit xmldsig and make this type more complete
type KeyInfo struct {
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# KeyInfo"`
Certificate string `xml:"X509Data>X509Certificate"`
}

// Endpoint represents the SAML EndpointType object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf section 2.2.2
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.2
type Endpoint struct {
Binding string `xml:"Binding,attr"`
Location string `xml:"Location,attr"`
Expand All @@ -87,38 +170,113 @@ type Endpoint struct {

// IndexedEndpoint represents the SAML IndexedEndpointType object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf section 2.2.3
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.3
type IndexedEndpoint struct {
Binding string `xml:"Binding,attr"`
Location string `xml:"Location,attr"`
Index int `xml:"index,attr"`
Binding string `xml:"Binding,attr"`
Location string `xml:"Location,attr"`
ResponseLocation *string `xml:"ResponseLocation,attr,omitempty"`
Index int `xml:"index,attr"`
IsDefault *bool `xml:"isDefault,attr"`
}

// SPSSODescriptor represents the SAML SPSSODescriptorType object.
// SSODescriptor represents the SAML complex type SSODescriptor
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf section 2.4.2
type SPSSODescriptor struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata SPSSODescriptor"`
AuthnRequestsSigned bool `xml:",attr"`
WantAssertionsSigned bool `xml:",attr"`
ProtocolSupportEnumeration string `xml:"protocolSupportEnumeration,attr"`
KeyDescriptor []KeyDescriptor `xml:"KeyDescriptor"`
ArtifactResolutionService []IndexedEndpoint `xml:"ArtifactResolutionService"`
SingleLogoutService []Endpoint `xml:"SingleLogoutService"`
ManageNameIDService []Endpoint
NameIDFormat []string `xml:"NameIDFormat"`
AssertionConsumerService []IndexedEndpoint `xml:"AssertionConsumerService"`
AttributeConsumingService []interface{}
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.2
type SSODescriptor struct {
RoleDescriptor
ArtifactResolutionServices []IndexedEndpoint `xml:"ArtifactResolutionService"`
SingleLogoutServices []Endpoint `xml:"SingleLogoutService"`
ManageNameIDServices []Endpoint `xml:"ManageNameIDService"`
NameIDFormats []NameIDFormat `xml:"NameIDFormat"`
}

// IDPSSODescriptor represents the SAML IDPSSODescriptorType object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf section 2.4.3
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.3
type IDPSSODescriptor struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata IDPSSODescriptor"`
WantAuthnRequestsSigned bool `xml:",attr"`
ProtocolSupportEnumeration string `xml:"protocolSupportEnumeration,attr"`
KeyDescriptor []KeyDescriptor `xml:"KeyDescriptor"`
NameIDFormat []string `xml:"NameIDFormat"`
SingleSignOnService []Endpoint `xml:"SingleSignOnService"`
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata IDPSSODescriptor"`
SSODescriptor
WantAuthnRequestsSigned *bool `xml:",attr"`

SingleSignOnServices []Endpoint `xml:"SingleSignOnService"`
NameIDMappingServices []Endpoint `xml:"NameIDMappingService"`
AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"`
AttributeProfiles []string `xml:"AttributeProfile"`
Attributes []Attribute `xml:"Attribute"`
}

// SPSSODescriptor represents the SAML SPSSODescriptorType object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.2
type SPSSODescriptor struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata SPSSODescriptor"`
SSODescriptor
AuthnRequestsSigned *bool `xml:",attr"`
WantAssertionsSigned *bool `xml:",attr"`
AssertionConsumerServices []IndexedEndpoint `xml:"AssertionConsumerService"`
AttributeConsumingServices []AttributeConsumingService `xml:"AttributeConsumingService"`
}

// AttributeConsumingService represents the SAML AttributeConsumingService object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.4.1
type AttributeConsumingService struct {
Index int `xml:"index,attr"`
IsDefault *bool `xml:"isDefault,attr"`
ServiceNames []LocalizedName `xml:"ServiceName"`
ServiceDescriptions []LocalizedName `xml:"ServiceDescription"`
RequestedAttributes []RequestedAttribute `xml:"RequestedAttribute"`
}

// RequestedAttribute represents the SAML RequestedAttribute object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.4.2
type RequestedAttribute struct {
Attribute
IsRequired *bool `xml:"isRequired,attr"`
}

// AuthnAuthorityDescriptor represents the SAML AuthnAuthorityDescriptor object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.5
type AuthnAuthorityDescriptor struct {
RoleDescriptor
AuthnQueryServices []Endpoint `xml:"AuthnQueryService"`
AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"`
NameIDFormats []NameIDFormat `xml:"NameIDFormat"`
}

// PDPDescriptor represents the SAML PDPDescriptor object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.6
type PDPDescriptor struct {
RoleDescriptor
AuthzServices []Endpoint `xml:"AuthzService"`
AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"`
NameIDFormats []NameIDFormat `xml:"NameIDFormat"`
}

// AttributeAuthorityDescriptor represents the SAML AttributeAuthorityDescriptor object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.7
type AttributeAuthorityDescriptor struct {
RoleDescriptor
AttributeServices []Endpoint `xml:"AttributeService"`
AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"`
NameIDFormats []NameIDFormat `xml:"NameIDFormat"`
AttributeProfiles []string `xml:"AttributeProfile"`
Attributes []Attribute `xml:"Attribute"`
}

// AffiliationDescriptor represents the SAML AffiliationDescriptor object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.5
type AffiliationDescriptor struct {
AffiliationOwnerID string `xml:"affiliationOwnerID,attr"`
ID string `xml:",attr"`
ValidUntil time.Time `xml:"validUntil,attr,omitempty"`
CacheDuration time.Duration `xml:"cacheDuration,attr"`
Signature *etree.Element
AffiliateMembers []string `xml:"AffiliateMember"`
KeyDescriptors []KeyDescriptor `xml:"KeyDescriptor"`
}
Loading

0 comments on commit b595cb6

Please sign in to comment.