diff --git a/identity_provider.go b/identity_provider.go index abaaad68..6d37bd27 100644 --- a/identity_provider.go +++ b/identity_provider.go @@ -113,17 +113,20 @@ type IdentityProvider struct { func (idp *IdentityProvider) Metadata() *EntityDescriptor { certStr := base64.StdEncoding.EncodeToString(idp.Certificate.Raw) - var validDuration time.Duration - if idp.ValidDuration != nil { - validDuration = *idp.ValidDuration - } else { - validDuration = DefaultValidDuration + var validUntil *time.Time + + if idp.ValidDuration == nil { + // To avoid breaking backward compatibility, nil here means default + defaultValid := time.Now().Add(DefaultValidDuration) + validUntil = &defaultValid + } else if *(idp.ValidDuration) == METADATA_OMIT_VALID_UNTIL { + validUntil = nil } ed := &EntityDescriptor{ EntityID: idp.MetadataURL.String(), - ValidUntil: TimeNow().Add(validDuration), - CacheDuration: validDuration, + ValidUntil: validUntil, + CacheDuration: idp.ValidDuration, IDPSSODescriptors: []IDPSSODescriptor{ { SSODescriptor: SSODescriptor{ diff --git a/metadata.go b/metadata.go index 006a9e67..544b2a2b 100644 --- a/metadata.go +++ b/metadata.go @@ -51,11 +51,11 @@ var Metadata = struct{}{} // // 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"` + 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"` @@ -69,16 +69,31 @@ type EntityDescriptor struct { AdditionalMetadataLocations []string `xml:"AdditionalMetadataLocation"` } +const METADATA_OMIT_VALID_UNTIL = -1 + // MarshalXML implements xml.Marshaler func (m EntityDescriptor) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { type Alias EntityDescriptor + var rt *RelaxedTime + + var cacheDuration *Duration + if m.CacheDuration != nil { + d := Duration(*m.CacheDuration) + cacheDuration = &d + } + + if m.ValidUntil != nil { + rttemp := RelaxedTime(*m.ValidUntil) + rt = &rttemp + } + aux := &struct { - ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"` - CacheDuration Duration `xml:"cacheDuration,attr,omitempty"` + ValidUntil *RelaxedTime `xml:"validUntil,attr,omitempty"` + CacheDuration *Duration `xml:"cacheDuration,attr,omitempty"` *Alias }{ - ValidUntil: RelaxedTime(m.ValidUntil), - CacheDuration: Duration(m.CacheDuration), + ValidUntil: rt, + CacheDuration: cacheDuration, Alias: (*Alias)(&m), } return e.Encode(aux) @@ -88,8 +103,8 @@ func (m EntityDescriptor) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { func (m *EntityDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type Alias EntityDescriptor aux := &struct { - ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"` - CacheDuration Duration `xml:"cacheDuration,attr,omitempty"` + ValidUntil *RelaxedTime `xml:"validUntil,attr,omitempty"` + CacheDuration *Duration `xml:"cacheDuration,attr,omitempty"` *Alias }{ Alias: (*Alias)(m), @@ -97,8 +112,20 @@ func (m *EntityDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) if err := d.DecodeElement(aux, &start); err != nil { return err } - m.ValidUntil = time.Time(aux.ValidUntil) - m.CacheDuration = time.Duration(aux.CacheDuration) + + if aux.ValidUntil != nil { + t := time.Time(*aux.ValidUntil) + m.ValidUntil = &t + } else { + m.ValidUntil = nil + } + + if aux.CacheDuration != nil { + d := time.Duration(*aux.CacheDuration) + m.CacheDuration = &d + } else { + m.ValidUntil = nil + } return nil } diff --git a/service_provider.go b/service_provider.go index df584d79..4b37386e 100644 --- a/service_provider.go +++ b/service_provider.go @@ -143,14 +143,19 @@ const DefaultCacheDuration = time.Hour * 24 * 1 // Metadata returns the service provider metadata func (sp *ServiceProvider) Metadata() *EntityDescriptor { - validDuration := DefaultValidDuration + var validUntil *time.Time + now := TimeNow() + if sp.MetadataValidDuration > 0 { - validDuration = sp.MetadataValidDuration + *validUntil = now.Add(sp.MetadataValidDuration) + } else if sp.MetadataValidDuration == METADATA_OMIT_VALID_UNTIL { + validUntil = nil + } else { + *validUntil = now.Add(DefaultValidDuration) } authnRequestsSigned := len(sp.SignatureMethod) > 0 wantAssertionsSigned := true - validUntil := TimeNow().Add(validDuration) var keyDescriptors []KeyDescriptor if sp.Certificate != nil { @@ -209,7 +214,7 @@ func (sp *ServiceProvider) Metadata() *EntityDescriptor { RoleDescriptor: RoleDescriptor{ ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol", KeyDescriptors: keyDescriptors, - ValidUntil: &validUntil, + ValidUntil: validUntil, }, SingleLogoutServices: sloEndpoints, NameIDFormats: []NameIDFormat{sp.AuthnNameIDFormat},