packages feed

wai-saml2 0.6 → 0.7.0

raw patch · 23 files changed

+584/−302 lines, 23 filesdep +tasty-expected-failuredep +tasty-hunitdep +transformers

Dependencies added: tasty-expected-failure, tasty-hunit, transformers

Files

CHANGELOG.md view
@@ -1,5 +1,13 @@ # Changelog for `wai-saml2` +## 0.7++-   Replaced `x509Certificate` with `x509Certificates` in `IDPSSODescriptor` so that it may have more than one certificate ([#65](https://github.com/mbg/wai-saml2/pull/65) by [@fumieval](https://github.com/fumieval))+-   Added `attributeValues` to `AssertionAttribute` in order to handle multiple attribute values with the same name ([#67](https://github.com/mbg/wai-saml2/pull/67) by [@fumieval](https://github.com/fumieval))+-   Support signed assertions, not just signed responses ([#45](https://github.com/mbg/wai-saml2/pull/45) by [@fumieval](https://github.com/fumieval))+-   Fixed a bug that could cause `renderXML` to crash ([#66](https://github.com/mbg/wai-saml2/pull/66) by [@fumieval](https://github.com/fumieval))+-   Test more Stack resolvers in CI ([#71](https://github.com/mbg/wai-saml2/pull/71) by [@kushagarr](https://github.com/kushagarr))+ ## 0.6  -   Switch from `x509-*` to `crypton-x509-*` ([#50](https://github.com/mbg/wai-saml2/pull/50) by [@mbg](https://github.com/mbg)).
README.md view
@@ -1,8 +1,8 @@ # wai-saml2  ![GitHub](https://img.shields.io/github/license/mbg/wai-saml2)-![Haskell CI](https://github.com/mbg/wai-saml2/workflows/Haskell/badge.svg?branch=master)-![stackage-nightly](https://github.com/mbg/wai-saml2/workflows/stackage-nightly/badge.svg)+[![Haskell](https://github.com/mbg/wai-saml2/actions/workflows/haskell.yml/badge.svg)](https://github.com/mbg/wai-saml2/actions/workflows/haskell.yml)+[![Stackage Nightly](https://github.com/mbg/wai-saml2/actions/workflows/stackage-nightly.yml/badge.svg)](https://github.com/mbg/wai-saml2/actions/workflows/stackage-nightly.yml) [![Hackage](https://img.shields.io/hackage/v/wai-saml2)](https://hackage.haskell.org/package/wai-saml2)  A Haskell library which implements SAML2 assertion validation as WAI middleware. This can be used by a Haskell web application (the service provider, SP) to perform identity provider (IdP) initiated authentication, i.e. SAML2-based authentication where the authentication begins at the IdP-end, the IdP authenticates the user, and then gets the user to submit a SAML2 assertion back to the SP (known as "unsolicited SSO" within e.g. [the Shibboleth project](https://wiki.shibboleth.net/confluence/display/IDP30/UnsolicitedSSOConfiguration#UnsolicitedSSOConfiguration-SAML2.0)).@@ -31,8 +31,12 @@  ### Configuration -The `saml2Config` function may be used to construct `SAML2Config` values. It expects at least the SP's private key and the IdP's public key as arguments (even when mandatory encryption is disabled) but you should almost certainly customise the configuration further. The private and public keys can be loaded with functions from the `Data.X509` and `Data.X509.File` modules (from the `x509` and `x509-store` packages, respectively):+How to configure this library depends on your IdP's configuration. You should consult the relevant documentation for your IdP as well as review your SP's configuration on the IdP end. You should almost certainly customise the configuration beyond the defaults described below. +The `saml2Config` function may be used to construct `SAML2Config` values. Configurations constructed with `saml2Config` expect assertions to be encrypted. If you expect assertions to be unencrypted, then you may wish to start with `saml2ConfigNoEncryption` instead.++Since the `saml2Config` function expects encrypted assertions, it needs at least the SP's private key and the IdP's public key as arguments (even when mandatory encryption is disabled). The private and public keys can be loaded with functions from the `Data.X509` and `Data.X509.File` modules (from the `x509` and `x509-store` packages, respectively):+ ```haskell (saml2Config spPrivateKey idpPublicKey){     saml2AssertionPath = "/sso/assert",@@ -41,11 +45,13 @@ } ``` -The configuration options are documented in the Haddock documentation for the `Network.Wai.SAML2.Config` module.+The configuration options (`saml2AssertionPath`, `saml2ExpectedIssuer`, `saml2ExpectedDestination`, etc.) are documented in the Haddock documentation for the `Network.Wai.SAML2.Config` module. +Both `saml2Config` and `saml2ConfigNoEncryption` construct configurations which validate only the response signature. If you need to validate the assertion signature, you must change the `saml2ValidationTarget` property to `ValidateAssertion`. This can also be set to `ValidateEither`, which will require __either__ the response signature __or__ the assertion signature to be valid. Do not use `ValidateEither` unless your IdP requires this.+ ### Implementation -Two interfaces to the middleware are provided. See the Haddock documentation for the `Network.Wai.SAML2` module for full usage examples. An example using the `saml2Callback` variant is shown below, where `cfg` is a `SAML2Config` value and `app` is your existing WAI application:+Two interfaces to the middleware are provided. See the Haddock documentation for the `Network.Wai.SAML2` module for full usage examples. An example using the `saml2Callback` variant is shown below, where `cfg` is a `SAML2Config` value and `mainApp` is your existing WAI application:  ```haskell saml2Callback cfg callback mainApp
src/Network/Wai/SAML2/Assertion.hs view
@@ -31,6 +31,7 @@ import Text.XML.Cursor  import Network.Wai.SAML2.NameIDFormat+import Network.Wai.SAML2.Signature import Network.Wai.SAML2.XML  --------------------------------------------------------------------------------@@ -230,19 +231,24 @@     attributeFriendlyName :: !(Maybe T.Text),     -- | The name format.     attributeNameFormat :: !T.Text,-    -- | The value of the attribute.-    attributeValue :: !T.Text+    -- | The value of the attribute, concatened from the 'attributeValues'.+    attributeValue :: !T.Text,+    -- | The values of the attribute.+    --+    -- @since 0.7+    attributeValues :: ![T.Text] } deriving (Eq, Show)  instance FromXML AssertionAttribute where     parseXML cursor = do+        let attributeValues = cursor $/ element (saml2Name "AttributeValue") &/ content         pure AssertionAttribute{             attributeName = T.concat $ attribute "Name" cursor,             attributeFriendlyName =                 toMaybeText $ attribute "FriendlyName" cursor,             attributeNameFormat = T.concat $ attribute "NameFormat" cursor,-            attributeValue = T.concat $-                cursor $/ element (saml2Name "AttributeValue") &/ content+            attributeValue = T.concat attributeValues,+            attributeValues = attributeValues         }  -- | SAML2 assertion statements (collections of assertion attributes).@@ -273,7 +279,11 @@     -- | The authentication statement included in the assertion.     assertionAuthnStatement :: !AuthnStatement,     -- | The assertion's attribute statement.-    assertionAttributeStatement :: !AttributeStatement+    assertionAttributeStatement :: !AttributeStatement,+    -- | The assertion's signature.+    --+    -- @since 0.7+    assertionSignature :: !(Maybe Signature) } deriving (Eq, Show)  -- Reference [Assertion]@@ -301,7 +311,9 @@             assertionAuthnStatement = authnStatement,             assertionAttributeStatement =                 cursor $/ element (saml2Name "AttributeStatement")-                    >=> parseAttributeStatement+                    >=> parseAttributeStatement,+            assertionSignature = listToMaybe $+                cursor $/ element (dsName "Signature") >=> parseXML         }  --------------------------------------------------------------------------------
src/Network/Wai/SAML2/Config.hs view
@@ -8,6 +8,7 @@ -- | Configuration types and smart constructors for the SAML2 middleware. module Network.Wai.SAML2.Config (     SAML2Config(..),+    ValidationTarget(..),     saml2Config,     saml2ConfigNoEncryption ) where@@ -50,9 +51,25 @@     -- | Always decrypt assertions using 'saml2PrivateKey' and reject plaintext assertions.     --     -- @since 0.4-    saml2RequireEncryptedAssertion :: !Bool+    saml2RequireEncryptedAssertion :: !Bool,++    -- | Which part of the SAML2 response to validate.+    --+    -- @since 0.7+    saml2ValidationTarget :: !ValidationTarget } +-- | Which part of the SAML2 response to validate.+--+-- @since 0.7+data ValidationTarget+    -- | Passes validation if the assertion signature is valid.+    = ValidateAssertion+    -- | Passes validation if the response signature is valid.+    | ValidateResponse+    -- | Passes validation if either the assertion or the response signature is valid.+    | ValidateEither+ -- | 'saml2Config' @privateKey publicKey@ constructs a 'SAML2Config' value -- with the most basic set of options possible using @privateKey@ as the -- SP's private key and @publicKey@ as the IdP's public key. You should@@ -79,7 +96,8 @@     saml2ExpectedDestination = Nothing,     saml2Audiences = [],     saml2DisableTimeValidation = False,-    saml2RequireEncryptedAssertion = False+    saml2RequireEncryptedAssertion = False,+    saml2ValidationTarget = ValidateResponse }  --------------------------------------------------------------------------------
src/Network/Wai/SAML2/EntityDescriptor.hs view
@@ -36,8 +36,9 @@     = IDPSSODescriptor {         -- | IdP Entity ID. 'Network.Wai.SAML2.Config.saml2ExpectedIssuer' should be compared against this identifier         entityID :: Text-        -- | The X.509 certificate for signed assertions-    ,   x509Certificate :: X509.SignedExact X509.Certificate+        -- | @since 0.7+        -- The X.509 certificates for signed assertions+    ,   x509Certificates :: [X509.SignedExact X509.Certificate]         -- | Supported NameID formats     ,   nameIDFormats :: [Text]         -- | List of SSO urls corresponding to 'Binding's@@ -66,16 +67,18 @@         let entityID = T.concat $ attribute "entityID" cursor         descriptor <- oneOrFail "IDPSSODescriptor is required"             $ cursor $/ element (mdName "IDPSSODescriptor")-        rawCertificate <- oneOrFail "X509Certificate is required" $ descriptor-            $/ element (mdName "KeyDescriptor")-            &/ element (dsName "KeyInfo")-            &/ element (dsName "X509Data")-            &/ element (dsName "X509Certificate")-            &/ content-        x509Certificate <- either fail pure-            $ X509.decodeSignedObject-            $ Base64.decodeLenient-            $ T.encodeUtf8 rawCertificate+        let rawCertificates = descriptor+                $/ element (mdName "KeyDescriptor")+                &/ element (dsName "KeyInfo")+                &/ element (dsName "X509Data")+                &/ element (dsName "X509Certificate")+                &/ content+        x509Certificates <- traverse+            ( either fail pure+            . X509.decodeSignedObject+            . Base64.decodeLenient+            . T.encodeUtf8+            ) rawCertificates         let nameIDFormats = descriptor                 $/ element (mdName "NameIDFormat")                 &/ content
src/Network/Wai/SAML2/Error.hs view
@@ -73,6 +73,12 @@     --     -- @since 0.4     | EncryptedAssertionNotSupported+    -- | The response is missing an assertion.+    | AssertionMissing+    -- | The response is missing a signature for the assertion.+    | AssertionSignatureMissing+    -- | The response is missing a signature for the response.+    | ResponseSignatureMissing     deriving Show  --------------------------------------------------------------------------------
src/Network/Wai/SAML2/Request.hs view
@@ -33,6 +33,7 @@  import Crypto.Random +import Data.Foldable (toList) import Data.Time.Clock  import Network.Wai.SAML2.NameIDFormat@@ -132,7 +133,7 @@                 , ("AssertionConsumerServiceIndex", "1") -- [AuthnRequest]                 ]                 -- [RequestAbstractType]-                ++ [("Destination", uri) | let Just uri = authnRequestDestination] ))+                ++ [("Destination", uri) | uri <- toList authnRequestDestination] ))             [NodeElement issuer, NodeElement nameIdPolicy]         -- Reference [RequestAbstractType]         issuer = Element
src/Network/Wai/SAML2/Response.hs view
@@ -55,8 +55,12 @@     responseIssuer :: !T.Text,     -- | The status of the response.     responseStatusCode :: !StatusCode,-    -- | The response signature.-    responseSignature :: !Signature,+    -- | The optional response signature. Some IdPs may not include this in the response+    -- and may instead include a signature in the assertion.+    -- If so, you should set the @saml2ValidationTarget@ to @ValidateAssertion@.+    -- The `responseSignature` is expected to be some value and will be validated if+    -- @saml2ValidationTarget@ is @ValidateResponse@.+    responseSignature :: !(Maybe Signature),     -- | The unencrypted assertion.     --     -- @since 0.4@@ -88,9 +92,6 @@                     $/  element (saml2Name "EncryptedAssertion")                     ) >>= parseXML -        signature <- oneOrFail "Signature is required" (-            cursor $/ element (dsName "Signature") ) >>= parseXML-         pure Response{             responseDestination = T.concat $ attribute "Destination" cursor,             responseId = T.concat $ attribute "ID" cursor,@@ -100,7 +101,8 @@             responseIssuer = T.concat $                 cursor $/ element (saml2Name "Issuer") &/ content,             responseStatusCode = statusCode,-            responseSignature = signature,+            responseSignature = listToMaybe $+                (cursor $/ element (dsName "Signature")) >>= parseXML,             responseAssertion = assertion,             responseEncryptedAssertion = encAssertion         }
src/Network/Wai/SAML2/Validation.hs view
@@ -5,7 +5,7 @@ -- file in the root directory of this source tree.                            -- -------------------------------------------------------------------------------- --- | Functions to process and validate SAML2 respones.+-- | Functions to process and validate SAML2 responses. module Network.Wai.SAML2.Validation (     validateResponse,     decodeResponse,@@ -17,7 +17,7 @@  import Control.Exception import Control.Monad (forM_, when, unless)-import Control.Monad.Except+import Control.Monad.Except (ExceptT, runExceptT, throwError) import Control.Monad.IO.Class (liftIO)  import Crypto.Error@@ -83,16 +83,12 @@         Left err -> throwError $ InvalidResponse err         Right samlResponse -> pure (responseXmlDoc, samlResponse) --- | 'validateSAMLResponse' @cfg doc response timestamp@ validates a decoded SAML2--- response using the given @timestamp@.+-- | 'validateSAMLPreliminary' @cfg samlResponse@ validates the status code,+-- destination, and issuer of @samlResponse@ according to @cfg@. ----- @since 0.4-validateSAMLResponse :: SAML2Config-                     -> XML.Document-                     -> Response-                     -> UTCTime-                     -> ExceptT SAML2Error IO Assertion-validateSAMLResponse cfg responseXmlDoc samlResponse now = do+-- @since 0.7+validateSAMLPreliminary :: SAML2Config -> Response -> ExceptT SAML2Error IO ()+validateSAMLPreliminary cfg samlResponse = do      -- check that the response indicates success     case statusCodeValue $ responseStatusCode samlResponse of@@ -118,6 +114,52 @@             | issuer /= expectedIssuer -> throwError $ InvalidIssuer issuer         _ -> pure () +-- | 'validateSAMLResponse' @cfg doc response timestamp@ validates a decoded SAML2+-- response using the given @timestamp@.+--+-- @since 0.4+validateSAMLResponse :: SAML2Config+                     -> XML.Document+                     -> Response+                     -> UTCTime+                     -> ExceptT SAML2Error IO Assertion+validateSAMLResponse cfg responseXmlDoc samlResponse now = do+    validateSAMLPreliminary cfg samlResponse++    case saml2ValidationTarget cfg of+        ValidateAssertion -> validateSAMLAssertionSignature cfg responseXmlDoc samlResponse now+        ValidateResponse -> case responseSignature samlResponse of+            Just signature -> validateSAMLResponseSignature cfg responseXmlDoc samlResponse signature now+            Nothing -> throwError ResponseSignatureMissing+        ValidateEither -> case responseSignature samlResponse of+            Just signature -> validateSAMLResponseSignature cfg responseXmlDoc samlResponse signature now+            Nothing -> validateSAMLAssertionSignature cfg responseXmlDoc samlResponse now++-- | Represents state required for the validation of a SAML2 response.+--+-- @since 0.7+data ValidationContext = ValidationContext {+    cfg :: SAML2Config,+    responseXmlDoc :: XML.Document,+    samlResponse :: Response,+    now :: UTCTime,+    signedInfo :: XML.Element,+    signature :: Signature,+    docMinusSignature :: XML.Document+}++-- | `validateSAMLResponseSignature` validates a response signature+-- and returns the assertion.+--+-- @since 0.7+validateSAMLResponseSignature+    :: SAML2Config+    -> XML.Document+    -> Response+    -> Signature+    -> UTCTime+    -> ExceptT SAML2Error IO Assertion+validateSAMLResponseSignature cfg responseXmlDoc samlResponse signature now = do     --  ***CORE VALIDATION***     -- See https://www.w3.org/TR/xmldsig-core1/#sec-CoreValidation     --@@ -126,20 +168,6 @@     -- Signature element. This element contains     signedInfo <- extractSignedInfo (XML.fromDocument responseXmlDoc) -    -- construct a new XML document from the SignedInfo element and render-    -- it into a textual representation-    let doc = XML.Document (XML.Prologue [] Nothing []) signedInfo []-    let signedInfoXml = XML.renderLBS def doc--    -- canonicalise the textual representation of the SignedInfo element-    let prefixList = extractPrefixList (XML.fromDocument doc)-    signedInfoCanonResult <- liftIO $ try $-        canonicalise prefixList (LBS.toStrict signedInfoXml)--    normalisedSignedInfo <- case signedInfoCanonResult of-        Left err -> throwError $ CanonicalisationFailure err-        Right result -> pure result-     -- 2. At this point we should dereference all elements identified by     -- Reference elements inside the SignedInfo element. However, we do     -- not currently do that and instead just assume that there is only@@ -149,8 +177,7 @@     let documentId = responseId samlResponse     let referenceId = referenceURI                     $ signedInfoReference-                    $ signatureInfo-                    $ responseSignature samlResponse+                    $ signatureInfo signature      if documentId /= referenceId     then throwError $ UnexpectedReference referenceId@@ -162,6 +189,29 @@     -- the Signature element present). First remove the Signature element:     let docMinusSignature = removeSignature responseXmlDoc +    validateSAMLSignature ValidationContext{..}++-- | `validateSAMLSignature` @validationContext@ validates the SAML2 response+-- according to @validationContext@ and returns the `Assertion` contained in it+-- if validation is successful.+--+-- @since 0.7+validateSAMLSignature :: ValidationContext -> ExceptT SAML2Error IO Assertion+validateSAMLSignature ValidationContext{..} = do+    -- construct a new XML document from the SignedInfo element and render+    -- it into a textual representation+    let doc = XML.Document (XML.Prologue [] Nothing []) signedInfo []+    let signedInfoXml = XML.renderLBS def doc++    -- canonicalise the textual representation of the SignedInfo element+    let prefixList = extractPrefixList (XML.fromDocument doc)+    signedInfoCanonResult <- liftIO $ try $+        canonicalise prefixList (LBS.toStrict signedInfoXml)++    normalisedSignedInfo <- case signedInfoCanonResult of+        Left err -> throwError $ CanonicalisationFailure err+        Right result -> pure result+     -- then render the resulting document and canonicalise it     let renderedXml = XML.renderLBS def docMinusSignature     refCanonResult <- liftIO $ try $ canonicalise prefixList (LBS.toStrict renderedXml)@@ -180,8 +230,7 @@                       $ BS.decodeLenient                       $ referenceDigestValue                       $ signedInfoReference-                      $ signatureInfo-                      $ responseSignature samlResponse+                      $ signatureInfo signature      if Just documentHash /= referenceHash     then throwError InvalidDigest@@ -191,7 +240,7 @@     -- We need to check that the SignedInfo element has not been tampered     -- with, which we do by checking the signature contained in the response;     -- first: extract the signature data from the response-    let sig = BS.decodeLenient $ signatureValue $ responseSignature samlResponse+    let sig = BS.decodeLenient $ signatureValue signature      -- using the IdP's public key and the canonicalised SignedInfo element,     -- check that the signature is correct@@ -209,7 +258,7 @@             | saml2RequireEncryptedAssertion cfg -> throwError EncryptedAssertionRequired             | otherwise -> case responseAssertion samlResponse of                 Just plain -> pure plain-                Nothing -> throwError $ InvalidResponse $ userError "Assertion or EncryptedAssertion is required"+                Nothing -> throwError AssertionMissing      -- validate that the assertion is valid at this point in time     let Conditions{..} = assertionConditions assertion@@ -232,6 +281,41 @@      -- all checks out, return the assertion     pure assertion++-- | `validateSAMLAssertionSignature` @cfg doc res time@ validates the assertion signature+-- contained in @res@ and returns the `Assertion` if the response can be validated.+--+-- @since 0.7+validateSAMLAssertionSignature+    :: SAML2Config+    -> XML.Document+    -> Response+    -> UTCTime+    -> ExceptT SAML2Error IO Assertion+validateSAMLAssertionSignature cfg responseXmlDoc samlResponse now = do+    assertion <- case responseAssertion samlResponse of+        Just a -> pure a+        _ -> throwError AssertionMissing++    signature <- case assertionSignature assertion of+        Just a -> pure a+        _ -> throwError AssertionSignatureMissing++    -- Obtain the XML node of the assertion for validation+    assertionXml <- oneOrFail "Assertion is required" $+        XML.fromDocument responseXmlDoc XML.$/ XML.element (saml2Name "Assertion")++    signedInfo <- extractSignedInfo assertionXml++    docMinusSignature <- removeSignature <$> case XML.node assertionXml of+        XML.NodeElement node -> pure XML.Document+            { documentPrologue = XML.Prologue [] Nothing []+            , documentRoot = node+            , documentEpilogue = []+            }+        _ -> throwError $ InvalidResponse $ userError "Assertion is not a valid XML element"++    validateSAMLSignature ValidationContext{..}  -- | `decryptAssertion` @key encryptedAssertion@ decrypts the AES key in -- @encryptedAssertion@ using `key`, then decrypts the contents using
tests/Parser.hs view
@@ -1,6 +1,9 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE TypeApplications #-}++module Parser where+ import Network.Wai.SAML2.EntityDescriptor import Network.Wai.SAML2.Response import Network.Wai.SAML2.XML@@ -18,8 +21,8 @@     resp <- parseXML (fromDocument doc)     pure $ BC.pack $ ppShow (resp :: t) -main :: IO ()-main = defaultMain $ testGroup "Parse SAML2 response"+tests :: TestTree+tests = testGroup "Parse SAML2 response"     [ mkGolden @Response $ prefix </> "keycloak.xml"     , mkGolden @Response $ prefix </> "okta.xml"     , mkGolden @Response $ prefix </> "google.xml"
+ tests/Validation.hs view
@@ -0,0 +1,54 @@+module Validation where++import Control.Monad.Trans.Except+import Crypto.PubKey.RSA (PublicKey)+import qualified Data.ByteString as B+import qualified Data.ByteString.Base64 as Base64+import Data.Time.Format.ISO8601+import qualified Data.X509 as X509+import qualified Data.X509.Memory as X509+import Network.Wai.SAML2+import Network.Wai.SAML2.Validation+import System.FilePath+import Test.Tasty+import Test.Tasty.HUnit++-- | Get a public key from a X.509 certificate+parseCertificate :: B.ByteString -> PublicKey+parseCertificate certificate = case X509.readSignedObjectFromMemory certificate of+    [signedCert] -> case X509.certPubKey $ X509.signedObject $ X509.getSigned signedCert of+        X509.PubKeyRSA key -> key+        other -> error $ "Expected PubKeyRSA, but got " <> show other+    xs -> error $ show xs++run :: FilePath -> String -> FilePath -> IO ()+run certPath timestamp respPath = do+    cert <- B.readFile $ prefix </> certPath+    xml <- B.readFile $ prefix </> respPath+    now <- iso8601ParseM timestamp++    let pub = parseCertificate cert+        cfg = (saml2ConfigNoEncryption pub) {+            saml2ValidationTarget = ValidateEither+        }++    assertion <- runExceptT $ do+        (responseXmlDoc, samlResponse) <- decodeResponse $ Base64.encode xml+        validateSAMLResponse cfg responseXmlDoc samlResponse now++    case assertion of+        Left err -> assertFailure $ show err+        Right _ -> pure ()++prefix :: FilePath+prefix = "tests/data"++tests :: TestTree+tests = testGroup "Validate SAML2 Response"+    [ testCase "AzureAD signed response"+        $ run "azuread.crt" "2023-05-10T01:20:00Z" "azuread-signed-response.xml"+    , testCase "AzureAD signed assertion"+        $ run "azuread.crt" "2023-05-09T16:00:00Z" "azuread-signed-assertion.xml"+    , testCase "Okta with AttributeStatement"+        $ run "okta.crt" "2023-06-16T06:43:00.000Z" "okta-attributes.xml"+    ]
+ tests/data/azuread-signed-assertion.xml view
@@ -0,0 +1,1 @@+<samlp:Response ID="_c082940d-31cf-40a2-a581-2a7af122e7e5" Version="2.0" IssueInstant="2023-05-09T15:45:24.293Z" Destination="https://loopback.ja-sore.de:3443/auth/page/saml2/login" InResponseTo="id23dffd06a31f7ad10975c9c893bf8668" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://sts.windows.net/b0a63ade-3ec7-4d8b-991f-87eb4336274a/</Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><Assertion ID="_7dd71b79-0320-4c6b-b524-72f6993d8100" IssueInstant="2023-05-09T15:45:24.288Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Issuer>https://sts.windows.net/b0a63ade-3ec7-4d8b-991f-87eb4336274a/</Issuer><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="#_7dd71b79-0320-4c6b-b524-72f6993d8100"><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>SkxHylilOD37KOxJT4V0YLIsL3W3AYHWM+iIZHmbukc=</DigestValue></Reference></SignedInfo><SignatureValue>EIg22vtTqnEhiwE3HYruwnWOTKQjs57aQSqeq4gnLV7yoqQw0jjPWkkGTto2/0TeHWomX58Gj2MDNCRjlwid2jQuy6jZQW2+wDBurElVAO7trcxrX48EaKnG9ZPh/1++40O1l970zVzSRwknFvnOHpghWQsib9NadrRWB6/ZbmwpVhCfYYAcfu8z/o8TdQQtE66I2dr6YD8kAPbBe/vEeHBVPycaZj+8fqia5sIpGBUnH7rTvaTnzBHol1zg1YYyK8O53p7baQaQQ8WEZ4agBNjtHeJGbo2bP8uvO14FnoVoUQqDATJKkDHq5rM+6tQ0RvZgSP6jjKoiw5pfchedpQ==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIC8DCCAdigAwIBAgIQafqoqGZ3HoxNh23cdsDACjANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQDEylNaWNyb3NvZnQgQXp1cmUgRmVkZXJhdGVkIFNTTyBDZXJ0aWZpY2F0ZTAeFw0yMjEwMjQwNDM3MzJaFw0yNTEwMjQwNDM3MzJaMDQxMjAwBgNVBAMTKU1pY3Jvc29mdCBBenVyZSBGZWRlcmF0ZWQgU1NPIENlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuIrXrPws5kjzFTAJbXa/pitQ2hZTs9CMOv48iFXJLRRr90GaIUikqbU0X4CL3bewMC0XVBlBQwTGpRIIWbYreZ6lfQYaP/ACGysQ96m2aknH8cUQdlUFCEo94LlzTLqkDf+JWfdBT6AWDS9aLjS/r25HZRUR7xBcdSYOfSEE2UcO8QBH9BvoOD/xBBwAvSo4rjOwr9ZaKAG3Axu7Dh/T2AAE5ZHbCIQEeMEEkofQbexitiTYt0c2CyWdAFoR6MlxEPhWE8sIko62PhDMBMuGu67ZCbBINIVj2CcDr1kBx6OVdgvZYum/A09RRzBTMuFMP2+WG3yCjaUMA3Gn5lpv2QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAO0/TzTx1OQYUTPGwafh2mHzVpc9Hk2LIY+YvV4bbVsUwnuV6HKVr2Sn3uLIUiSE/JjTdjy7KE/1LBN2KNMe7vs67gOIjOODf/LMQJMHqu7oJtZdt1omrpxJH6DkA/YmPGyUOcX7ADLbaw4cf2lTt8Pk97HP+EvAM31ZfjLtgyGDlREeWa/y2wWOHOdeO1CGwvK1BKz9Sdg7bAs7lBSX/1Qp8pnnOJb/2wNuc9vw6p5UCEFvlAzGyRRLPZfDiazDzTznTyYDPupzJ5pic3rcogzCGQGUWW5dGG7c6lM6EAYDKNAZ+cv4wWrMA4sAo+DdNkzs8sDSv8Jw1AXGRuOTzQ</X509Certificate></X509Data></KeyInfo></Signature><Subject><NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">fumieval@herpdev.onmicrosoft.com</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><SubjectConfirmationData InResponseTo="id23dffd06a31f7ad10975c9c893bf8668" NotOnOrAfter="2023-05-09T16:45:24.198Z" Recipient="https://loopback.ja-sore.de:3443/auth/page/saml2/login"/></SubjectConfirmation></Subject><Conditions NotBefore="2023-05-09T15:40:24.198Z" NotOnOrAfter="2023-05-09T16:45:24.198Z"><AudienceRestriction><Audience>https://loopback.ja-sore.de:3443/</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid"><AttributeValue>b0a63ade-3ec7-4d8b-991f-87eb4336274a</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/objectidentifier"><AttributeValue>552200d7-3516-4d81-8ea1-a87b429f07ef</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/displayname"><AttributeValue>fumieval</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider"><AttributeValue>https://sts.windows.net/b0a63ade-3ec7-4d8b-991f-87eb4336274a/</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/claims/authnmethodsreferences"><AttributeValue>http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><AttributeValue>fumieval@herpdev.onmicrosoft.com</AttributeValue></Attribute></AttributeStatement><AuthnStatement AuthnInstant="2023-05-09T06:21:17.599Z" SessionIndex="_7dd71b79-0320-4c6b-b524-72f6993d8100"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion></samlp:Response>
+ tests/data/azuread-signed-response.xml view
@@ -0,0 +1,1 @@+<samlp:Response ID="_3276aca6-caa4-4e08-843a-f03eeafde126" Version="2.0" IssueInstant="2023-05-10T01:17:32.634Z" Destination="https://loopback.ja-sore.de:3443/auth/page/saml2/login" InResponseTo="id63a9912a51445aa4d4ec3dbf2aada166" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://sts.windows.net/b0a63ade-3ec7-4d8b-991f-87eb4336274a/</Issuer><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="#_3276aca6-caa4-4e08-843a-f03eeafde126"><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>smKor6LEHK0P+AlWTo7tPay67uUlbAe+ab0i9SrP6l8=</DigestValue></Reference></SignedInfo><SignatureValue>naCN4lVR8RyqmLg4k0xjV2iM3mauBfBvswhJC/y2ikUf/i61WnOzmwI6+71yM8KSWCwiclQeUdgQf1ZHlNUlqub/ovaHQw6h5PN5wNSxDXp1O/YJ7Mh+JgcIAqKS5lQyes0LO1KAIukEShcla1ml4CnnzEjVQl7dBDsmwu3hRmkYSOeLCh1Ln0kCclG1W5IFJiDd2IJLoomUGvUq3Ei5sS/dFCRgPizu8IdFYjAvo51WwFDJGMVJLFnfo/xf+FctUt9MWMtOJ4X0J2RefLgyAVyT9NFzQWMOEBPXHinHfmWp9bI1DtQz4UZJnwJW1IizNlKpdE0Yt8j0FqvmAFHwOA==</SignatureValue><KeyInfo><ds:X509Data xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Certificate>MIIC8DCCAdigAwIBAgIQafqoqGZ3HoxNh23cdsDACjANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQDEylNaWNyb3NvZnQgQXp1cmUgRmVkZXJhdGVkIFNTTyBDZXJ0aWZpY2F0ZTAeFw0yMjEwMjQwNDM3MzJaFw0yNTEwMjQwNDM3MzJaMDQxMjAwBgNVBAMTKU1pY3Jvc29mdCBBenVyZSBGZWRlcmF0ZWQgU1NPIENlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuIrXrPws5kjzFTAJbXa/pitQ2hZTs9CMOv48iFXJLRRr90GaIUikqbU0X4CL3bewMC0XVBlBQwTGpRIIWbYreZ6lfQYaP/ACGysQ96m2aknH8cUQdlUFCEo94LlzTLqkDf+JWfdBT6AWDS9aLjS/r25HZRUR7xBcdSYOfSEE2UcO8QBH9BvoOD/xBBwAvSo4rjOwr9ZaKAG3Axu7Dh/T2AAE5ZHbCIQEeMEEkofQbexitiTYt0c2CyWdAFoR6MlxEPhWE8sIko62PhDMBMuGu67ZCbBINIVj2CcDr1kBx6OVdgvZYum/A09RRzBTMuFMP2+WG3yCjaUMA3Gn5lpv2QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAO0/TzTx1OQYUTPGwafh2mHzVpc9Hk2LIY+YvV4bbVsUwnuV6HKVr2Sn3uLIUiSE/JjTdjy7KE/1LBN2KNMe7vs67gOIjOODf/LMQJMHqu7oJtZdt1omrpxJH6DkA/YmPGyUOcX7ADLbaw4cf2lTt8Pk97HP+EvAM31ZfjLtgyGDlREeWa/y2wWOHOdeO1CGwvK1BKz9Sdg7bAs7lBSX/1Qp8pnnOJb/2wNuc9vw6p5UCEFvlAzGyRRLPZfDiazDzTznTyYDPupzJ5pic3rcogzCGQGUWW5dGG7c6lM6EAYDKNAZ+cv4wWrMA4sAo+DdNkzs8sDSv8Jw1AXGRuOTzQ</ds:X509Certificate></ds:X509Data></KeyInfo></Signature><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><Assertion ID="_f28f92be-9cc4-44df-bfa0-4245434f9d00" IssueInstant="2023-05-10T01:17:32.632Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Issuer>https://sts.windows.net/b0a63ade-3ec7-4d8b-991f-87eb4336274a/</Issuer><Subject><NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">fumieval@herpdev.onmicrosoft.com</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><SubjectConfirmationData InResponseTo="id63a9912a51445aa4d4ec3dbf2aada166" NotOnOrAfter="2023-05-10T02:17:32.563Z" Recipient="https://loopback.ja-sore.de:3443/auth/page/saml2/login"/></SubjectConfirmation></Subject><Conditions NotBefore="2023-05-10T01:12:32.563Z" NotOnOrAfter="2023-05-10T02:17:32.563Z"><AudienceRestriction><Audience>https://loopback.ja-sore.de:3443/</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid"><AttributeValue>b0a63ade-3ec7-4d8b-991f-87eb4336274a</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/objectidentifier"><AttributeValue>552200d7-3516-4d81-8ea1-a87b429f07ef</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/displayname"><AttributeValue>fumieval</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider"><AttributeValue>https://sts.windows.net/b0a63ade-3ec7-4d8b-991f-87eb4336274a/</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/claims/authnmethodsreferences"><AttributeValue>http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><AttributeValue>fumieval@herpdev.onmicrosoft.com</AttributeValue></Attribute></AttributeStatement><AuthnStatement AuthnInstant="2023-05-09T06:21:17.599Z" SessionIndex="_f28f92be-9cc4-44df-bfa0-4245434f9d00"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion></samlp:Response>
+ tests/data/azuread.crt view
@@ -0,0 +1,3 @@+-----BEGIN CERTIFICATE-----+MIIC8DCCAdigAwIBAgIQafqoqGZ3HoxNh23cdsDACjANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQDEylNaWNyb3NvZnQgQXp1cmUgRmVkZXJhdGVkIFNTTyBDZXJ0aWZpY2F0ZTAeFw0yMjEwMjQwNDM3MzJaFw0yNTEwMjQwNDM3MzJaMDQxMjAwBgNVBAMTKU1pY3Jvc29mdCBBenVyZSBGZWRlcmF0ZWQgU1NPIENlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuIrXrPws5kjzFTAJbXa/pitQ2hZTs9CMOv48iFXJLRRr90GaIUikqbU0X4CL3bewMC0XVBlBQwTGpRIIWbYreZ6lfQYaP/ACGysQ96m2aknH8cUQdlUFCEo94LlzTLqkDf+JWfdBT6AWDS9aLjS/r25HZRUR7xBcdSYOfSEE2UcO8QBH9BvoOD/xBBwAvSo4rjOwr9ZaKAG3Axu7Dh/T2AAE5ZHbCIQEeMEEkofQbexitiTYt0c2CyWdAFoR6MlxEPhWE8sIko62PhDMBMuGu67ZCbBINIVj2CcDr1kBx6OVdgvZYum/A09RRzBTMuFMP2+WG3yCjaUMA3Gn5lpv2QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAO0/TzTx1OQYUTPGwafh2mHzVpc9Hk2LIY+YvV4bbVsUwnuV6HKVr2Sn3uLIUiSE/JjTdjy7KE/1LBN2KNMe7vs67gOIjOODf/LMQJMHqu7oJtZdt1omrpxJH6DkA/YmPGyUOcX7ADLbaw4cf2lTt8Pk97HP+EvAM31ZfjLtgyGDlREeWa/y2wWOHOdeO1CGwvK1BKz9Sdg7bAs7lBSX/1Qp8pnnOJb/2wNuc9vw6p5UCEFvlAzGyRRLPZfDiazDzTznTyYDPupzJ5pic3rcogzCGQGUWW5dGG7c6lM6EAYDKNAZ+cv4wWrMA4sAo+DdNkzs8sDSv8Jw1AXGRuOTzQ+-----END CERTIFICATE-----
tests/data/google.xml.expected view
@@ -13,22 +13,23 @@       MkStatusCode         { statusCodeValue = Success , statusCodeSubordinate = Nothing }   , responseSignature =-      Signature-        { signatureInfo =-            SignedInfo-              { signedInfoCanonicalisationMethod = C14N_EXC_1_0-              , signedInfoSignatureMethod = RSA_SHA256-              , signedInfoReference =-                  Reference-                    { referenceURI = "_b9917f3478ff3f776cb351547379d0bc"-                    , referenceDigestMethod = DigestSHA256-                    , referenceDigestValue =-                        "z3YXe+aIiyAYbSGqxRiYPwTO38JNZad2WBgqinjiX8g="-                    }-              }-        , signatureValue =-            "S6IAb+4yYyNVIfnqM7ZIrFSKbzi0RgVqk6qNL0ehzFZhcaArqN/8S0oCDrrc+0B2ygCkpbDKt03/\nvNIxq862VYa1Y39n8w0/eIKc+m3beNS6e+n316o3LCtSTPvgC2kE96Jh44MOUX9z/KIYxXiPGgdi\nnnlxK6L1dZGrMKKBxEFiBJMhFI42gWADyrUz311PDImM+rVhINJT9NyD1i1G++PILLer/BlGwg5n\nwNQHmjt4pV3m+8zMMIrfLjZT/YX8s7+1Xn1Y3bMzo6PnsbvS7OX1YGuN4cPn9f4MLu5Vcn9lWo/r\n+zuZay/pS0mEqTmeOOMGk4BOseiU/hAra5NDgQ=="-        }+      Just+        Signature+          { signatureInfo =+              SignedInfo+                { signedInfoCanonicalisationMethod = C14N_EXC_1_0+                , signedInfoSignatureMethod = RSA_SHA256+                , signedInfoReference =+                    Reference+                      { referenceURI = "_b9917f3478ff3f776cb351547379d0bc"+                      , referenceDigestMethod = DigestSHA256+                      , referenceDigestValue =+                          "z3YXe+aIiyAYbSGqxRiYPwTO38JNZad2WBgqinjiX8g="+                      }+                }+          , signatureValue =+              "S6IAb+4yYyNVIfnqM7ZIrFSKbzi0RgVqk6qNL0ehzFZhcaArqN/8S0oCDrrc+0B2ygCkpbDKt03/\nvNIxq862VYa1Y39n8w0/eIKc+m3beNS6e+n316o3LCtSTPvgC2kE96Jh44MOUX9z/KIYxXiPGgdi\nnnlxK6L1dZGrMKKBxEFiBJMhFI42gWADyrUz311PDImM+rVhINJT9NyD1i1G++PILLer/BlGwg5n\nwNQHmjt4pV3m+8zMMIrfLjZT/YX8s7+1Xn1Y3bMzo6PnsbvS7OX1YGuN4cPn9f4MLu5Vcn9lWo/r\n+zuZay/pS0mEqTmeOOMGk4BOseiU/hAra5NDgQ=="+          }   , responseAssertion =       Just         Assertion@@ -72,6 +73,7 @@                 , authnStatementLocality = ""                 }           , assertionAttributeStatement = []+          , assertionSignature = Nothing           }   , responseEncryptedAssertion = Nothing   }
tests/data/keycloak.xml.expected view
@@ -10,22 +10,23 @@       MkStatusCode         { statusCodeValue = Success , statusCodeSubordinate = Nothing }   , responseSignature =-      Signature-        { signatureInfo =-            SignedInfo-              { signedInfoCanonicalisationMethod = C14N_EXC_1_0-              , signedInfoSignatureMethod = RSA_SHA256-              , signedInfoReference =-                  Reference-                    { referenceURI = "ID_5b1d000b-3a5e-4dfe-aa4e-b7bf1e3efbfd"-                    , referenceDigestMethod = DigestSHA256-                    , referenceDigestValue =-                        "/U47P3hsUf+tUyyglYF8M1u6lbVnHimCthtxusju4mo="-                    }-              }-        , signatureValue =-            "b9vgIBQ1yNvUYgNmfAyuQJXOJ68PMfRvNAZEa93tnzZXHPEsf7/F49xI6/mlYI/T9pDxYnFcfl7kPMxgz4ssvMjwUEgAR3G3ZrNv4gPMUPmbZnXe0KG8yU9AskK90ya/T11kQfI21cSlA8FrLPTGP2X97yErR10mIDvEJ/m5dWra95cGLx/ntjaSIqNJpVgpHhRxieS4Lw+zeWe/nVuznXQnb8VRhCq18ikL/u23+YhYT3ws3iXQssJ2BosX9JJt0O+X31sIHJIWHsxbI69NLJ782bVDDkI1PNF8MKoa8gSEiLsNSmp3SyXtMPzaRIBguksl9xbnmYmsJDQg6kFVlQ=="-        }+      Just+        Signature+          { signatureInfo =+              SignedInfo+                { signedInfoCanonicalisationMethod = C14N_EXC_1_0+                , signedInfoSignatureMethod = RSA_SHA256+                , signedInfoReference =+                    Reference+                      { referenceURI = "ID_5b1d000b-3a5e-4dfe-aa4e-b7bf1e3efbfd"+                      , referenceDigestMethod = DigestSHA256+                      , referenceDigestValue =+                          "/U47P3hsUf+tUyyglYF8M1u6lbVnHimCthtxusju4mo="+                      }+                }+          , signatureValue =+              "b9vgIBQ1yNvUYgNmfAyuQJXOJ68PMfRvNAZEa93tnzZXHPEsf7/F49xI6/mlYI/T9pDxYnFcfl7kPMxgz4ssvMjwUEgAR3G3ZrNv4gPMUPmbZnXe0KG8yU9AskK90ya/T11kQfI21cSlA8FrLPTGP2X97yErR10mIDvEJ/m5dWra95cGLx/ntjaSIqNJpVgpHhRxieS4Lw+zeWe/nVuznXQnb8VRhCq18ikL/u23+YhYT3ws3iXQssJ2BosX9JJt0O+X31sIHJIWHsxbI69NLJ782bVDDkI1PNF8MKoa8gSEiLsNSmp3SyXtMPzaRIBguksl9xbnmYmsJDQg6kFVlQ=="+          }   , responseAssertion = Nothing   , responseEncryptedAssertion =       Just
tests/data/metadata/google.xml.expected view
@@ -1,131 +1,140 @@ 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 }+  , x509Certificates =+      [ 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"+                                      }+                                  )+                                ]                             }-                        )-                    , 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"+                      , certValidity =+                          ( DateTime+                              { dtDate =+                                  Date { dateYear = 2022 , dateMonth = July , dateDay = 21 }+                              , dtTime =+                                  TimeOfDay+                                    { todHour = 9 h+                                    , todMin = 18 m+                                    , todSec = 0 s+                                    , todNSec = 0 ns                                     }-                                )-                              , ( [ 2 , 5 , 4 , 8 ]-                                , ASN1CharacterString-                                    { characterEncoding = Printable-                                    , getCharacterStringRawData = "California"+                              }+                          , DateTime+                              { dtDate =+                                  Date { dateYear = 2027 , dateMonth = July , dateDay = 20 }+                              , dtTime =+                                  TimeOfDay+                                    { todHour = 9 h+                                    , todMin = 18 m+                                    , todSec = 0 s+                                    , todNSec = 0 ns                                     }-                                )-                              ]-                          }-                    , certPubKey =-                        PubKeyRSA-                          PublicKey-                            { public_size = 256-                            , public_n =-                                30883043751460212091009024715988672157201981026347726370499687818828606623610036063558065564910721575436562697595833895389078194628597753679300253553943785088191301337694287320462812204176082900979493095532403911400962461718281343090255859587662209446117641661982570706673532506066365285856542563187402675519172527708567282278848705855498817485769832020293045021199187525818891914876547302505893509597998904610940749090045911784327818251262543295743496580502018384973717126996683932142133838721711519234780192895538524052074852409104614561380839223725538723547399956034683375536115322508017066877882514789596315962019-                            , public_e = 65537+                              }+                          )+                      , 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"+                                      }+                                  )+                                ]                             }-                    , 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"-        }+                      , 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 =
tests/data/metadata/keycloak.xml.expected view
@@ -1,71 +1,80 @@ 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 }+  , x509Certificates =+      [ 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"+                                      }+                                  )+                                ]                             }-                        )-                    , certSubjectDN =-                        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                                     }-                                )-                              ]-                          }-                    , certPubKey =-                        PubKeyRSA-                          PublicKey-                            { public_size = 256-                            , public_n =-                                26794629232301390559329251866062548351573676959105948670047115599484903764153173211872085488745203643849524181464124430648664560428395941550339690556064181777927415101879096439027796505139145202723504898654935482393722978364774771646139306434335595822802292206982564879345724045673514452796802353368762193429772204243864488315862304970769841689431483551389268024017379114959044330977741690398490550580701831871711628065022359440646669330356587937408829251700791982308190330679272353052846282673680829331107723412188694305482396334605009242290197499924909025290277992294341810791131897406148044363907966374411188642337-                            , public_e = 65537+                              }+                          , 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"+                                      }+                                  )+                                ]                             }-                    , 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"-        }+                      , 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"
+ tests/data/okta-attributes.xml view
@@ -0,0 +1,17 @@+<?xml version="1.0" encoding="UTF-8"?><saml2p:Response Destination="https://panemagi.beta.ja-sore.de/authn/sso" ID="id92549195330378941022989346" IssueInstant="2023-06-16T06:42:44.371Z" Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xs="http://www.w3.org/2001/XMLSchema"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://www.okta.com/exk5qcxp4hc3aXlST697</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#id92549195330378941022989346"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xs" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>yE2k0Ez50kHpdaFnQdGIYs/fT18JtldMOhsgMfdBQ7c=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>PNuTkyHJKBlO0ZE53J/CicLGmSmDQK4RfIkMZyzDJHdtN2FOrLaMKYUIZIMt5dZsUGlRNe+p5b8TsMLzp+LQyf72JkrAtfoqin3TQXWJlxffW+ZkloWsyVxG/Prvox7PhgHgZDZDDCAdTPPLsLosCaptuC3m06DvEuSq7+p5UPtRqbkBaFEb27fe3NKGoGnOcBFZ/Le/ExJQ7thvB3RyvZk5RwVQ1R2M2jCLuZ5jlsc4FogRJ9V0tqj/PVxPK5fhhgnZbsZr3yNS8nWJNAIWwRt6sHEUKi5CrWUG5TuN9Hp/+kSbR7b0Ge1JKV1jZAUodeqzZ06luXipwIqBwV0Y2g==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDoDCCAoigAwIBAgIGAYiKK3aGMA0GCSqGSIb3DQEBCwUAMIGQMQswCQYDVQQGEwJVUzETMBEG+A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU+MBIGA1UECwwLU1NPUHJvdmlkZXIxETAPBgNVBAMMCGhlcnAtaW5jMRwwGgYJKoZIhvcNAQkBFg1p+bmZvQG9rdGEuY29tMB4XDTIzMDYwNTA2MDcwNFoXDTMzMDYwNTA2MDgwNFowgZAxCzAJBgNVBAYT+AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQK+DARPa3RhMRQwEgYDVQQLDAtTU09Qcm92aWRlcjERMA8GA1UEAwwIaGVycC1pbmMxHDAaBgkqhkiG+9w0BCQEWDWluZm9Ab2t0YS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2YKQa+PDrssVNqBokKyT77wYUlXKkTnHNtbD1rdXhiIGTszmxmF/NuzLfS1TMvzqiMnpbAwswTnNMF6sx2+M/gl9tWpL6OF4MvCQf78LvzyTOKvghojJkpE65XbkB4HETpOKYlXhvwwbCG4rskMqtFEosM2dxY6+KWUPAJyL0Z9hpqavvq6Ct8nAjZxHCKFQGcYfCfMXxI55/+xYuetHHo4BTj417FGLvHBgJkgYsc//+KRPzC1rPkTjIGn8hlmnGfkZ7srp+UGrewhlPvj6rZVkrgQdL6PTqXvwbe7XHOKjt79vPfGZBp/jq+FRwKTO1fbvGWzF2/vIJFuR90p4a90x6pAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGOKuxgCynAU+YU5oX19FiXrITcj3/XmdWZ2yTF72T0a4edhiKM0E0adcywxplllihSQV75k90Z+fmVREHFU+WacC+s9X8WdBkuZFH94Mgd1o2yXvFoZsbu4U1awNsgVpzKMsE7tSNScp2adz0JoU7oXqojiX90ED7m0bW+veEoVep+q6qc1kymA+mw9N42vEUOAN0i7ZD7SFtx2F9/yQGZt9egdr1NtLh6/pRw+wjyCjWQAGqW+dR4LKvZeoxejw3h3NOPt/lcImoEOPzrmNgZe6PXaTVG5NB9RmUuhM28DlofFP5z+8LraE4zvVxNn+Kw4QKKQWq+GelzAysM/94owvTA0=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2p:Status xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"><saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></saml2p:Status><saml2:Assertion ID="id92549195332235481708587333" IssueInstant="2023-06-16T06:42:44.371Z" Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/exk5qcxp4hc3aXlST697</saml2:Issuer><saml2:Subject><saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">hiroqn@herp.co.jp</saml2:NameID><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData NotOnOrAfter="2023-06-16T06:47:44.372Z" Recipient="https://panemagi.beta.ja-sore.de/authn/sso"/></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="2023-06-16T06:37:44.372Z" NotOnOrAfter="2023-06-16T06:47:44.372Z"><saml2:AudienceRestriction><saml2:Audience>panemagi.beta.ja-sore.de</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AuthnStatement AuthnInstant="2023-06-16T05:44:30.782Z" SessionIndex="id1686897764193.2050463806"><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement><saml2:Attribute Name="lastName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">netwalk</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="firstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">hiroqn</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="id" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">hiroqn@herp.co.jp</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">panemagi_access</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion></saml2p:Response>
+ tests/data/okta.crt view
@@ -0,0 +1,19 @@+-----BEGIN CERTIFICATE-----+MIIDoDCCAoigAwIBAgIGAYiKK3aGMA0GCSqGSIb3DQEBCwUAMIGQMQswCQYDVQQGEwJVUzETMBEG+A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU+MBIGA1UECwwLU1NPUHJvdmlkZXIxETAPBgNVBAMMCGhlcnAtaW5jMRwwGgYJKoZIhvcNAQkBFg1p+bmZvQG9rdGEuY29tMB4XDTIzMDYwNTA2MDcwNFoXDTMzMDYwNTA2MDgwNFowgZAxCzAJBgNVBAYT+AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQK+DARPa3RhMRQwEgYDVQQLDAtTU09Qcm92aWRlcjERMA8GA1UEAwwIaGVycC1pbmMxHDAaBgkqhkiG+9w0BCQEWDWluZm9Ab2t0YS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2YKQa+PDrssVNqBokKyT77wYUlXKkTnHNtbD1rdXhiIGTszmxmF/NuzLfS1TMvzqiMnpbAwswTnNMF6sx2+M/gl9tWpL6OF4MvCQf78LvzyTOKvghojJkpE65XbkB4HETpOKYlXhvwwbCG4rskMqtFEosM2dxY6+KWUPAJyL0Z9hpqavvq6Ct8nAjZxHCKFQGcYfCfMXxI55/+xYuetHHo4BTj417FGLvHBgJkgYsc//+KRPzC1rPkTjIGn8hlmnGfkZ7srp+UGrewhlPvj6rZVkrgQdL6PTqXvwbe7XHOKjt79vPfGZBp/jq+FRwKTO1fbvGWzF2/vIJFuR90p4a90x6pAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGOKuxgCynAU+YU5oX19FiXrITcj3/XmdWZ2yTF72T0a4edhiKM0E0adcywxplllihSQV75k90Z+fmVREHFU+WacC+s9X8WdBkuZFH94Mgd1o2yXvFoZsbu4U1awNsgVpzKMsE7tSNScp2adz0JoU7oXqojiX90ED7m0bW+veEoVep+q6qc1kymA+mw9N42vEUOAN0i7ZD7SFtx2F9/yQGZt9egdr1NtLh6/pRw+wjyCjWQAGqW+dR4LKvZeoxejw3h3NOPt/lcImoEOPzrmNgZe6PXaTVG5NB9RmUuhM28DlofFP5z+8LraE4zvVxNn+Kw4QKKQWq+GelzAysM/94owvTA0=+-----END CERTIFICATE-----
tests/data/okta.xml.expected view
@@ -10,22 +10,23 @@       MkStatusCode         { statusCodeValue = Success , statusCodeSubordinate = Nothing }   , responseSignature =-      Signature-        { signatureInfo =-            SignedInfo-              { signedInfoCanonicalisationMethod = C14N_EXC_1_0-              , signedInfoSignatureMethod = RSA_SHA256-              , signedInfoReference =-                  Reference-                    { referenceURI = "id36905492230634463108368061"-                    , referenceDigestMethod = DigestSHA256-                    , referenceDigestValue =-                        "i300U58mYoywmpgMkq7AHOplar4B6ZwW++Ri3PUvGlc="-                    }-              }-        , signatureValue =-            "eKymDiZIxFd+oJCIhdHQpI+s2nQYFBVH7TBoZaqXizJdNnNPpNlKM5wds33zP6a72GWOL/2n8WRtW+xSnPTmw5PumEuUhWbO2EizfU/SdBL4HKxxJt0nntkDRhdIoHBi+9Gy80So4NGr82B2clnhpj74GW0Ko8A0bt2sHzdQ9NaJ5ru4Qvx0Fk/UCBAGAYobryjAc9Tb5qxxgHGMmPUHG5CprRYTINKZlGF1Jl88VSdTbOYmGjJ4b6GY8pLR2qLt4LErVPUSI4vEUuPFU2f/9/i8D39hzWtJOCzuaUZ5tdKU3Fyjsgoa7ooDfZVzb4howWhQ9zPUgwjZEd9tbW2EwA=="-        }+      Just+        Signature+          { signatureInfo =+              SignedInfo+                { signedInfoCanonicalisationMethod = C14N_EXC_1_0+                , signedInfoSignatureMethod = RSA_SHA256+                , signedInfoReference =+                    Reference+                      { referenceURI = "id36905492230634463108368061"+                      , referenceDigestMethod = DigestSHA256+                      , referenceDigestValue =+                          "i300U58mYoywmpgMkq7AHOplar4B6ZwW++Ri3PUvGlc="+                      }+                }+          , signatureValue =+              "eKymDiZIxFd+oJCIhdHQpI+s2nQYFBVH7TBoZaqXizJdNnNPpNlKM5wds33zP6a72GWOL/2n8WRtW+xSnPTmw5PumEuUhWbO2EizfU/SdBL4HKxxJt0nntkDRhdIoHBi+9Gy80So4NGr82B2clnhpj74GW0Ko8A0bt2sHzdQ9NaJ5ru4Qvx0Fk/UCBAGAYobryjAc9Tb5qxxgHGMmPUHG5CprRYTINKZlGF1Jl88VSdTbOYmGjJ4b6GY8pLR2qLt4LErVPUSI4vEUuPFU2f/9/i8D39hzWtJOCzuaUZ5tdKU3Fyjsgoa7ooDfZVzb4howWhQ9zPUgwjZEd9tbW2EwA=="+          }   , responseAssertion = Nothing   , responseEncryptedAssertion =       Just
+ tests/spec.hs view
@@ -0,0 +1,12 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeApplications #-}+import Test.Tasty+import qualified Parser+import qualified Validation++main :: IO ()+main = defaultMain $ testGroup "wai-saml2 tests"+    [ Parser.tests+    , Validation.tests+    ]
wai-saml2.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.36.0.+-- This file has been generated from package.yaml by hpack version 0.38.1. -- -- see: https://github.com/sol/hpack  name:           wai-saml2-version:        0.6+version:        0.7.0 synopsis:       SAML2 assertion validation as WAI middleware description:    A Haskell library which implements SAML2 assertion validation as WAI middleware category:       Security@@ -20,10 +20,15 @@ extra-source-files:     README.md     CHANGELOG.md+    tests/data/azuread-signed-assertion.xml+    tests/data/azuread-signed-response.xml+    tests/data/azuread.crt     tests/data/google.xml     tests/data/google.xml.expected     tests/data/keycloak.xml     tests/data/keycloak.xml.expected+    tests/data/okta-attributes.xml+    tests/data/okta.crt     tests/data/okta.xml     tests/data/okta.xml.expected     tests/data/metadata/google.xml@@ -84,10 +89,12 @@     , zlib >=0.6.0.0 && <0.8   default-language: Haskell2010 -test-suite parser+test-suite wai-saml2-test   type: exitcode-stdio-1.0-  main-is: Parser.hs+  main-is: spec.hs   other-modules:+      Parser+      Validation       Paths_wai_saml2   hs-source-dirs:       tests@@ -113,9 +120,12 @@     , network-uri >=2.0 && <3     , pretty-show     , tasty+    , tasty-expected-failure     , tasty-golden+    , tasty-hunit     , text <2.2     , time >=1.9 && <2+    , transformers     , vault >=0.3 && <1     , wai >=3.0 && <4     , wai-extra >=3.0 && <4