packages feed

wai-saml2 0.4 → 0.5

raw patch · 11 files changed

+349/−11 lines, 11 filesdep ~bytestringdep ~textdep ~zlib

Dependency ranges changed: bytestring, text, zlib

Files

CHANGELOG.md view
@@ -1,5 +1,13 @@ # Changelog for `wai-saml2` +## Unreleased changes++## 0.5++-   Support GHC 9.6 ([#53](https://github.com/mbg/wai-saml2/pull/53) by [@mbg](https://github.com/mbg))+-   Fixed a bug in XML canonicalisation causing a digest mismatch on Okta when assertion attributes are present (special thanks to @hiroqn) ([#51](https://github.com/mbg/wai-saml2/pull/51) by [@fumieval](https://github.com/fumieval))+-   Added `authnRequestDestination` field to `AuthnRequest` ([#47](https://github.com/mbg/wai-saml2/pull/47) by [@Philonous](https://github.com/Philonous))+ ## 0.4  -   Split `validateResponse` into `decodeResponse` and `validateSAMLResponse` ([#31](https://github.com/mbg/wai-saml2/pull/31) by [@fumieval](https://github.com/fumieval))
src/Network/Wai/SAML2/C14N.hs view
@@ -14,6 +14,8 @@ --------------------------------------------------------------------------------  import qualified Data.ByteString as BS+import Data.Text (Text)+import qualified Data.Text.Encoding as T  import Foreign.C.Types @@ -21,9 +23,10 @@  -------------------------------------------------------------------------------- --- | 'canonicalise' @xml@ produces a canonical representation of @xml@.-canonicalise :: BS.ByteString -> IO BS.ByteString-canonicalise xml = c14n c14nOpts c14n_exclusive_1_0 [] False Nothing xml+-- | 'canonicalise' @prefixList@ @xml@ produces a canonical representation of @xml@+-- while retaining namespaces matching @prefixList@.+canonicalise :: [Text] -> BS.ByteString -> IO BS.ByteString+canonicalise prefixList xml = c14n c14nOpts c14n_exclusive_1_0 (map T.encodeUtf8 prefixList) False Nothing xml  -- | The options we want to use for canonicalisation of XML documents. c14nOpts :: [CInt]
src/Network/Wai/SAML2/Request.hs view
@@ -63,6 +63,11 @@     ,   authnRequestID :: !T.Text         -- | SP Entity ID     ,   authnRequestIssuer :: !T.Text+        -- | The URI reference to which this request is to be sent. Required+        -- for signed requests+        --+        -- @since 0.5+    ,   authnRequestDestination :: !(Maybe T.Text)         -- | Allow IdP to generate a new identifier     ,   authnRequestAllowCreate :: !Bool         -- | The URI reference corresponding to a name identifier format@@ -83,6 +88,7 @@     pure AuthnRequest{         authnRequestAllowCreate = True     ,   authnRequestNameIDFormat = Transient+    ,   authnRequestDestination = Nothing     ,   ..     } @@ -118,13 +124,15 @@         root = Element             (saml2pName "AuthnRequest")             (Map.fromList-                [ ("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol")+               ([ ("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol")                 , ("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion")                 , ("ID", authnRequestID) -- Reference [RequestAbstractType] and see [ID Values]                 , ("Version", "2.0")  -- [RequestAbstractType]                 , ("IssueInstant", timestamp) -- [RequestAbstractType]                 , ("AssertionConsumerServiceIndex", "1") -- [AuthnRequest]-                ])+                ]+                -- [RequestAbstractType]+                ++ [("Destination", uri) | let Just uri = authnRequestDestination] ))             [NodeElement issuer, NodeElement nameIdPolicy]         -- Reference [RequestAbstractType]         issuer = Element
src/Network/Wai/SAML2/Response.hs view
@@ -11,6 +11,7 @@     Response(..),     removeSignature,     extractSignedInfo,+    extractPrefixList,      -- * Re-exports     module Network.Wai.SAML2.StatusCode,@@ -132,6 +133,18 @@                            &/ element (dsName "SignedInfo")                           ) >>= nodes     pure signedInfo++-- | Obtain a list of InclusiveNamespaces entries used for exclusive XML canonicalisation.+--+-- @since 0.5+extractPrefixList :: Cursor -> [T.Text]+extractPrefixList cursor = concatMap T.words+    $ concatMap (attribute "PrefixList")+    $ cursor+    $/ element (dsName "Reference")+    &/ element (dsName "Transforms")+    &/ element (dsName "Transform")+    &/ element (ecName "InclusiveNamespaces")  -------------------------------------------------------------------------------- 
src/Network/Wai/SAML2/Validation.hs view
@@ -16,7 +16,9 @@ --------------------------------------------------------------------------------  import Control.Exception+import Control.Monad (forM_, when, unless) import Control.Monad.Except+import Control.Monad.IO.Class (liftIO)  import Crypto.Error import Crypto.Hash@@ -69,7 +71,7 @@      -- try to parse the XML document; throw an exception if it is not     -- a valid XML document-    responseXmlDoc <- case XML.parseLBS def (LBS.fromStrict resXmlDocData) of+    responseXmlDoc <- case XML.parseLBS parseSettings (LBS.fromStrict resXmlDocData) of         Left err -> throwError $ InvalidResponseXml err         Right responseXmlDoc -> pure responseXmlDoc @@ -130,8 +132,9 @@     let signedInfoXml = XML.renderLBS def doc      -- canonicalise the textual representation of the SignedInfo element+    let prefixList = extractPrefixList (XML.fromDocument doc)     signedInfoCanonResult <- liftIO $ try $-        canonicalise (LBS.toStrict signedInfoXml)+        canonicalise prefixList (LBS.toStrict signedInfoXml)      normalisedSignedInfo <- case signedInfoCanonResult of         Left err -> throwError $ CanonicalisationFailure err@@ -161,7 +164,7 @@      -- then render the resulting document and canonicalise it     let renderedXml = XML.renderLBS def docMinusSignature-    refCanonResult <- liftIO $ try $ canonicalise (LBS.toStrict renderedXml)+    refCanonResult <- liftIO $ try $ canonicalise prefixList (LBS.toStrict renderedXml)      normalised <- case refCanonResult of         Left err -> throwError $ CanonicalisationFailure err
src/Network/Wai/SAML2/XML.hs view
@@ -13,6 +13,7 @@     xencName,     dsName,     mdName,+    ecName,      -- * Utility functions     toMaybeText,@@ -21,7 +22,8 @@      -- * XML parsing     FromXML(..),-    oneOrFail+    oneOrFail,+    parseSettings ) where  --------------------------------------------------------------------------------@@ -66,6 +68,14 @@     Name name (Just "urn:oasis:names:tc:SAML:2.0:metadata") (Just "md")  +-- | 'ecName' @name@ constructs a 'Name' for @name@ in the+-- http://www.w3.org/2001/10/xml-exc-c14n# namespace.+--+-- @since 0.5+ecName :: T.Text -> Name+ecName name =+    Name name (Just "http://www.w3.org/2001/10/xml-exc-c14n#") (Just "ec")+ -- | 'toMaybeText' @xs@ returns 'Nothing' if @xs@ is the empty list, or -- the result of concatenating @xs@ wrapped in 'Just' otherwise. toMaybeText :: [T.Text] -> Maybe T.Text@@ -100,3 +110,10 @@ oneOrFail _ (x:_) = pure x  --------------------------------------------------------------------------------++-- | It is important to retain namespaces in order to calculate the hash of the canonicalised XML correctly.+-- see: https://stackoverflow.com/questions/69252831/saml-2-0-digest-value-calculation-in-saml-assertion+--+-- @since 0.5+parseSettings :: ParseSettings+parseSettings = def { psRetainNamespaces = True }
+ tests/data/metadata/google.xml view
@@ -0,0 +1,29 @@+<?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://accounts.google.com/o/saml2?idpid=C01aa60hc" validUntil="2027-07-20T09:18:00.000Z">+  <md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">+    <md:KeyDescriptor use="signing">+      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">+        <ds:X509Data>+          <ds:X509Certificate>MIIDdDCCAlygAwIBAgIGAYIgDLo9MA0GCSqGSIb3DQEBCwUAMHsxFDASBgNVBAoTC0dvb2dsZSBJ+bmMuMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MQ8wDQYDVQQDEwZHb29nbGUxGDAWBgNVBAsTD0dv+b2dsZSBGb3IgV29yazELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWEwHhcNMjIwNzIx+MDkxODAwWhcNMjcwNzIwMDkxODAwWjB7MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEWMBQGA1UEBxMN+TW91bnRhaW4gVmlldzEPMA0GA1UEAxMGR29vZ2xlMRgwFgYDVQQLEw9Hb29nbGUgRm9yIFdvcmsx+CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A+MIIBCgKCAQEA9KQNdYuN7oPUsJ1SK2L58egBELYIWr5cRd9PXuV1k1s2hzt2vMoPS1l788+/hVb4+CtZCeabZDFYfNMbS2olbhG06FrS9+ptm2pKucXqEp+KM9FozmK/qn7+geQl7uHdcT0xRqfVK46Wr+RB4S8VdbK9WIK2prCvHIPu1O9eH+m9g+G1Y116RIqKKGfGbsWdhyNY47+LcxXzThgdfi4BPwcB/i+K/X/i4qNje4FLHrAJQ7S6bRiTXPyv7q9Md96R1T9hXdFA9siW9rmrJCg/H022TrqxwctlIkQjPGj+yskF3vCcwuIj5M8PuP37k6AuiBZdV73gG8t8SgoMHhElWSLKowIDAQABMA0GCSqGSIb3DQEBCwUA+A4IBAQDuIthaqITon7u3aZg70NutC54EsQ6NLfWsbwgadOahb5EtGnBkKB7bairnTSAMtH4wLnNj+BrgxswnxVd5DdrmqpuoY+Pn3hAXYBCFwmAzEEYwmfkZbTVa7AcT6bw6nOqHrfnYump9LhUwxI4dV+4Yr2YkLL767CeSMLv58GBlFAqxP63HVMa1BJAhZhjamqvSYQCJZYnBG7Nuy2ek6Y5kHQM/8L0h6n+gx1Jv0nPvPI+9NlWRkUhDN/jQkzxKcYnCNlT/bi3xY+QjFWD4z/Qx2h9hrxZtLRtcrp6WqUmpXha+dq1JsmSbXwRj4Tu7IVZ4tYqeY/mOJGYf8fr5h/Q8GRCd</ds:X509Certificate>+        </ds:X509Data>+      </ds:KeyInfo>+    </md:KeyDescriptor>+    <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>+    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://accounts.google.com/o/saml2/idp?idpid=C01aa60hc"/>+    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://accounts.google.com/o/saml2/idp?idpid=C01aa60hc"/>+  </md:IDPSSODescriptor>+</md:EntityDescriptor>
+ tests/data/metadata/google.xml.expected view
@@ -0,0 +1,139 @@+IDPSSODescriptor+  { entityID = "https://accounts.google.com/o/saml2?idpid=C01aa60hc"+  , x509Certificate =+      SignedExact+        { getSigned =+            Signed+              { signedObject =+                  Certificate+                    { certVersion = 2+                    , certSerial = 1658395081277+                    , certSignatureAlg = SignatureALG HashSHA256 PubKeyALG_RSA+                    , certIssuerDN =+                        DistinguishedName+                          { getDistinguishedElements =+                              [ ( [ 2 , 5 , 4 , 10 ]+                                , ASN1CharacterString+                                    { characterEncoding = Printable+                                    , getCharacterStringRawData = "Google Inc."+                                    }+                                )+                              , ( [ 2 , 5 , 4 , 7 ]+                                , ASN1CharacterString+                                    { characterEncoding = Printable+                                    , getCharacterStringRawData = "Mountain View"+                                    }+                                )+                              , ( [ 2 , 5 , 4 , 3 ]+                                , ASN1CharacterString+                                    { characterEncoding = Printable+                                    , getCharacterStringRawData = "Google"+                                    }+                                )+                              , ( [ 2 , 5 , 4 , 11 ]+                                , ASN1CharacterString+                                    { characterEncoding = Printable+                                    , getCharacterStringRawData = "Google For Work"+                                    }+                                )+                              , ( [ 2 , 5 , 4 , 6 ]+                                , ASN1CharacterString+                                    { characterEncoding = Printable+                                    , getCharacterStringRawData = "US"+                                    }+                                )+                              , ( [ 2 , 5 , 4 , 8 ]+                                , ASN1CharacterString+                                    { characterEncoding = Printable+                                    , getCharacterStringRawData = "California"+                                    }+                                )+                              ]+                          }+                    , certValidity =+                        ( DateTime+                            { dtDate =+                                Date { dateYear = 2022 , dateMonth = July , dateDay = 21 }+                            , dtTime =+                                TimeOfDay+                                  { todHour = 9 h , todMin = 18 m , todSec = 0 s , todNSec = 0 ns }+                            }+                        , DateTime+                            { dtDate =+                                Date { dateYear = 2027 , dateMonth = July , dateDay = 20 }+                            , dtTime =+                                TimeOfDay+                                  { todHour = 9 h , todMin = 18 m , todSec = 0 s , todNSec = 0 ns }+                            }+                        )+                    , certSubjectDN =+                        DistinguishedName+                          { getDistinguishedElements =+                              [ ( [ 2 , 5 , 4 , 10 ]+                                , ASN1CharacterString+                                    { characterEncoding = Printable+                                    , getCharacterStringRawData = "Google Inc."+                                    }+                                )+                              , ( [ 2 , 5 , 4 , 7 ]+                                , ASN1CharacterString+                                    { characterEncoding = Printable+                                    , getCharacterStringRawData = "Mountain View"+                                    }+                                )+                              , ( [ 2 , 5 , 4 , 3 ]+                                , ASN1CharacterString+                                    { characterEncoding = Printable+                                    , getCharacterStringRawData = "Google"+                                    }+                                )+                              , ( [ 2 , 5 , 4 , 11 ]+                                , ASN1CharacterString+                                    { characterEncoding = Printable+                                    , getCharacterStringRawData = "Google For Work"+                                    }+                                )+                              , ( [ 2 , 5 , 4 , 6 ]+                                , ASN1CharacterString+                                    { characterEncoding = Printable+                                    , getCharacterStringRawData = "US"+                                    }+                                )+                              , ( [ 2 , 5 , 4 , 8 ]+                                , ASN1CharacterString+                                    { characterEncoding = Printable+                                    , getCharacterStringRawData = "California"+                                    }+                                )+                              ]+                          }+                    , certPubKey =+                        PubKeyRSA+                          PublicKey+                            { public_size = 256+                            , public_n =+                                30883043751460212091009024715988672157201981026347726370499687818828606623610036063558065564910721575436562697595833895389078194628597753679300253553943785088191301337694287320462812204176082900979493095532403911400962461718281343090255859587662209446117641661982570706673532506066365285856542563187402675519172527708567282278848705855498817485769832020293045021199187525818891914876547302505893509597998904610940749090045911784327818251262543295743496580502018384973717126996683932142133838721711519234780192895538524052074852409104614561380839223725538723547399956034683375536115322508017066877882514789596315962019+                            , public_e = 65537+                            }+                    , certExtensions = Extensions Nothing+                    }+              , signedAlg = SignatureALG HashSHA256 PubKeyALG_RSA+              , signedSignature =+                  "\238\"\216Z\168\132\232\159\187\183i\152;\208\219\173\v\158\EOT\177\SO\141-\245\172o\b\SUBt\230\161o\145-\SUBpd(\RS\219j*\231M \f\180~0.sc\ACK\184\&1\179\t\241U\222Cv\185\170\166\234\CAN\248\249\247\132\ENQ\216\EOT!p\152\f\196\DC1\140&~F[MV\187\SOH\196\250o\SO\167:\161\235~v.\154\159K\133L1#\135U\225\138\246bB\203\239\174\194y#\v\191\159\ACK\ACKQ@\171\DC3\250\220uLkPI\STX\SYNa\141\169\170\189&\DLE\b\150X\156\DC1\187\&6\236\182zN\152\230A\208\&3\255\v\210\RS\167\131\GSI\191I\207\188\242>\244\217VFE!\f\223\227BL\241)\198'\b\217S\253\184\183\197\143\144\140U\131\227?\208\199h}\134\188Y\180\180mr\186zZ\165&\165xZv\173I\178d\155_\EOTc\225;\187!Vx\181\138\158c\249\142$f\US\241\250\249\135\244<\EM\DLE\157"+              }+        , exactObjectRaw =+            "0\130\STX\\\160\ETX\STX\SOH\STX\STX\ACK\SOH\130 \f\186=0\r\ACK\t*\134H\134\247\r\SOH\SOH\v\ENQ\NUL0{1\DC40\DC2\ACK\ETXU\EOT\n\DC3\vGoogle Inc.1\SYN0\DC4\ACK\ETXU\EOT\a\DC3\rMountain View1\SI0\r\ACK\ETXU\EOT\ETX\DC3\ACKGoogle1\CAN0\SYN\ACK\ETXU\EOT\v\DC3\SIGoogle For Work1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\DC30\DC1\ACK\ETXU\EOT\b\DC3\nCalifornia0\RS\ETB\r220721091800Z\ETB\r270720091800Z0{1\DC40\DC2\ACK\ETXU\EOT\n\DC3\vGoogle Inc.1\SYN0\DC4\ACK\ETXU\EOT\a\DC3\rMountain View1\SI0\r\ACK\ETXU\EOT\ETX\DC3\ACKGoogle1\CAN0\SYN\ACK\ETXU\EOT\v\DC3\SIGoogle For Work1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\DC30\DC1\ACK\ETXU\EOT\b\DC3\nCalifornia0\130\SOH\"0\r\ACK\t*\134H\134\247\r\SOH\SOH\SOH\ENQ\NUL\ETX\130\SOH\SI\NUL0\130\SOH\n\STX\130\SOH\SOH\NUL\244\164\ru\139\141\238\131\212\176\157R+b\249\241\232\SOH\DLE\182\bZ\190\\E\223O^\229u\147[6\135;v\188\202\SIKY{\243\207\191\133V\248\n\214By\166\217\fV\US4\198\210\218\137[\132m:\SYN\180\189\250\155f\218\146\174qz\132\167\226\140\244Z3\152\175\234\159\191\160y\t{\184w\\OLQ\169\245J\227\165\171D\RS\DC2\241W[+\213\136+jk\n\241\200>\237N\245\225\254\155\216>\ESCV5\215\164H\168\162\134|f\236Y\216r5\142;\248\183\&1_4\225\129\215\226\224\DC3\240p\US\226+\245\255\139\138\141\141\238\ENQ,z\192%\SO\210\233\180bMs\242\191\186\189\&1\223zGT\253\133wE\ETX\219\"[\218\230\172\144\160\252}6\217:\234\199\a-\148\137\DLE\140\241\163\202\201\ENQ\222\240\156\194\226#\228\207\SI\184\253\251\147\160.\136\SYN]W\189\224\ESC\203|J\n\f\RS\DC1%Y\"\202\163\STX\ETX\SOH\NUL\SOH"+        , encodeSignedObject =+            "0\130\ETXt0\130\STX\\\160\ETX\STX\SOH\STX\STX\ACK\SOH\130 \f\186=0\r\ACK\t*\134H\134\247\r\SOH\SOH\v\ENQ\NUL0{1\DC40\DC2\ACK\ETXU\EOT\n\DC3\vGoogle Inc.1\SYN0\DC4\ACK\ETXU\EOT\a\DC3\rMountain View1\SI0\r\ACK\ETXU\EOT\ETX\DC3\ACKGoogle1\CAN0\SYN\ACK\ETXU\EOT\v\DC3\SIGoogle For Work1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\DC30\DC1\ACK\ETXU\EOT\b\DC3\nCalifornia0\RS\ETB\r220721091800Z\ETB\r270720091800Z0{1\DC40\DC2\ACK\ETXU\EOT\n\DC3\vGoogle Inc.1\SYN0\DC4\ACK\ETXU\EOT\a\DC3\rMountain View1\SI0\r\ACK\ETXU\EOT\ETX\DC3\ACKGoogle1\CAN0\SYN\ACK\ETXU\EOT\v\DC3\SIGoogle For Work1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\DC30\DC1\ACK\ETXU\EOT\b\DC3\nCalifornia0\130\SOH\"0\r\ACK\t*\134H\134\247\r\SOH\SOH\SOH\ENQ\NUL\ETX\130\SOH\SI\NUL0\130\SOH\n\STX\130\SOH\SOH\NUL\244\164\ru\139\141\238\131\212\176\157R+b\249\241\232\SOH\DLE\182\bZ\190\\E\223O^\229u\147[6\135;v\188\202\SIKY{\243\207\191\133V\248\n\214By\166\217\fV\US4\198\210\218\137[\132m:\SYN\180\189\250\155f\218\146\174qz\132\167\226\140\244Z3\152\175\234\159\191\160y\t{\184w\\OLQ\169\245J\227\165\171D\RS\DC2\241W[+\213\136+jk\n\241\200>\237N\245\225\254\155\216>\ESCV5\215\164H\168\162\134|f\236Y\216r5\142;\248\183\&1_4\225\129\215\226\224\DC3\240p\US\226+\245\255\139\138\141\141\238\ENQ,z\192%\SO\210\233\180bMs\242\191\186\189\&1\223zGT\253\133wE\ETX\219\"[\218\230\172\144\160\252}6\217:\234\199\a-\148\137\DLE\140\241\163\202\201\ENQ\222\240\156\194\226#\228\207\SI\184\253\251\147\160.\136\SYN]W\189\224\ESC\203|J\n\f\RS\DC1%Y\"\202\163\STX\ETX\SOH\NUL\SOH0\r\ACK\t*\134H\134\247\r\SOH\SOH\v\ENQ\NUL\ETX\130\SOH\SOH\NUL\238\"\216Z\168\132\232\159\187\183i\152;\208\219\173\v\158\EOT\177\SO\141-\245\172o\b\SUBt\230\161o\145-\SUBpd(\RS\219j*\231M \f\180~0.sc\ACK\184\&1\179\t\241U\222Cv\185\170\166\234\CAN\248\249\247\132\ENQ\216\EOT!p\152\f\196\DC1\140&~F[MV\187\SOH\196\250o\SO\167:\161\235~v.\154\159K\133L1#\135U\225\138\246bB\203\239\174\194y#\v\191\159\ACK\ACKQ@\171\DC3\250\220uLkPI\STX\SYNa\141\169\170\189&\DLE\b\150X\156\DC1\187\&6\236\182zN\152\230A\208\&3\255\v\210\RS\167\131\GSI\191I\207\188\242>\244\217VFE!\f\223\227BL\241)\198'\b\217S\253\184\183\197\143\144\140U\131\227?\208\199h}\134\188Y\180\180mr\186zZ\165&\165xZv\173I\178d\155_\EOTc\225;\187!Vx\181\138\158c\249\142$f\US\241\250\249\135\244<\EM\DLE\157"+        }+  , nameIDFormats =+      [ "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" ]+  , singleSignOnServices =+      [ ( HTTPRedirect+        , "https://accounts.google.com/o/saml2/idp?idpid=C01aa60hc"+        )+      , ( HTTPPost+        , "https://accounts.google.com/o/saml2/idp?idpid=C01aa60hc"+        )+      ]+  }
+ tests/data/metadata/keycloak.xml view
@@ -0,0 +1,25 @@+<?xml version="1.0" encoding="UTF-8"?>+<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" entityID="https://sso.ja-sore.de/auth/realms/HERP">+   <md:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">+      <md:KeyDescriptor use="signing">+         <ds:KeyInfo>+            <ds:KeyName>AViu9Cf7_rjRHwqkm_MgvMcjWHuRPSP0mMNNEjHslaY</ds:KeyName>+            <ds:X509Data>+               <ds:X509Certificate>MIIClzCCAX8CBgF3TK79BzANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARIRVJQMB4XDTIxMDEyOTA1NDYxMloXDTMxMDEyOTA1NDc1MlowDzENMAsGA1UEAwwESEVSUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANRBHQMqF2+g7CrESyzrv+HvwjvIdhdZWWc6z4GiD3zN1TE+cbh88JMUrVTWd7EJLq+9uAazCyZfzX5BdauDqsp+LP9z2xiJ6Zlao1z5ycfG4dwvEIlOv4rX1zSdtg35Cz+QdaUfIkCEX3dTHI7dFcwou+Nel0BGCnp17w8gVPOBGYzvOtBgWcNV9+9Zmn7oSRoG5R4uRzhmPv12pyKRO/0hZr6gTkQ11CsnyfW890bDM05I5m8gDT0fAO2PznbQIQKClqUoyNbJZbBeDyV7QaEkkC8Nckvo6y+bae8oEF9pxQiXBV7dHtY8VLWq5SN2IeX1VK59hMg2NdHUxjMZIiECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAzGn04cz8oaZoRUCo6OKhcwfOSrjojfqbUxW8ZAY6UPUV8IfAbaxMlc4j1NVq3awi+yCJWLDaZyOX4mQyD+NoHB5D1/qQi/wLBkg9TJt2wp6MjkOPrwkN8lvhabq8Us2NiBnhHzLjWawltie9r1W0pugRG4qSZvJqosJJh9Wr0m8MnaP28P7fp4J/j3UAivlGZvXXh1kcl/lRWhoYqmR3r2j98OWy/WdYfx/oPNDWMdJDdR9I/qtlOVHsiEDP8Z478/z9IYweOYYqg7Ou77Ys/5G6wRxRCwaUjMDPdFfyvfUBZ9EFpYUV1G/zXuGQwfuh27GWz8gytdd1vDxBU06h3w==</ds:X509Certificate>+            </ds:X509Data>+         </ds:KeyInfo>+      </md:KeyDescriptor>+      <md:ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml/resolve" index="0" />+      <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+      <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+      <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+      <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>+      <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>+      <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>+      <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>+      <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+      <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+      <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+      <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+   </md:IDPSSODescriptor>+</md:EntityDescriptor>
+ tests/data/metadata/keycloak.xml.expected view
@@ -0,0 +1,89 @@+IDPSSODescriptor+  { entityID = "https://sso.ja-sore.de/auth/realms/HERP"+  , x509Certificate =+      SignedExact+        { getSigned =+            Signed+              { signedObject =+                  Certificate+                    { certVersion = 0+                    , certSerial = 1611899272455+                    , certSignatureAlg = SignatureALG HashSHA256 PubKeyALG_RSA+                    , certIssuerDN =+                        DistinguishedName+                          { getDistinguishedElements =+                              [ ( [ 2 , 5 , 4 , 3 ]+                                , ASN1CharacterString+                                    { characterEncoding = UTF8+                                    , getCharacterStringRawData = "HERP"+                                    }+                                )+                              ]+                          }+                    , certValidity =+                        ( DateTime+                            { dtDate =+                                Date { dateYear = 2021 , dateMonth = January , dateDay = 29 }+                            , dtTime =+                                TimeOfDay+                                  { todHour = 5 h , todMin = 46 m , todSec = 12 s , todNSec = 0 ns }+                            }+                        , DateTime+                            { dtDate =+                                Date { dateYear = 2031 , dateMonth = January , dateDay = 29 }+                            , dtTime =+                                TimeOfDay+                                  { todHour = 5 h , todMin = 47 m , todSec = 52 s , todNSec = 0 ns }+                            }+                        )+                    , certSubjectDN =+                        DistinguishedName+                          { getDistinguishedElements =+                              [ ( [ 2 , 5 , 4 , 3 ]+                                , ASN1CharacterString+                                    { characterEncoding = UTF8+                                    , getCharacterStringRawData = "HERP"+                                    }+                                )+                              ]+                          }+                    , certPubKey =+                        PubKeyRSA+                          PublicKey+                            { public_size = 256+                            , public_n =+                                26794629232301390559329251866062548351573676959105948670047115599484903764153173211872085488745203643849524181464124430648664560428395941550339690556064181777927415101879096439027796505139145202723504898654935482393722978364774771646139306434335595822802292206982564879345724045673514452796802353368762193429772204243864488315862304970769841689431483551389268024017379114959044330977741690398490550580701831871711628065022359440646669330356587937408829251700791982308190330679272353052846282673680829331107723412188694305482396334605009242290197499924909025290277992294341810791131897406148044363907966374411188642337+                            , public_e = 65537+                            }+                    , certExtensions = Extensions Nothing+                    }+              , signedAlg = SignatureALG HashSHA256 PubKeyALG_RSA+              , signedSignature =+                  "\204i\244\225\204\252\161\166hE@\168\232\226\161s\a\206J\184\232\141\250\155S\NAK\188d\ACK:P\245\NAK\240\135\192m\172L\149\206#\212\213j\221\172\"\251 \137X\176\218g#\151\226d2\SI\227h\FS\RSC\215\250\144\139\252\v\ACKH=L\155v\194\158\140\142C\143\175\t\r\242[\225i\186\188R\205\141\136\EM\225\US2\227Y\172%\182'\189\175U\180\166\232\DC1\ESC\138\146f\242j\162\194I\135\213\171\210o\f\157\163\246\240\254\223\167\130\DEL\143u\NUL\138\249Ff\245\215\135Y\FS\151\249QZ\SUB\CAN\170dw\175h\253\240\229\178\253gX\DEL\US\232<\208\214\&1\210Cu\USH\254\171e9Q\236\136@\207\241\158;\243\252\253!\140\RS9\134*\131\179\174\239\182,\255\145\186\193\FSQ\v\ACK\148\140\192\207tW\242\189\245\SOHg\209\ENQ\165\133\NAK\212o\243^\225\144\193\251\161\219\177\150\207\200\&2\181\215u\188<ASN\161\223"+              }+        , exactObjectRaw =+            "0\130\SOH\DEL\STX\ACK\SOHwL\174\253\a0\r\ACK\t*\134H\134\247\r\SOH\SOH\v\ENQ\NUL0\SI1\r0\v\ACK\ETXU\EOT\ETX\f\EOTHERP0\RS\ETB\r210129054612Z\ETB\r310129054752Z0\SI1\r0\v\ACK\ETXU\EOT\ETX\f\EOTHERP0\130\SOH\"0\r\ACK\t*\134H\134\247\r\SOH\SOH\SOH\ENQ\NUL\ETX\130\SOH\SI\NUL0\130\SOH\n\STX\130\SOH\SOH\NUL\212A\GS\ETX*\ETBo\160\236*\196K,\235\191\225\239\194;\200v\ETBYYg:\207\129\162\SI|\205\213\&1>q\184|\240\147\DC4\173T\214w\177\t.\175\189\184\ACK\179\v&_\205~Au\171\131\170\202~,\255s\219\CAN\137\233\153Z\163\\\249\201\199\198\225\220/\DLE\137N\191\138\215\215\&4\157\182\r\249\v?\144u\165\US\"@\132_wS\FS\142\221\NAK\204(\187\227^\151@F\nzu\239\SI T\243\129\EM\140\239:\208`Y\195U\247\239Y\154~\232I\SUB\ACK\229\RS.G8f>\253v\167\"\145;\253!f\190\160ND5\212+'\201\245\188\247F\195\&3NH\230o \r=\US\NUL\237\143\206v\208!\STX\130\150\165(\200\214\201e\176^\SI%{A\161$\144/\rrK\232\235/\155i\239(\DLE_i\197\b\151\ENQ^\221\RS\214<T\181\170\229#v!\229\245T\174}\132\200\&65\209\212\198\&3\EM\"!\STX\ETX\SOH\NUL\SOH"+        , encodeSignedObject =+            "0\130\STX\151\&0\130\SOH\DEL\STX\ACK\SOHwL\174\253\a0\r\ACK\t*\134H\134\247\r\SOH\SOH\v\ENQ\NUL0\SI1\r0\v\ACK\ETXU\EOT\ETX\f\EOTHERP0\RS\ETB\r210129054612Z\ETB\r310129054752Z0\SI1\r0\v\ACK\ETXU\EOT\ETX\f\EOTHERP0\130\SOH\"0\r\ACK\t*\134H\134\247\r\SOH\SOH\SOH\ENQ\NUL\ETX\130\SOH\SI\NUL0\130\SOH\n\STX\130\SOH\SOH\NUL\212A\GS\ETX*\ETBo\160\236*\196K,\235\191\225\239\194;\200v\ETBYYg:\207\129\162\SI|\205\213\&1>q\184|\240\147\DC4\173T\214w\177\t.\175\189\184\ACK\179\v&_\205~Au\171\131\170\202~,\255s\219\CAN\137\233\153Z\163\\\249\201\199\198\225\220/\DLE\137N\191\138\215\215\&4\157\182\r\249\v?\144u\165\US\"@\132_wS\FS\142\221\NAK\204(\187\227^\151@F\nzu\239\SI T\243\129\EM\140\239:\208`Y\195U\247\239Y\154~\232I\SUB\ACK\229\RS.G8f>\253v\167\"\145;\253!f\190\160ND5\212+'\201\245\188\247F\195\&3NH\230o \r=\US\NUL\237\143\206v\208!\STX\130\150\165(\200\214\201e\176^\SI%{A\161$\144/\rrK\232\235/\155i\239(\DLE_i\197\b\151\ENQ^\221\RS\214<T\181\170\229#v!\229\245T\174}\132\200\&65\209\212\198\&3\EM\"!\STX\ETX\SOH\NUL\SOH0\r\ACK\t*\134H\134\247\r\SOH\SOH\v\ENQ\NUL\ETX\130\SOH\SOH\NUL\204i\244\225\204\252\161\166hE@\168\232\226\161s\a\206J\184\232\141\250\155S\NAK\188d\ACK:P\245\NAK\240\135\192m\172L\149\206#\212\213j\221\172\"\251 \137X\176\218g#\151\226d2\SI\227h\FS\RSC\215\250\144\139\252\v\ACKH=L\155v\194\158\140\142C\143\175\t\r\242[\225i\186\188R\205\141\136\EM\225\US2\227Y\172%\182'\189\175U\180\166\232\DC1\ESC\138\146f\242j\162\194I\135\213\171\210o\f\157\163\246\240\254\223\167\130\DEL\143u\NUL\138\249Ff\245\215\135Y\FS\151\249QZ\SUB\CAN\170dw\175h\253\240\229\178\253gX\DEL\US\232<\208\214\&1\210Cu\USH\254\171e9Q\236\136@\207\241\158;\243\252\253!\140\RS9\134*\131\179\174\239\182,\255\145\186\193\FSQ\v\ACK\148\140\192\207tW\242\189\245\SOHg\209\ENQ\165\133\NAK\212o\243^\225\144\193\251\161\219\177\150\207\200\&2\181\215u\188<ASN\161\223"+        }+  , nameIDFormats =+      [ "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"+      , "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"+      , "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"+      , "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"+      ]+  , singleSignOnServices =+      [ ( HTTPPost+        , "https://sso.ja-sore.de/auth/realms/HERP/protocol/saml"+        )+      , ( HTTPRedirect+        , "https://sso.ja-sore.de/auth/realms/HERP/protocol/saml"+        )+      , ( SOAP+        , "https://sso.ja-sore.de/auth/realms/HERP/protocol/saml"+        )+      , ( HTTPArtifact+        , "https://sso.ja-sore.de/auth/realms/HERP/protocol/saml"+        )+      ]+  }
wai-saml2.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.1.+-- This file has been generated from package.yaml by hpack version 0.35.2. -- -- see: https://github.com/sol/hpack  name:           wai-saml2-version:        0.4+version:        0.5 synopsis:       SAML2 assertion validation as WAI middleware description:    A Haskell library which implements SAML2 assertion validation as WAI middleware category:       Security@@ -26,6 +26,10 @@     tests/data/keycloak.xml.expected     tests/data/okta.xml     tests/data/okta.xml.expected+    tests/data/metadata/google.xml+    tests/data/metadata/google.xml.expected+    tests/data/metadata/keycloak.xml+    tests/data/metadata/keycloak.xml.expected  source-repository head   type: git