func TestParseResponse(t *testing.T) { // we'll need to change this function later, so keep a copy of it for the reset newTokenCopy := newToken Convey("Given a WS-Fed response message", t, func() { config := Config{} config.MetadataURL = "https://signin.blackbaud.com/wsfederation/metadata" config.MetadataCertsAreTrusted = true config.Realm = "http://account.blackbaud.com" sso := New(&config) Convey("When ParseResponse is called", func() { // change this function after signature validation, so that we can pass // the expiration check newToken = func(wresult string, realm string) (Token, error) { doc := etree.NewDocument() err := doc.ReadFromString(wresult) if err != nil { return nil, err } conditions := doc.FindElement("//Conditions") notOnOrAfterVal := conditions.SelectAttr("NotOnOrAfter") notOnOrAfterVal.Value = "3015-09-26T16:28:56.681Z" return &SAMLv11{XMLDoc: doc, Realm: realm}, nil } claims, err := sso.ParseResponse(wresult) Convey("Then a Claim is returned without error", func() { So(err, ShouldBeNil) So(claims.Subject.ID, ShouldEqual, "6ef81ad2-99f8-4c42-996c-def1d98db711") }) }) Convey("When ParseResponse is called with the wrong audience/realm combo", func() { // change this function after signature validation, so that we can pass // the expiration check newToken = func(wresult string, realm string) (Token, error) { doc := etree.NewDocument() err := doc.ReadFromString(wresult) if err != nil { return nil, err } conditions := doc.FindElement("//Conditions") notOnOrAfterVal := conditions.SelectAttr("NotOnOrAfter") notOnOrAfterVal.Value = "3015-09-26T16:28:56.681Z" return &SAMLv11{XMLDoc: doc, Realm: "http://wrongaudience.com"}, nil } _, err := sso.ParseResponse(wresult) Convey("Then an error should occur", func() { So(err, ShouldNotBeNil) So(err.Error(), ShouldEqual, "go-wsfed: Audience check failed") }) }) Convey("When ParseResponse is called with an old response", func() { _, err := sso.ParseResponse(wresult) Convey("Then an error should occur", func() { So(err, ShouldNotBeNil) So(err.Error(), ShouldEqual, "go-wsfed: Expiration check failed") }) }) Convey("When ParseResponse is called with an invalid signature response", func() { wresultModified := `<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512"><trust:RequestSecurityTokenResponse><trust:Lifetime><wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2015-09-26T15:28:56.681Z</wsu:Created><wsu:Expires xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2015-09-26T16:28:56.681Z</wsu:Expires></trust:Lifetime><wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"><wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing"><wsa:Address>http://account.blackbaud.com/</wsa:Address></wsa:EndpointReference></wsp:AppliesTo><trust:RequestedSecurityToken><saml:Assertion MajorVersion="1" MinorVersion="1" AssertionID="_79b4fd7d-e0cd-406e-86a7-19ca73d360f8" Issuer="Blackbaud Authentication Service" IssueInstant="2015-09-26T15:28:56.681Z" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"><saml:Conditions NotBefore="2015-09-26T15:28:56.681Z" NotOnOrAfter="2015-09-26T16:28:56.681Z"><saml:AudienceRestrictionCondition><saml:Audience>http://account.blackbaud.com/</saml:Audience></saml:AudienceRestrictionCondition></saml:Conditions><saml:AttributeStatement><saml:Subject></saml:Subject></saml:AttributeStatement><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /><Reference URI="#_79b4fd7d-e0cd-406e-86a7-19ca73d360f8"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /><DigestValue>pC1Oa1rmYaYBxVW6bZJnUm7aIFHoGa1a6z0MIKVcWoc=</DigestValue></Reference></SignedInfo><SignatureValue>gQObr3Indcv1709DpW3AZUhZevHnYWGzpB6edlGh4tHpViiu6h8f1uATeIvqMxi/bttcrfQk+Rls3K8GmE7BgAXBuewKR2chObRB+CFPvnteLQOVm5DQGY4C8mEqRTJvL4LLBajw03YulFvg95WLHIzqjguTmKf4gYx++uAS7n9zuAHrQ5XF/5B1ae6PER+dJsc2vNLdxrOuJNPYJZ0L5PJhF8bmAMHcwFYd3JfgDH8dJ0rxpfXr4MVinx94Alo9U2lzWuHu/yMQ8pr8qsNllDVq0yP5J7epSq5iO6RbkN2FQjFdsjmuNstteOn/vs8xc00lhDWvttnDH65RPUtgDA==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIDFzCCAgOgAwIBAgIQYyFH4hyHqIZJFSvhaTufDDAJBgUrDgMCHQUAMCAxHjAcBgNVBAMTFVRlc3RCQkF1dGhTaWduaW5nQ2VydDAeFw0xMzExMDUxNjUzNTlaFw0zOTEyMzEyMzU5NTlaMCAxHjAcBgNVBAMTFVRlc3RCQkF1dGhTaWduaW5nQ2VydDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMyjnT+N3cypXtoTfr8HTNXcThqPKSQyioqIt0A7ZEQjByErcFdx5LyPSxyUWNg9r9Ay3T/qUqmCD4roK/LruT8W+02SLgZLGJ4cecuQqOryABVn3SeTCuViLVRFkAbGVWXqEI2woFq226xCyKEbYMIr0Lxisuo8nEQ0XSAYh2tiT6XXMf2aHomBxxUk9tkfyjBqim+OBZxglKQ+jQgA35CDi6NezJaSE13oNuo7iMlOyYV3+jPh/tohYKxhAod2biMa5oKDmVc3C6zvKZyoSVMQE0jRs2SzZW/TzBXkHfBAtoSRCarWtTv+ode+XYcQw0Hi5p5FKYmKx/sdx4RvLG0CAwEAAaNVMFMwUQYDVR0BBEowSIAQZY/UuhZiXYqsNv1VH1ec3aEiMCAxHjAcBgNVBAMTFVRlc3RCQkF1dGhTaWduaW5nQ2VydIIQYyFH4hyHqIZJFSvhaTufDDAJBgUrDgMCHQUAA4IBAQBokHC1xp2I4+K7SzGQiXehlLcjDX4+9wWX+8ZzByOKyTIfc+3DthoU1aWiuG1ioFyL8ttRLm10n3PSXR7hJtXY4JnyxfolZy+c6+n3AsYnstaZipZgnCxJ2+P1e+MzbOoMFuBceg+vpW0dJex2MrJ5h/khwFNVvhoPnGT8W7j6Q+Lw6VeexbbLBNPtpmHlrK5/7RjjZdZTvFbEMqBz1hl4Ny1Gz+mLq4fsNxC4eoW5kq/MyVbigX8kwOonr4dh68OSOLoYJ9ml62wE0uhiamM89zaFeui6e/R/xAqsTlnl10qDBDVKcGHyIrmgBUkHYknQxCnHoFW/N+w8KmhryAko</X509Certificate></X509Data></KeyInfo></Signature></saml:Assertion></trust:RequestedSecurityToken><trust:RequestedAttachedReference><o:SecurityTokenReference k:TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" xmlns:k="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><o:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID">_79b4fd7d-e0cd-406e-86a7-19ca73d360f8</o:KeyIdentifier></o:SecurityTokenReference></trust:RequestedAttachedReference><trust:RequestedUnattachedReference><o:SecurityTokenReference k:TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" xmlns:k="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><o:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID">_79b4fd7d-e0cd-406e-86a7-19ca73d360f8</o:KeyIdentifier></o:SecurityTokenReference></trust:RequestedUnattachedReference><trust:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</trust:TokenType><trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType><trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType></trust:RequestSecurityTokenResponse></trust:RequestSecurityTokenResponseCollection>` _, err := sso.ParseResponse(wresultModified) Convey("Then an error should occur", func() { So(err, ShouldNotBeNil) So(err.Error(), ShouldStartWith, "signedxml: ") }) }) Convey("When ParseResponse is called with an unsupported Token", func() { newToken = func(wresult string, realm string) (Token, error) { return nil, errors.New("fake token error") } _, err := sso.ParseResponse(wresult) Convey("Then an error should occur", func() { So(err, ShouldNotBeNil) So(err.Error(), ShouldEqual, "fake token error") }) }) Reset(func() { newToken = newTokenCopy }) }) }
import ( "errors" "github.com/ma314smith/etree" ) // Token abstracts the different assertions available for WS-Fed type Token interface { Validate() error GetClaims() (Claims, error) } // newToken pareses the assertion from a wresult string and returns the // corresponding token type var newToken = func(wresult string, realm string) (Token, error) { doc := etree.NewDocument() err := doc.ReadFromString(wresult) if err != nil { return nil, err } assertion := doc.FindElement("//Assertion") if assertion == nil { return nil, errors.New("go-wsfed: unable to find Assertion element") } majorVersion := assertion.SelectAttrValue("MajorVersion", "") minorVersion := assertion.SelectAttrValue("MinorVersion", "") switch { case majorVersion == "1" && minorVersion == "1":