Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cacheDuration, validUntil to RoleDescriptors #33

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions identity_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ func (idp *IdentityProvider) Metadata() *EntityDescriptor {
SSODescriptor: SSODescriptor{
RoleDescriptor: RoleDescriptor{
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
CacheDuration: validDuration,
ValidUntil: TimeNow().Add(validDuration),
KeyDescriptors: []KeyDescriptor{
{
Use: "signing",
Expand Down
5 changes: 4 additions & 1 deletion identity_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ func TestIDPCanProduceMetadata(t *testing.T) {
{
SSODescriptor: SSODescriptor{
RoleDescriptor: RoleDescriptor{
ValidUntil: TimeNow().Add(DefaultValidDuration),
CacheDuration: DefaultValidDuration,
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
KeyDescriptors: []KeyDescriptor{
{
Expand Down Expand Up @@ -207,7 +209,8 @@ func TestIDPHTTPCanHandleMetadataRequest(t *testing.T) {
test.IDP.Handler().ServeHTTP(w, r)
assert.Check(t, is.Equal(http.StatusOK, w.Code))
assert.Check(t, is.Equal("application/samlmetadata+xml", w.Header().Get("Content-type")))
assert.Check(t, strings.HasPrefix(string(w.Body.Bytes()), "<EntityDescriptor"),
body := string(w.Body.Bytes())
assert.Check(t, strings.HasPrefix(body, "<EntityDescriptor"),
string(w.Body.Bytes()))
}

Expand Down
171 changes: 167 additions & 4 deletions metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ type ContactPerson struct {
// 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"`
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"`
Expand Down Expand Up @@ -214,9 +214,9 @@ type SSODescriptor struct {
//
// 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"`
SSODescriptor
WantAuthnRequestsSigned *bool `xml:",attr"`
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata IDPSSODescriptor"`
WantAuthnRequestsSigned *bool `xml:",attr"`

SingleSignOnServices []Endpoint `xml:"SingleSignOnService"`
ArtifactResolutionServices []Endpoint `xml:"ArtifactResolutionService"`
Expand All @@ -226,18 +226,82 @@ type IDPSSODescriptor struct {
Attributes []Attribute `xml:"Attribute"`
}

func (m IDPSSODescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias IDPSSODescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
ValidUntil: RelaxedTime(m.ValidUntil),
CacheDuration: Duration(m.CacheDuration),
Alias: (*Alias)(&m),
}
return e.Encode(aux)
}

// UnmarshalXML implements xml.Unmarshaler
func (m *IDPSSODescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias IDPSSODescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration 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
}

// 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
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata SPSSODescriptor"`
AuthnRequestsSigned *bool `xml:",attr"`
WantAssertionsSigned *bool `xml:",attr"`
AssertionConsumerServices []IndexedEndpoint `xml:"AssertionConsumerService"`
AttributeConsumingServices []AttributeConsumingService `xml:"AttributeConsumingService"`
}

func (m SPSSODescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias SPSSODescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
ValidUntil: RelaxedTime(m.ValidUntil),
CacheDuration: Duration(m.CacheDuration),
Alias: (*Alias)(&m),
}
return e.Encode(aux)
}

// UnmarshalXML implements xml.Unmarshaler
func (m *SPSSODescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias SPSSODescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration 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
}

// 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
Expand All @@ -262,33 +326,132 @@ type RequestedAttribute struct {
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.5
type AuthnAuthorityDescriptor struct {
RoleDescriptor
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata AuthnAuthorityDescriptor"`
AuthnQueryServices []Endpoint `xml:"AuthnQueryService"`
AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"`
NameIDFormats []NameIDFormat `xml:"NameIDFormat"`
}

func (m AuthnAuthorityDescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias AuthnAuthorityDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
ValidUntil: RelaxedTime(m.ValidUntil),
CacheDuration: Duration(m.CacheDuration),
Alias: (*Alias)(&m),
}
return e.Encode(aux)
}

// UnmarshalXML implements xml.Unmarshaler
func (m *AuthnAuthorityDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias AuthnAuthorityDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration 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
}

// 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
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata PDPDescriptor"`
AuthzServices []Endpoint `xml:"AuthzService"`
AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"`
NameIDFormats []NameIDFormat `xml:"NameIDFormat"`
}

func (m PDPDescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias PDPDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
ValidUntil: RelaxedTime(m.ValidUntil),
CacheDuration: Duration(m.CacheDuration),
Alias: (*Alias)(&m),
}
return e.Encode(aux)
}

// UnmarshalXML implements xml.Unmarshaler
func (m *PDPDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias PDPDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration 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
}

// 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
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata AttributeAuthorityDescriptor"`
AttributeServices []Endpoint `xml:"AttributeService"`
AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"`
NameIDFormats []NameIDFormat `xml:"NameIDFormat"`
AttributeProfiles []string `xml:"AttributeProfile"`
Attributes []Attribute `xml:"Attribute"`
}

func (m AttributeAuthorityDescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias AttributeAuthorityDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
ValidUntil: RelaxedTime(m.ValidUntil),
CacheDuration: Duration(m.CacheDuration),
Alias: (*Alias)(&m),
}
return e.Encode(aux)
}

// UnmarshalXML implements xml.Unmarshaler
func (m *AttributeAuthorityDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias AttributeAuthorityDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration 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
}

// AffiliationDescriptor represents the SAML AffiliationDescriptor object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.5
Expand Down
5 changes: 4 additions & 1 deletion metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ func TestCanParseMetadata(t *testing.T) {
CacheDuration: time.Hour,
SPSSODescriptors: []SPSSODescriptor{
{
XMLName: xml.Name{Space: "urn:oasis:names:tc:SAML:2.0:metadata", Local: "SPSSODescriptor"},
SSODescriptor: SSODescriptor{
RoleDescriptor: RoleDescriptor{
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
ValidUntil: time.Date(2001, time.February, 3, 4, 5, 6, 789000000, time.UTC),
CacheDuration: time.Hour,
},
},
AuthnRequestsSigned: &False,
Expand Down Expand Up @@ -101,6 +102,8 @@ func TestCanProduceSPMetadata(t *testing.T) {
WantAssertionsSigned: &WantAssertionsSigned,
SSODescriptor: SSODescriptor{
RoleDescriptor: RoleDescriptor{
ValidUntil: validUntil,
CacheDuration: DefaultCacheDuration,
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
KeyDescriptors: []KeyDescriptor{
{
Expand Down
2 changes: 1 addition & 1 deletion samlidp/testdata/http_metadata_response.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2015-12-03T01:57:09Z" cacheDuration="PT48H" entityID="https://idp.example.com/metadata">
<IDPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<IDPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2015-12-03T01:57:09Z" cacheDuration="PT48H" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<KeyDescriptor use="signing">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data xmlns="http://www.w3.org/2000/09/xmldsig#">
Expand Down
2 changes: 1 addition & 1 deletion samlsp/testdata/expected_middleware_metadata.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2015-12-03T01:57:09.123Z" entityID="https://15661444.ngrok.io/saml2/metadata">
<SPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2015-12-03T01:57:09.123456789Z" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="false" WantAssertionsSigned="true">
<SPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2015-12-03T01:57:09.123Z" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="false" WantAssertionsSigned="true">
<KeyDescriptor use="encryption">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data xmlns="http://www.w3.org/2000/09/xmldsig#">
Expand Down
2 changes: 1 addition & 1 deletion samlsp/testdata/idp_metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<mdalg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<mdalg:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
</Extensions>
<IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:2.0:protocol">
<IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:2.0:protocol" cacheDuration='PT1H'>
<Extensions>
<shibmd:Scope regexp="false">testshib.org</shibmd:Scope>
<mdui:UIInfo>
Expand Down
2 changes: 1 addition & 1 deletion service_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,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},
Expand Down
24 changes: 23 additions & 1 deletion testdata/TestCanParseMetadata_metadata.xml
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
<?xml version='1.0' encoding='UTF-8'?><md:EntityDescriptor ID='_af805d1c-c2e3-444e-9cf5-efc664eeace6' entityID='https://dev.aa.kndr.org/users/auth/saml/metadata' validUntil='2001-02-03T04:05:06.789' cacheDuration='PT1H' xmlns:md='urn:oasis:names:tc:SAML:2.0:metadata' xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'><md:SPSSODescriptor AuthnRequestsSigned='false' WantAssertionsSigned='false' protocolSupportEnumeration='urn:oasis:names:tc:SAML:2.0:protocol'><md:AssertionConsumerService Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' Location='https://dev.aa.kndr.org/users/auth/saml/callback' index='0' isDefault='true'/><md:AttributeConsumingService index='1' isDefault='true'><md:ServiceName xml:lang='en'>Required attributes</md:ServiceName><md:RequestedAttribute FriendlyName='Email address' Name='email' NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic'/><md:RequestedAttribute FriendlyName='Full name' Name='name' NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic'/><md:RequestedAttribute FriendlyName='Given name' Name='first_name' NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic'/><md:RequestedAttribute FriendlyName='Family name' Name='last_name' NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic'/></md:AttributeConsumingService></md:SPSSODescriptor></md:EntityDescriptor>
<?xml version='1.0' encoding='UTF-8'?>
<md:EntityDescriptor ID='_af805d1c-c2e3-444e-9cf5-efc664eeace6'
entityID='https://dev.aa.kndr.org/users/auth/saml/metadata' validUntil='2001-02-03T04:05:06.789'
cacheDuration='PT1H' xmlns:md='urn:oasis:names:tc:SAML:2.0:metadata'
xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>
<md:SPSSODescriptor AuthnRequestsSigned='false' WantAssertionsSigned='false'
protocolSupportEnumeration='urn:oasis:names:tc:SAML:2.0:protocol' validUntil='2001-02-03T04:05:06.789'
cacheDuration='PT1H'>
<md:AssertionConsumerService Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
Location='https://dev.aa.kndr.org/users/auth/saml/callback' index='0' isDefault='true' />
<md:AttributeConsumingService index='1' isDefault='true'>
<md:ServiceName xml:lang='en'>Required attributes</md:ServiceName>
<md:RequestedAttribute FriendlyName='Email address' Name='email'
NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic' />
<md:RequestedAttribute FriendlyName='Full name' Name='name'
NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic' />
<md:RequestedAttribute FriendlyName='Given name' Name='first_name'
NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic' />
<md:RequestedAttribute FriendlyName='Family name' Name='last_name'
NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic' />
</md:AttributeConsumingService>
</md:SPSSODescriptor>
</md:EntityDescriptor>
2 changes: 1 addition & 1 deletion testdata/TestCanProduceSPMetadata_expected
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2013-03-10T00:32:19.104Z" cacheDuration="PT1H" entityID="http://localhost:5000/e087a985171710fb9fb30f30f41384f9/saml2/metadata/">
<SPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="true" WantAssertionsSigned="true">
<SPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2013-03-10T00:32:19.104Z" cacheDuration="PT24H" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="true" WantAssertionsSigned="true">
<KeyDescriptor use="encryption">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data xmlns="http://www.w3.org/2000/09/xmldsig#">
Expand Down
Loading
Loading