diff --git a/internal/idp/providers/saml/saml.go b/internal/idp/providers/saml/saml.go index e0391bc099..11b1d36da2 100644 --- a/internal/idp/providers/saml/saml.go +++ b/internal/idp/providers/saml/saml.go @@ -126,7 +126,7 @@ func ParseMetadata(metadata []byte) (*saml.EntityDescriptor, error) { if _, err := reader.Seek(0, io.SeekStart); err != nil { return nil, err } - entities := &saml.EntitiesDescriptor{} + entities := &EntitiesDescriptor{} if err := decoder.Decode(entities); err != nil { return nil, err } @@ -253,3 +253,26 @@ func nameIDFormatFromDomain(format domain.SAMLNameIDFormat) saml.NameIDFormat { return saml.UnspecifiedNameIDFormat } } + +// EntitiesDescriptor is a workaround until we eventually fork the crewjam/saml library, since maintenance on that repo seems to have stopped. +// This is to be able to handle xsd:duration format using the UnmarshalXML method. +// crewjam/saml only implements the xsd:dateTime format for EntityDescriptor, but not EntitiesDescriptor. +type EntitiesDescriptor saml.EntitiesDescriptor + +// UnmarshalXML implements xml.Unmarshaler +func (m *EntitiesDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + type Alias EntitiesDescriptor + aux := &struct { + ValidUntil *saml.RelaxedTime `xml:"validUntil,attr,omitempty"` + CacheDuration *saml.Duration `xml:"cacheDuration,attr,omitempty"` + *Alias + }{ + Alias: (*Alias)(m), + } + if err := d.DecodeElement(aux, &start); err != nil { + return err + } + m.ValidUntil = (*time.Time)(aux.ValidUntil) + m.CacheDuration = (*time.Duration)(aux.CacheDuration) + return nil +} diff --git a/internal/idp/providers/saml/saml_test.go b/internal/idp/providers/saml/saml_test.go index 801ddd36fc..69ff231ccc 100644 --- a/internal/idp/providers/saml/saml_test.go +++ b/internal/idp/providers/saml/saml_test.go @@ -3,6 +3,7 @@ package saml import ( "encoding/xml" "testing" + "time" "github.com/crewjam/saml" "github.com/crewjam/saml/samlsp" @@ -271,6 +272,31 @@ func TestParseMetadata(t *testing.T) { }, nil, }, + { + "valid entities using xsd duration descriptor", + args{ + metadata: []byte(``), + }, + &saml.EntityDescriptor{ + EntityID: "http://localhost:8000/metadata", + CacheDuration: 5 * time.Hour, + IDPSSODescriptors: []saml.IDPSSODescriptor{ + { + XMLName: xml.Name{ + Space: "urn:oasis:names:tc:SAML:2.0:metadata", + Local: "IDPSSODescriptor", + }, + SingleSignOnServices: []saml.Endpoint{ + { + Binding: saml.HTTPRedirectBinding, + Location: "http://localhost:8000/sso", + }, + }, + }, + }, + }, + nil, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {