Skip to content

Commit 6873c32

Browse files
committed
Adjusted acs endpoint to extract NameQualifier and SPNameQualifier from SAMLResponse. Adjusted single logout service to provide NameQualifier and SPNameQualifier to logout method. Add getNameIdNameQualifier to Auth and SamlResponse. Extend logout method from Auth and LogoutRequest constructor to support SPNameQualifier parameter. Align LogoutRequest constructor with SAML specs"
1 parent a7e0835 commit 6873c32

9 files changed

Lines changed: 204 additions & 13 deletions

File tree

README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,8 @@ if (!$auth->isAuthenticated()) {
705705
$_SESSION['samlUserdata'] = $auth->getAttributes();
706706
$_SESSION['samlNameId'] = $auth->getNameId();
707707
$_SESSION['samlNameIdFormat'] = $auth->getNameIdFormat();
708+
$_SESSION['samlNameidNameQualifier' = $auth->getNameIdNameQualifier();
709+
$_SESSION['samlNameidSPNameQualifier' = $auth->getNameIdSPNameQualifier();
708710
$_SESSION['samlSessionIndex'] = $auth->getSessionIndex();
709711

710712
if (isset($_POST['RelayState']) && OneLogin\Saml2\Utils::getSelfURL() != $_POST['RelayState']) {
@@ -932,14 +934,16 @@ $auth = new OneLogin\Saml2\Auth();
932934
$auth->logout(); // Method that sent the Logout Request.
933935
```
934936

935-
Also there are six optional parameters that can be set:
937+
Also there are eight optional parameters that can be set:
936938
* `$returnTo` - The target URL the user should be returned to after logout.
937939
* `$parameters` - Extra parameters to be added to the GET.
938940
* `$name_id` - That will be used to build the LogoutRequest. If `name_id` parameter is not set and the auth object processed a
939941
SAML Response with a `NameId`, then this `NameId` will be used.
940942
* `$session_index` - SessionIndex that identifies the session of the user.
941943
* `$stay` - True if we want to stay (returns the url string) False to redirect.
942944
* `$nameIdFormat` - The NameID Format will be set in the LogoutRequest.
945+
* `$nameIdNameQualifier` - The NameID NameQualifier will be set in the LogoutRequest.
946+
* `$nameIdSPNameQualifier` - The NameID SP NameQualifier will be set in the LogoutRequest.
943947

944948
The Logout Request will be sent signed or unsigned based on the security
945949
info of the `advanced_settings.php` (`'logoutRequestSigned'`).
@@ -966,6 +970,9 @@ $paramters = array();
966970
$nameId = null;
967971
$sessionIndex = null;
968972
$nameIdFormat = null;
973+
$nameIdNameQualifier = null;
974+
$nameIdSPNameQualifier = null;
975+
969976
if (isset($_SESSION['samlNameId'])) {
970977
$nameId = $_SESSION['samlNameId'];
971978
}
@@ -975,7 +982,13 @@ if (isset($_SESSION['samlSessionIndex'])) {
975982
if (isset($_SESSION['samlNameIdFormat'])) {
976983
$nameIdFormat = $_SESSION['samlNameIdFormat'];
977984
}
978-
$auth->logout($returnTo, $paramters, $nameId, $sessionIndex, false, $nameIdFormat);
985+
if (isset($_SESSION['samlNameIdNameQualifier'])) {
986+
$nameIdNameQualifier = $_SESSION['samlNameIdNameQualifier'];
987+
}
988+
if (isset($_SESSION['samlNameIdSPNameQualifier'])) {
989+
$nameIdSPNameQualifier = $_SESSION['samlNameIdSPNameQualifier'];
990+
}
991+
$auth->logout($returnTo, $paramters, $nameId, $sessionIndex, false, $nameIdFormat, $nameIdNameQualifier, $nameIdSPNameQualifier);
979992
```
980993

981994
If a match on the future LogoutResponse ID and the LogoutRequest ID to be sent is required, that LogoutRequest ID must to be extracted and stored.
@@ -1152,6 +1165,9 @@ Main class of OneLogin PHP Toolkit
11521165
* `getAttributes` - Returns the set of SAML attributes.
11531166
* `getAttribute` - Returns the requested SAML attribute
11541167
* `getNameId` - Returns the nameID
1168+
* `getNameIdFormat` - Gets the NameID Format provided by the SAML response from the IdP.
1169+
* `getNameIdNameQualifier` - Gets the NameID NameQualifier provided from the SAML Response String.
1170+
* `getNameIdNameSPQualifier` - Gets the NameID SP NameQualifier provided from the SAML Response String.
11551171
* `getSessionIndex` - Gets the SessionIndex from the AuthnStatement.
11561172
* `getErrors` - Returns if there were any error
11571173
* `getSSOurl` - Gets the SSO url.
@@ -1188,6 +1204,8 @@ SAML 2 Authentication Response class
11881204
IdP.
11891205
* `getNameId` - Gets the NameID provided by the SAML response from the IdP.
11901206
* `getNameIdFormat` - Gets the NameID Format provided by the SAML response from the IdP.
1207+
* `getNameIdNameQualifier` - Gets the NameID NameQualifier provided from the SAML Response String.
1208+
* `getNameIdNameSPQualifier` - Gets the NameID SP NameQualifier provided from the SAML Response String.
11911209
* `getSessionNotOnOrAfter` - Gets the SessionNotOnOrAfter from the
11921210
AuthnStatement
11931211
* `getSessionIndex` - Gets the SessionIndex from the AuthnStatement.

demo1/index.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,26 @@
3535
$nameId = null;
3636
$sessionIndex = null;
3737
$nameIdFormat = null;
38+
$nameIdNameQualifier = null;
39+
$nameIdSPNameQualifier = null;
3840

3941
if (isset($_SESSION['samlNameId'])) {
4042
$nameId = $_SESSION['samlNameId'];
4143
}
42-
if (isset($_SESSION['samlSessionIndex'])) {
43-
$sessionIndex = $_SESSION['samlSessionIndex'];
44-
}
4544
if (isset($_SESSION['samlNameIdFormat'])) {
4645
$nameIdFormat = $_SESSION['samlNameIdFormat'];
4746
}
47+
if (isset($_SESSION['samlNameIdNameQualifier'])) {
48+
$nameIdNameQualifier = $_SESSION['samlNameIdNameQualifier'];
49+
}
50+
if (isset($_SESSION['samlNameIdSPNameQualifier'])) {
51+
$nameIdSPNameQualifier = $_SESSION['samlNameIdSPNameQualifier'];
52+
}
53+
if (isset($_SESSION['samlSessionIndex'])) {
54+
$sessionIndex = $_SESSION['samlSessionIndex'];
55+
}
4856

49-
$auth->logout($returnTo, $paramters, $nameId, $sessionIndex, false, $nameIdFormat);
57+
$auth->logout($returnTo, $paramters, $nameId, $sessionIndex, false, $nameIdFormat, $nameIdNameQualifier, $nameIdSPNameQualifier);
5058

5159
# If LogoutRequest ID need to be saved in order to later validate it, do instead
5260
# $sloBuiltUrl = $auth->logout(null, $paramters, $nameId, $sessionIndex, true);
@@ -79,7 +87,10 @@
7987
$_SESSION['samlUserdata'] = $auth->getAttributes();
8088
$_SESSION['samlNameId'] = $auth->getNameId();
8189
$_SESSION['samlNameIdFormat'] = $auth->getNameIdFormat();
90+
$_SESSION['samlNameIdNameQualifier'] = $auth->getNameIdNameQualifier();
91+
$_SESSION['samlNameIdSPNameQualifier'] = $auth->getNameIdSPNameQualifier();
8292
$_SESSION['samlSessionIndex'] = $auth->getSessionIndex();
93+
8394
unset($_SESSION['AuthNRequestID']);
8495
if (isset($_POST['RelayState']) && Utils::getSelfURL() != $_POST['RelayState']) {
8596
$auth->redirectTo($_POST['RelayState']);

src/Saml2/Auth.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ class Auth
6666
*/
6767
private $_nameidNameQualifier;
6868

69+
/**
70+
* NameID SP NameQualifier
71+
*
72+
* @var string
73+
*/
74+
private $_nameidSPNameQualifier;
75+
6976
/**
7077
* If user is authenticated.
7178
*
@@ -222,6 +229,7 @@ public function processResponse($requestId = null)
222229
$this->_nameid = $response->getNameId();
223230
$this->_nameidFormat = $response->getNameIdFormat();
224231
$this->_nameidNameQualifier = $response->getNameIdNameQualifier();
232+
$this->_nameidSPNameQualifier = $response->getNameIdSPNameQualifier();
225233
$this->_authenticated = true;
226234
$this->_sessionIndex = $response->getSessionIndex();
227235
$this->_sessionExpiration = $response->getSessionNotOnOrAfter();
@@ -407,6 +415,16 @@ public function getNameIdNameQualifier()
407415
return $this->_nameidNameQualifier;
408416
}
409417

418+
/**
419+
* Returns the nameID SP NameQualifier
420+
*
421+
* @return string The nameID SP NameQualifier of the assertion
422+
*/
423+
public function getNameIdSPNameQualifier()
424+
{
425+
return $this->_nameidSPNameQualifier;
426+
}
427+
410428
/**
411429
* Returns the SessionIndex
412430
*
@@ -547,7 +565,7 @@ public function login($returnTo = null, array $parameters = array(), $forceAuthn
547565
*
548566
* @throws Error
549567
*/
550-
public function logout($returnTo = null, array $parameters = array(), $nameId = null, $sessionIndex = null, $stay = false, $nameIdFormat = null, $nameIdNameQualifier = null)
568+
public function logout($returnTo = null, array $parameters = array(), $nameId = null, $sessionIndex = null, $stay = false, $nameIdFormat = null, $nameIdNameQualifier = null, $nameIdSPNameQualifier = null)
551569
{
552570
$sloUrl = $this->getSLOurl();
553571
if (empty($sloUrl)) {
@@ -564,7 +582,7 @@ public function logout($returnTo = null, array $parameters = array(), $nameId =
564582
$nameIdFormat = $this->_nameidFormat;
565583
}
566584

567-
$logoutRequest = new LogoutRequest($this->_settings, null, $nameId, $sessionIndex, $nameIdFormat, $nameIdNameQualifier);
585+
$logoutRequest = new LogoutRequest($this->_settings, null, $nameId, $sessionIndex, $nameIdFormat, $nameIdNameQualifier, $nameIdSPNameQualifier);
568586

569587
$this->_lastRequest = $logoutRequest->getXML();
570588
$this->_lastRequestID = $logoutRequest->id;

src/Saml2/LogoutRequest.php

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ class LogoutRequest
6262
* @param string|null $sessionIndex The SessionIndex (taken from the SAML Response in the SSO process).
6363
* @param string|null $nameIdFormat The NameID Format will be set in the LogoutRequest.
6464
* @param string|null $nameIdNameQualifier The NameID NameQualifier will be set in the LogoutRequest.
65+
* @param string|null $nameIdSPNameQualifier The NameID SP NameQualifier will be set in the LogoutRequest.
6566
*/
66-
public function __construct(\OneLogin\Saml2\Settings $settings, $request = null, $nameId = null, $sessionIndex = null, $nameIdFormat = null, $nameIdNameQualifier = null)
67+
public function __construct(\OneLogin\Saml2\Settings $settings, $request = null, $nameId = null, $sessionIndex = null, $nameIdFormat = null, $nameIdNameQualifier = null, $nameIdSPNameQualifier = null)
6768
{
6869
$this->_settings = $settings;
6970

@@ -80,7 +81,6 @@ public function __construct(\OneLogin\Saml2\Settings $settings, $request = null,
8081
$id = Utils::generateUniqueID();
8182
$this->id = $id;
8283

83-
$nameIdValue = Utils::generateUniqueID();
8484
$issueInstant = Utils::parseTime2SAML(time());
8585

8686
$cert = null;
@@ -99,16 +99,27 @@ public function __construct(\OneLogin\Saml2\Settings $settings, $request = null,
9999
&& $spData['NameIDFormat'] != Constants::NAMEID_UNSPECIFIED) {
100100
$nameIdFormat = $spData['NameIDFormat'];
101101
}
102-
$spNameQualifier = null;
103102
} else {
104103
$nameId = $idpData['entityId'];
105104
$nameIdFormat = Constants::NAMEID_ENTITY;
106-
$spNameQualifier = $spData['entityId'];
105+
}
106+
107+
/* From saml-core-2.0-os 8.3.6, when the entity Format is used:
108+
"The NameQualifier, SPNameQualifier, and SPProvidedID attributes MUST be omitted.
109+
*/
110+
if (!empty($nameIdFormat) && $nameIdFormat == Constants::NAMEID_ENTITY) {
111+
$nameIdNameQualifier = null;
112+
$nameIdSPNameQualifier = null;
113+
}
114+
115+
// NameID Format UNSPECIFIED omitted
116+
if (!empty($nameIdFormat) && $nameIdFormat == Constants::NAMEID_UNSPECIFIED) {
117+
$nameIdFormat = null;
107118
}
108119

109120
$nameIdObj = Utils::generateNameId(
110121
$nameId,
111-
$spNameQualifier,
122+
$nameIdSPNameQualifier,
112123
$nameIdFormat,
113124
$cert,
114125
$nameIdNameQualifier

src/Saml2/Response.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,23 @@ public function getNameIdNameQualifier()
700700
return $nameIdNameQualifier;
701701
}
702702

703+
/**
704+
* Gets the NameID SP NameQualifier provided by the SAML response from the IdP.
705+
*
706+
* @return string|null NameID SP NameQualifier
707+
*
708+
* @throws ValidationError
709+
*/
710+
public function getNameIdSPNameQualifier()
711+
{
712+
$nameIdSPNameQualifier = null;
713+
$nameIdData = $this->getNameIdData();
714+
if (!empty($nameIdData) && isset($nameIdData['SPNameQualifier'])) {
715+
$nameIdSPNameQualifier = $nameIdData['SPNameQualifier'];
716+
}
717+
return $nameIdSPNameQualifier;
718+
}
719+
703720
/**
704721
* Gets the SessionNotOnOrAfter from the AuthnStatement.
705722
* Could be used to set the local session expiration
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeDhmZWI5YWNkLTFlODYtYWMxMi05MDIzLTEzYjg0NDc5YjI1YiIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDItMTlUMDE6Mzc6MDFaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9waXRidWxrLm5vLWlwLm9yZy9uZXdvbmVsb2dpbi9kZW1vMS9pbmRleC5waHA/YWNzIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzVmZTlkNmU0OTliMmYwOTEzMjA2YWFiM2Y3MTkxNzI5MDQ5YmI4MDciPjxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+DQogIDxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+DQogICAgPGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPg0KICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDhmZWI5YWNkLTFlODYtYWMxMi05MDIzLTEzYjg0NDc5YjI1YiI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+NVRWZURYbGQ3YzhURmtybVlDeFpuL2ZHRTRzPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5hZlFaVUE2REpHa0hLNjVMMENBaTJBSDJkOWNwbExuekNPTHBCYm9hUmVmaWdtVC92L0tJZGcyYXpWRzY2Ykk1aFA1NTBNR0c2ZVVzaWJ1N2N3ZytFbG9tejVBalE3dzlGZG8waHdWWWhib3JaSkN2TUxLUzBEWkFzc01XZnZ3RGNUNmhra3UreXFlS2RhZ1BBOTYwQ25YcUMxeHpjMk43WS82dlBCU081bVU9PC9kczpTaWduYXR1cmVWYWx1ZT4NCjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNnVENDQWVvQ0NRQ2JPbHJXRGRYN0ZUQU5CZ2txaGtpRzl3MEJBUVVGQURDQmhERUxNQWtHQTFVRUJoTUNUazh4R0RBV0JnTlZCQWdURDBGdVpISmxZWE1nVTI5c1ltVnlaekVNTUFvR0ExVUVCeE1EUm05dk1SQXdEZ1lEVlFRS0V3ZFZUa2xPUlZSVU1SZ3dGZ1lEVlFRREV3OW1aV2xrWlM1bGNteGhibWN1Ym04eElUQWZCZ2txaGtpRzl3MEJDUUVXRW1GdVpISmxZWE5BZFc1cGJtVjBkQzV1YnpBZUZ3MHdOekEyTVRVeE1qQXhNelZhRncwd056QTRNVFF4TWpBeE16VmFNSUdFTVFzd0NRWURWUVFHRXdKT1R6RVlNQllHQTFVRUNCTVBRVzVrY21WaGN5QlRiMnhpWlhKbk1Rd3dDZ1lEVlFRSEV3TkdiMjh4RURBT0JnTlZCQW9UQjFWT1NVNUZWRlF4R0RBV0JnTlZCQU1URDJabGFXUmxMbVZ5YkdGdVp5NXViekVoTUI4R0NTcUdTSWIzRFFFSkFSWVNZVzVrY21WaGMwQjFibWx1WlhSMExtNXZNSUdmTUEwR0NTcUdTSWIzRFFFQkFRVUFBNEdOQURDQmlRS0JnUURpdmJoUjdQNTE2eC9TM0JxS3h1cFFlMExPTm9saXVwaUJPZXNDTzNTSGJEcmwzK3E5SWJmbmZtRTA0ck51TWNQc0l4QjE2MVRkRHBJZXNMQ243YzhhUEhJU0tPdFBsQWVUWlNuYjhRQXU3YVJqWnEzK1BiclA1dVczVGNmQ0dQdEtUeXRIT2dlL09sSmJvMDc4ZFZoWFExNGQxRUR3WEpXMXJSWHVVdDRDOFFJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQlFVQUE0R0JBQ0RWZnA4NkhPYnFZK2U4QlVvV1E5K1ZNUXgxQVNEb2hCandPc2cyV3lrVXFSWEYrZExmY1VIOWRXUjYzQ3RaSUtGRGJTdE5vbVBuUXo3bmJLK29ueWd3QnNwVkVibkh1VWloWnEzWlVkbXVtUXFDdzRVdnMvMVV2cTNvck9vL1dKVmhUeXZMZ0ZWSzJRYXJRNC82N09aZkhkN1IrUE9CWGhvcGhTTXYxWk9vPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDQxN2ZiOTc2LTk0NGEtNDNiZi05ZTUyLWZiOWM1OTYxNzYxZiIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDItMTlUMDE6Mzc6MDFaIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPg0KICA8ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPg0KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4NCiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNwZng0MTdmYjk3Ni05NDRhLTQzYmYtOWU1Mi1mYjljNTk2MTc2MWYiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPmxSbTJ3UW13ZGhmZVZuMDFaS1Ewb05CN1JqQT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+aktwQUNmMWkxR0FMSWQ5Y0liQlFsTkJQMVhpZDhhYXFKOUxyTkFIZ1lpR2VIc0NscldVUkZJREprOGI0T3RmdHdXTGZKeXBXbXgwWm15M2hpTTJyVHBIbDBLMGVqSFNsOS9Ed0pabkNEQW1CS1lhZ0ZFR0xxWXYwaXI0Y2lYaForTkdXSDY1czhBRlVibjU2SytaS3lpMFkwMWc4TmVqaS92OTNlZFZ6ZTZnPTwvZHM6U2lnbmF0dXJlVmFsdWU+DQo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDZ1RDQ0Flb0NDUUNiT2xyV0RkWDdGVEFOQmdrcWhraUc5dzBCQVFVRkFEQ0JoREVMTUFrR0ExVUVCaE1DVGs4eEdEQVdCZ05WQkFnVEQwRnVaSEpsWVhNZ1UyOXNZbVZ5WnpFTU1Bb0dBMVVFQnhNRFJtOXZNUkF3RGdZRFZRUUtFd2RWVGtsT1JWUlVNUmd3RmdZRFZRUURFdzltWldsa1pTNWxjbXhoYm1jdWJtOHhJVEFmQmdrcWhraUc5dzBCQ1FFV0VtRnVaSEpsWVhOQWRXNXBibVYwZEM1dWJ6QWVGdzB3TnpBMk1UVXhNakF4TXpWYUZ3MHdOekE0TVRReE1qQXhNelZhTUlHRU1Rc3dDUVlEVlFRR0V3Sk9UekVZTUJZR0ExVUVDQk1QUVc1a2NtVmhjeUJUYjJ4aVpYSm5NUXd3Q2dZRFZRUUhFd05HYjI4eEVEQU9CZ05WQkFvVEIxVk9TVTVGVkZReEdEQVdCZ05WQkFNVEQyWmxhV1JsTG1WeWJHRnVaeTV1YnpFaE1COEdDU3FHU0liM0RRRUpBUllTWVc1a2NtVmhjMEIxYm1sdVpYUjBMbTV2TUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FEaXZiaFI3UDUxNngvUzNCcUt4dXBRZTBMT05vbGl1cGlCT2VzQ08zU0hiRHJsMytxOUliZm5mbUUwNHJOdU1jUHNJeEIxNjFUZERwSWVzTENuN2M4YVBISVNLT3RQbEFlVFpTbmI4UUF1N2FSalpxMytQYnJQNXVXM1RjZkNHUHRLVHl0SE9nZS9PbEpibzA3OGRWaFhRMTRkMUVEd1hKVzFyUlh1VXQ0QzhRSURBUUFCTUEwR0NTcUdTSWIzRFFFQkJRVUFBNEdCQUNEVmZwODZIT2JxWStlOEJVb1dROStWTVF4MUFTRG9oQmp3T3NnMld5a1VxUlhGK2RMZmNVSDlkV1I2M0N0WklLRkRiU3ROb21QblF6N25iSytvbnlnd0JzcFZFYm5IdVVpaFpxM1pVZG11bVFxQ3c0VXZzLzFVdnEzb3JPby9XSlZoVHl2TGdGVksyUWFyUTQvNjdPWmZIZDdSK1BPQlhob3BoU012MVpPbzwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIE5hbWVRdWFsaWZpZXI9Imh0dHBzOi8vdGVzdC5leGFtcGxlLmNvbS9zYW1sL21ldGFkYXRhIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+NDkyODgyNjE1YWNmMzFjODA5NmI2MjcyNDVkNzZhZTUzMDM2YzA5MDwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjA1NC0wOC0yM1QwNjo1NzowMVoiIFJlY2lwaWVudD0iaHR0cHM6Ly9waXRidWxrLm5vLWlwLm9yZy9uZXdvbmVsb2dpbi9kZW1vMS9pbmRleC5waHA/YWNzIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzVmZTlkNmU0OTliMmYwOTEzMjA2YWFiM2Y3MTkxNzI5MDQ5YmI4MDciLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxNC0wMi0xOVQwMTozNjozMVoiIE5vdE9uT3JBZnRlcj0iMjA1NC0wOC0yM1QwNjo1NzowMVoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+aHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNC0wMi0xOVQwMTozNzowMVoiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwNTQtMDItMTlUMDk6Mzc6MDFaIiBTZXNzaW9uSW5kZXg9Il82MjczZDc3YjhjZGUwYzMzM2VjNzlkMjJhOWZhMDAwM2I5ZmUyZDc1Y2IiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c21hcnRpbjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5zbWFydGluQHlhY28uZXM8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iY24iIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPlNpeHRvMzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJzbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+TWFydGluMjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlZHVQZXJzb25BZmZpbGlhdGlvbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5hZG1pbjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==

0 commit comments

Comments
 (0)