037b53fb46b43898491ccb4e547f819811f25a0c
[friendica-addons.git/.git] / saml / vendor / onelogin / php-saml / src / Saml2 / Auth.php
1 <?php
2 /**
3  * This file is part of php-saml.
4  *
5  * (c) OneLogin Inc
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  *
10  * @package OneLogin
11  * @author  OneLogin Inc <saml-info@onelogin.com>
12  * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE
13  * @link    https://github.com/onelogin/php-saml
14  */
15
16 namespace OneLogin\Saml2;
17
18 use RobRichards\XMLSecLibs\XMLSecurityKey;
19
20 use Exception;
21
22 /**
23  * Main class of OneLogin's PHP Toolkit
24  */
25 class Auth
26 {
27     /**
28      * Settings data.
29      *
30      * @var Settings
31      */
32     private $_settings;
33
34     /**
35      * User attributes data.
36      *
37      * @var array
38      */
39     private $_attributes = array();
40
41     /**
42      * User attributes data with FriendlyName index.
43      *
44      * @var array
45      */
46     private $_attributesWithFriendlyName = array();
47
48     /**
49      * NameID
50      *
51      * @var string
52      */
53     private $_nameid;
54
55     /**
56      * NameID Format
57      *
58      * @var string
59      */
60     private $_nameidFormat;
61
62     /**
63      * NameID NameQualifier
64      *
65      * @var string
66      */
67     private $_nameidNameQualifier;
68
69     /**
70      * NameID SP NameQualifier
71      *
72      * @var string
73      */
74     private $_nameidSPNameQualifier;
75
76     /**
77      * If user is authenticated.
78      *
79      * @var bool
80      */
81     private $_authenticated = false;
82
83
84     /**
85      * SessionIndex. When the user is logged, this stored it
86      * from the AuthnStatement of the SAML Response
87      *
88      * @var string
89      */
90     private $_sessionIndex;
91
92     /**
93      * SessionNotOnOrAfter. When the user is logged, this stored it
94      * from the AuthnStatement of the SAML Response
95      *
96      * @var int|null
97      */
98     private $_sessionExpiration;
99
100     /**
101      * The ID of the last message processed
102      *
103      * @var string
104      */
105     private $_lastMessageId;
106
107     /**
108      * The ID of the last assertion processed
109      *
110      * @var string
111      */
112     private $_lastAssertionId;
113
114     /**
115      * The NotOnOrAfter value of the valid SubjectConfirmationData
116      * node (if any) of the last assertion processed
117      *
118      * @var int
119      */
120     private $_lastAssertionNotOnOrAfter;
121
122     /**
123      * If any error.
124      *
125      * @var array
126      */
127     private $_errors = array();
128
129     /**
130      * Last error object.
131      *
132      * @var Error|null
133      */
134     private $_lastErrorException;
135
136     /**
137      * Last error.
138      *
139      * @var string|null
140      */
141     private $_lastError;
142
143     /**
144      * Last AuthNRequest ID or LogoutRequest ID generated by this Service Provider
145      *
146      * @var string
147      */
148     private $_lastRequestID;
149
150     /**
151      * The most recently-constructed/processed XML SAML request
152      * (AuthNRequest, LogoutRequest)
153      *
154      * @var string
155      */
156     private $_lastRequest;
157
158     /**
159      * The most recently-constructed/processed XML SAML response
160      * (SAMLResponse, LogoutResponse). If the SAMLResponse was
161      * encrypted, by default tries to return the decrypted XML
162      *
163      * @var string|\DomDocument|null
164      */
165     private $_lastResponse;
166
167     /**
168      * Initializes the SP SAML instance.
169      *
170      * @param array|null $settings Setting data
171      *
172      * @throws Exception
173      * @throws Error
174      */
175     public function __construct(array $settings = null)
176     {
177         $this->_settings = new Settings($settings);
178     }
179
180     /**
181      * Returns the settings info
182      *
183      * @return Settings The settings data.
184      */
185     public function getSettings()
186     {
187         return $this->_settings;
188     }
189
190     /**
191      * Set the strict mode active/disable
192      *
193      * @param bool $value Strict parameter
194      *
195      * @throws Error
196      */
197     public function setStrict($value)
198     {
199         if (!is_bool($value)) {
200             throw new Error(
201                 'Invalid value passed to setStrict()',
202                 Error::SETTINGS_INVALID_SYNTAX
203             );
204         }
205
206         $this->_settings->setStrict($value);
207     }
208
209     /**
210      * Set schemas path
211      *
212      * @param string $path
213      * @return $this
214      */
215     public function setSchemasPath($path)
216     {
217         $this->_paths['schemas'] = $path;
218     }
219
220     /**
221      * Process the SAML Response sent by the IdP.
222      *
223      * @param string|null $requestId The ID of the AuthNRequest sent by this SP to the IdP
224      *
225      * @throws Error
226      * @throws ValidationError
227      */
228     public function processResponse($requestId = null)
229     {
230         $this->_errors = array();
231         $this->_lastError = $this->_lastErrorException = null;
232         if (isset($_POST['SAMLResponse'])) {
233             // AuthnResponse -- HTTP_POST Binding
234             $response = new Response($this->_settings, $_POST['SAMLResponse']);
235             $this->_lastResponse = $response->getXMLDocument();
236
237             if ($response->isValid($requestId)) {
238                 $this->_attributes = $response->getAttributes();
239                 $this->_attributesWithFriendlyName = $response->getAttributesWithFriendlyName();
240                 $this->_nameid = $response->getNameId();
241                 $this->_nameidFormat = $response->getNameIdFormat();
242                 $this->_nameidNameQualifier = $response->getNameIdNameQualifier();
243                 $this->_nameidSPNameQualifier = $response->getNameIdSPNameQualifier();
244                 $this->_authenticated = true;
245                 $this->_sessionIndex = $response->getSessionIndex();
246                 $this->_sessionExpiration = $response->getSessionNotOnOrAfter();
247                 $this->_lastMessageId = $response->getId();
248                 $this->_lastAssertionId = $response->getAssertionId();
249                 $this->_lastAssertionNotOnOrAfter = $response->getAssertionNotOnOrAfter();
250             } else {
251                 $this->_errors[] = 'invalid_response';
252                 $this->_lastErrorException = $response->getErrorException();
253                 $this->_lastError = $response->getError();
254                 $this->_errors[] = $this->_lastError;
255             }
256         } else {
257             $this->_errors[] = 'invalid_binding';
258             throw new Error(
259                 'SAML Response not found, Only supported HTTP_POST Binding',
260                 Error::SAML_RESPONSE_NOT_FOUND
261             );
262         }
263     }
264
265     /**
266      * Process the SAML Logout Response / Logout Request sent by the IdP.
267      *
268      * @param bool        $keepLocalSession             When false will destroy the local session, otherwise will keep it
269      * @param string|null $requestId                    The ID of the LogoutRequest sent by this SP to the IdP
270      * @param bool        $retrieveParametersFromServer True if we want to use parameters from $_SERVER to validate the signature
271      * @param callable    $cbDeleteSession              Callback to be executed to delete session
272      * @param bool        $stay                         True if we want to stay (returns the url string) False to redirect
273      *
274      * @return string|null
275      *
276      * @throws Error
277      */
278     public function processSLO($keepLocalSession = false, $requestId = null, $retrieveParametersFromServer = false, $cbDeleteSession = null, $stay = false)
279     {
280         $this->_errors = array();
281         $this->_lastError = $this->_lastErrorException = null;
282         if (isset($_GET['SAMLResponse'])) {
283             $logoutResponse = new LogoutResponse($this->_settings, $_GET['SAMLResponse']);
284             $this->_lastResponse = $logoutResponse->getXML();
285             if (!$logoutResponse->isValid($requestId, $retrieveParametersFromServer)) {
286                 $this->_errors[] = 'invalid_logout_response';
287                 $this->_lastErrorException = $logoutResponse->getErrorException();
288                 $this->_lastError = $logoutResponse->getError();
289
290             } else if ($logoutResponse->getStatus() !== Constants::STATUS_SUCCESS) {
291                 $this->_errors[] = 'logout_not_success';
292             } else {
293                 $this->_lastMessageId = $logoutResponse->id;
294                 if (!$keepLocalSession) {
295                     if ($cbDeleteSession === null) {
296                         Utils::deleteLocalSession();
297                     } else {
298                         call_user_func($cbDeleteSession);
299                     }
300                 }
301             }
302         } else if (isset($_GET['SAMLRequest'])) {
303             $logoutRequest = new LogoutRequest($this->_settings, $_GET['SAMLRequest']);
304             $this->_lastRequest = $logoutRequest->getXML();
305             if (!$logoutRequest->isValid($retrieveParametersFromServer)) {
306                 $this->_errors[] = 'invalid_logout_request';
307                 $this->_lastErrorException = $logoutRequest->getErrorException();
308                 $this->_lastError = $logoutRequest->getError();
309             } else {
310                 if (!$keepLocalSession) {
311                     if ($cbDeleteSession === null) {
312                         Utils::deleteLocalSession();
313                     } else {
314                         call_user_func($cbDeleteSession);
315                     }
316                 }
317                 $inResponseTo = $logoutRequest->id;
318                 $this->_lastMessageId = $logoutRequest->id;
319                 $responseBuilder = new LogoutResponse($this->_settings);
320                 $responseBuilder->build($inResponseTo);
321                 $this->_lastResponse = $responseBuilder->getXML();
322
323                 $logoutResponse = $responseBuilder->getResponse();
324
325                 $parameters = array('SAMLResponse' => $logoutResponse);
326                 if (isset($_GET['RelayState'])) {
327                     $parameters['RelayState'] = $_GET['RelayState'];
328                 }
329
330                 $security = $this->_settings->getSecurityData();
331                 if (isset($security['logoutResponseSigned']) && $security['logoutResponseSigned']) {
332                     $signature = $this->buildResponseSignature($logoutResponse, isset($parameters['RelayState'])? $parameters['RelayState']: null, $security['signatureAlgorithm']);
333                     $parameters['SigAlg'] = $security['signatureAlgorithm'];
334                     $parameters['Signature'] = $signature;
335                 }
336
337                 return $this->redirectTo($this->getSLOResponseUrl(), $parameters, $stay);
338             }
339         } else {
340             $this->_errors[] = 'invalid_binding';
341             throw new Error(
342                 'SAML LogoutRequest/LogoutResponse not found. Only supported HTTP_REDIRECT Binding',
343                 Error::SAML_LOGOUTMESSAGE_NOT_FOUND
344             );
345         }
346     }
347
348     /**
349      * Redirects the user to the url past by parameter
350      * or to the url that we defined in our SSO Request.
351      *
352      * @param string $url        The target URL to redirect the user.
353      * @param array  $parameters Extra parameters to be passed as part of the url
354      * @param bool   $stay       True if we want to stay (returns the url string) False to redirect
355      *
356      * @return string|null
357      */
358     public function redirectTo($url = '', array $parameters = array(), $stay = false)
359     {
360         assert(is_string($url));
361
362         if (empty($url) && isset($_REQUEST['RelayState'])) {
363             $url = $_REQUEST['RelayState'];
364         }
365
366         return Utils::redirect($url, $parameters, $stay);
367     }
368
369     /**
370      * Checks if the user is authenticated or not.
371      *
372      * @return bool  True if the user is authenticated
373      */
374     public function isAuthenticated()
375     {
376         return $this->_authenticated;
377     }
378
379     /**
380      * Returns the set of SAML attributes.
381      *
382      * @return array  Attributes of the user.
383      */
384     public function getAttributes()
385     {
386         return $this->_attributes;
387     }
388
389
390     /**
391      * Returns the set of SAML attributes indexed by FriendlyName
392      *
393      * @return array  Attributes of the user.
394      */
395     public function getAttributesWithFriendlyName()
396     {
397         return $this->_attributesWithFriendlyName;
398     }
399
400     /**
401      * Returns the nameID
402      *
403      * @return string  The nameID of the assertion
404      */
405     public function getNameId()
406     {
407         return $this->_nameid;
408     }
409
410     /**
411      * Returns the nameID Format
412      *
413      * @return string  The nameID Format of the assertion
414      */
415     public function getNameIdFormat()
416     {
417         return $this->_nameidFormat;
418     }
419
420     /**
421      * Returns the nameID NameQualifier
422      *
423      * @return string  The nameID NameQualifier of the assertion
424      */
425     public function getNameIdNameQualifier()
426     {
427         return $this->_nameidNameQualifier;
428     }
429
430     /**
431      * Returns the nameID SP NameQualifier
432      *
433      * @return string  The nameID SP NameQualifier of the assertion
434      */
435     public function getNameIdSPNameQualifier()
436     {
437         return $this->_nameidSPNameQualifier;
438     }
439
440     /**
441      * Returns the SessionIndex
442      *
443      * @return string|null  The SessionIndex of the assertion
444      */
445     public function getSessionIndex()
446     {
447         return $this->_sessionIndex;
448     }
449
450     /**
451      * Returns the SessionNotOnOrAfter
452      *
453      * @return int|null  The SessionNotOnOrAfter of the assertion
454      */
455     public function getSessionExpiration()
456     {
457         return $this->_sessionExpiration;
458     }
459
460     /**
461      * Returns if there were any error
462      *
463      * @return array  Errors
464      */
465     public function getErrors()
466     {
467         return $this->_errors;
468     }
469
470     /**
471      * Returns the reason for the last error
472      *
473      * @return string|null  Error reason
474      */
475     public function getLastErrorReason()
476     {
477         return $this->_lastError;
478     }
479
480
481     /**
482      * Returns the last error
483      *
484      * @return Exception|null Error
485      */
486     public function getLastErrorException()
487     {
488         return $this->_lastErrorException;
489     }
490
491     /**
492      * Returns the requested SAML attribute
493      *
494      * @param string $name The requested attribute of the user.
495      *
496      * @return array|null Requested SAML attribute ($name).
497      */
498     public function getAttribute($name)
499     {
500         assert(is_string($name));
501
502         $value = null;
503         if (isset($this->_attributes[$name])) {
504             return $this->_attributes[$name];
505         }
506         return $value;
507     }
508
509     /**
510      * Returns the requested SAML attribute indexed by FriendlyName
511      *
512      * @param string $friendlyName The requested attribute of the user.
513      *
514      * @return array|null Requested SAML attribute ($friendlyName).
515      */
516     public function getAttributeWithFriendlyName($friendlyName)
517     {
518         assert(is_string($friendlyName));
519         $value = null;
520         if (isset($this->_attributesWithFriendlyName[$friendlyName])) {
521             return $this->_attributesWithFriendlyName[$friendlyName];
522         }
523         return $value;
524     }
525
526     /**
527      * Initiates the SSO process.
528      *
529      * @param string|null $returnTo        The target URL the user should be returned to after login.
530      * @param array       $parameters      Extra parameters to be added to the GET
531      * @param bool        $forceAuthn      When true the AuthNRequest will set the ForceAuthn='true'
532      * @param bool        $isPassive       When true the AuthNRequest will set the Ispassive='true'
533      * @param bool        $stay            True if we want to stay (returns the url string) False to redirect
534      * @param bool        $setNameIdPolicy When true the AuthNRequest will set a nameIdPolicy element
535      * @param string      $nameIdValueReq  Indicates to the IdP the subject that should be authenticated
536      *
537      * @return string|null If $stay is True, it return a string with the SLO URL + LogoutRequest + parameters
538      *
539      * @throws Error
540      */
541     public function login($returnTo = null, array $parameters = array(), $forceAuthn = false, $isPassive = false, $stay = false, $setNameIdPolicy = true, $nameIdValueReq = null)
542     {
543         $authnRequest = $this->buildAuthnRequest($this->_settings, $forceAuthn, $isPassive, $setNameIdPolicy, $nameIdValueReq);
544
545         $this->_lastRequest = $authnRequest->getXML();
546         $this->_lastRequestID = $authnRequest->getId();
547
548         $samlRequest = $authnRequest->getRequest();
549         $parameters['SAMLRequest'] = $samlRequest;
550
551         if (!empty($returnTo)) {
552             $parameters['RelayState'] = $returnTo;
553         } else {
554             $parameters['RelayState'] = Utils::getSelfRoutedURLNoQuery();
555         }
556
557         $security = $this->_settings->getSecurityData();
558         if (isset($security['authnRequestsSigned']) && $security['authnRequestsSigned']) {
559             $signature = $this->buildRequestSignature($samlRequest, $parameters['RelayState'], $security['signatureAlgorithm']);
560             $parameters['SigAlg'] = $security['signatureAlgorithm'];
561             $parameters['Signature'] = $signature;
562         }
563         return $this->redirectTo($this->getSSOurl(), $parameters, $stay);
564     }
565
566     /**
567      * Initiates the SLO process.
568      *
569      * @param string|null $returnTo            The target URL the user should be returned to after logout.
570      * @param array       $parameters          Extra parameters to be added to the GET
571      * @param string|null $nameId              The NameID that will be set in the LogoutRequest.
572      * @param string|null $sessionIndex        The SessionIndex (taken from the SAML Response in the SSO process).
573      * @param bool        $stay                True if we want to stay (returns the url string) False to redirect
574      * @param string|null $nameIdFormat        The NameID Format will be set in the LogoutRequest.
575      * @param string|null $nameIdNameQualifier The NameID NameQualifier will be set in the LogoutRequest.
576      *
577      * @return string|null If $stay is True, it return a string with the SLO URL + LogoutRequest + parameters
578      *
579      * @throws Error
580      */
581     public function logout($returnTo = null, array $parameters = array(), $nameId = null, $sessionIndex = null, $stay = false, $nameIdFormat = null, $nameIdNameQualifier = null, $nameIdSPNameQualifier = null)
582     {
583         $sloUrl = $this->getSLOurl();
584         if (empty($sloUrl)) {
585             throw new Error(
586                 'The IdP does not support Single Log Out',
587                 Error::SAML_SINGLE_LOGOUT_NOT_SUPPORTED
588             );
589         }
590
591         if (empty($nameId) && !empty($this->_nameid)) {
592             $nameId = $this->_nameid;
593         }
594         if (empty($nameIdFormat) && !empty($this->_nameidFormat)) {
595             $nameIdFormat = $this->_nameidFormat;
596         }
597
598         $logoutRequest = new LogoutRequest($this->_settings, null, $nameId, $sessionIndex, $nameIdFormat, $nameIdNameQualifier, $nameIdSPNameQualifier);
599
600         $this->_lastRequest = $logoutRequest->getXML();
601         $this->_lastRequestID = $logoutRequest->id;
602
603         $samlRequest = $logoutRequest->getRequest();
604
605         $parameters['SAMLRequest'] = $samlRequest;
606         if (!empty($returnTo)) {
607             $parameters['RelayState'] = $returnTo;
608         } else {
609             $parameters['RelayState'] = Utils::getSelfRoutedURLNoQuery();
610         }
611
612         $security = $this->_settings->getSecurityData();
613         if (isset($security['logoutRequestSigned']) && $security['logoutRequestSigned']) {
614             $signature = $this->buildRequestSignature($samlRequest, $parameters['RelayState'], $security['signatureAlgorithm']);
615             $parameters['SigAlg'] = $security['signatureAlgorithm'];
616             $parameters['Signature'] = $signature;
617         }
618
619         return $this->redirectTo($sloUrl, $parameters, $stay);
620     }
621
622    /**
623      * Gets the IdP SSO url.
624      *
625      * @return string The url of the IdP Single Sign On Service
626      */
627     public function getSSOurl()
628     {
629         return $this->_settings->getIdPSSOUrl();
630     }
631
632     /**
633      * Gets the IdP SLO url.
634      *
635      * @return string|null The url of the IdP Single Logout Service
636      */
637     public function getSLOurl()
638     {
639         return $this->_settings->getIdPSLOUrl();
640     }
641
642     /**
643      * Gets the IdP SLO response url.
644      *
645      * @return string|null The response url of the IdP Single Logout Service
646      */
647     public function getSLOResponseUrl()
648     {
649         return $this->_settings->getIdPSLOResponseUrl();
650     }
651
652
653     /**
654      * Gets the ID of the last AuthNRequest or LogoutRequest generated by the Service Provider.
655      *
656      * @return string The ID of the Request SAML message.
657      */
658     public function getLastRequestID()
659     {
660         return $this->_lastRequestID;
661     }
662
663     /**
664      * Creates an AuthnRequest
665      *
666      * @param Settings $settings        Setting data
667      * @param bool     $forceAuthn      When true the AuthNRequest will set the ForceAuthn='true'
668      * @param bool     $isPassive       When true the AuthNRequest will set the Ispassive='true'
669      * @param bool     $setNameIdPolicy When true the AuthNRequest will set a nameIdPolicy element
670      * @param string   $nameIdValueReq  Indicates to the IdP the subject that should be authenticated
671      *
672      * @return AuthnRequest The AuthnRequest object
673      */
674     public function buildAuthnRequest($settings, $forceAuthn, $isPassive, $setNameIdPolicy, $nameIdValueReq = null)
675     {
676         return new AuthnRequest($settings, $forceAuthn, $isPassive, $setNameIdPolicy, $nameIdValueReq);
677     }
678
679     /**
680      * Generates the Signature for a SAML Request
681      *
682      * @param string $samlRequest   The SAML Request
683      * @param string $relayState    The RelayState
684      * @param string $signAlgorithm Signature algorithm method
685      *
686      * @return string A base64 encoded signature
687      *
688      * @throws Exception
689      * @throws Error
690      */
691     public function buildRequestSignature($samlRequest, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA256)
692     {
693         return $this->buildMessageSignature($samlRequest, $relayState, $signAlgorithm, "SAMLRequest");
694     }
695
696     /**
697      * Generates the Signature for a SAML Response
698      *
699      * @param string $samlResponse  The SAML Response
700      * @param string $relayState    The RelayState
701      * @param string $signAlgorithm Signature algorithm method
702      *
703      * @return string A base64 encoded signature
704      *
705      * @throws Exception
706      * @throws Error
707      */
708     public function buildResponseSignature($samlResponse, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA256)
709     {
710         return $this->buildMessageSignature($samlResponse, $relayState, $signAlgorithm, "SAMLResponse");
711     }
712
713     /**
714      * Generates the Signature for a SAML Message
715      *
716      * @param string $samlMessage   The SAML Message
717      * @param string $relayState    The RelayState
718      * @param string $signAlgorithm Signature algorithm method
719      * @param string $type          "SAMLRequest" or "SAMLResponse"
720      *
721      * @return string A base64 encoded signature
722      *
723      * @throws Exception
724      * @throws Error
725      */
726     private function buildMessageSignature($samlMessage, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA256, $type = "SAMLRequest")
727     {
728         $key = $this->_settings->getSPkey();
729         if (empty($key)) {
730             if ($type == "SAMLRequest") {
731                 $errorMsg = "Trying to sign the SAML Request but can't load the SP private key";
732             } else {
733                 $errorMsg = "Trying to sign the SAML Response but can't load the SP private key";
734             }
735
736             throw new Error($errorMsg, Error::PRIVATE_KEY_NOT_FOUND);
737         }
738
739         $objKey = new XMLSecurityKey($signAlgorithm, array('type' => 'private'));
740         $objKey->loadKey($key, false);
741
742         $security = $this->_settings->getSecurityData();
743         if ($security['lowercaseUrlencoding']) {
744             $msg = $type.'='.rawurlencode($samlMessage);
745             if (isset($relayState)) {
746                 $msg .= '&RelayState='.rawurlencode($relayState);
747             }
748             $msg .= '&SigAlg=' . rawurlencode($signAlgorithm);
749         } else {
750             $msg = $type.'='.urlencode($samlMessage);
751             if (isset($relayState)) {
752                 $msg .= '&RelayState='.urlencode($relayState);
753             }
754             $msg .= '&SigAlg=' . urlencode($signAlgorithm);
755         }
756         $signature = $objKey->signData($msg);
757         return base64_encode($signature);
758     }
759
760     /**
761      * @return string The ID of the last message processed
762      */
763     public function getLastMessageId()
764     {
765         return $this->_lastMessageId;
766     }
767
768     /**
769      * @return string The ID of the last assertion processed
770      */
771     public function getLastAssertionId()
772     {
773         return $this->_lastAssertionId;
774     }
775
776     /**
777      * @return int The NotOnOrAfter value of the valid
778      *         SubjectConfirmationData node (if any)
779      *         of the last assertion processed
780      */
781     public function getLastAssertionNotOnOrAfter()
782     {
783         return $this->_lastAssertionNotOnOrAfter;
784     }
785
786     /**
787      * Returns the most recently-constructed/processed
788      * XML SAML request (AuthNRequest, LogoutRequest)
789      *
790      * @return string|null The Request XML
791      */
792     public function getLastRequestXML()
793     {
794         return $this->_lastRequest;
795     }
796
797     /**
798      * Returns the most recently-constructed/processed
799      * XML SAML response (SAMLResponse, LogoutResponse).
800      * If the SAMLResponse was encrypted, by default tries
801      * to return the decrypted XML.
802      *
803      * @return string|null The Response XML
804      */
805     public function getLastResponseXML()
806     {
807         $response = null;
808         if (isset($this->_lastResponse)) {
809             if (is_string($this->_lastResponse)) {
810                 $response = $this->_lastResponse;
811             } else {
812                 $response = $this->_lastResponse->saveXML();
813             }
814         }
815
816         return $response;
817     }
818 }