From 1a1a11c8e965fac05031295ee3557802887805ea Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Thu, 16 Feb 2017 11:47:45 +0100 Subject: [PATCH] Add support for mapping attributes This adds support for mapping attributes for full name and email Signed-off-by: Lukas Reschke --- appinfo/app.php | 3 +- js/admin.js | 26 ++++ lib/Controller/SAMLController.php | 6 +- lib/Settings/Admin.php | 13 ++ lib/userbackend.php | 112 +++++++++++++++++- templates/admin.php | 20 ++++ .../features/EnvironmentVariable.feature | 4 +- tests/integration/features/Shibboleth.feature | 30 ++++- .../features/bootstrap/FeatureContext.php | 22 +++- tests/unit/Settings/AdminTest.php | 13 ++ tests/unit/UserBackendTest.php | 9 +- 11 files changed, 240 insertions(+), 18 deletions(-) diff --git a/appinfo/app.php b/appinfo/app.php index 7c5dd7b..1c67200 100644 --- a/appinfo/app.php +++ b/appinfo/app.php @@ -42,7 +42,8 @@ $userBackend = new \OCA\User_SAML\UserBackend( $config, $urlGenerator, \OC::$server->getSession(), - \OC::$server->getDatabaseConnection() + \OC::$server->getDatabaseConnection(), + \OC::$server->getUserManager() ); $userBackend->registerBackends(\OC::$server->getUserManager()->getBackends()); OC_User::useBackend($userBackend); diff --git a/js/admin.js b/js/admin.js index 0e2b932..6867a25 100644 --- a/js/admin.js +++ b/js/admin.js @@ -53,6 +53,10 @@ $(function() { $('#user-saml-settings .button').addClass('hidden'); } + if($('#user-saml-general-require_provisioned_account').val() === '0') { + $('#user-saml-attribute-mapping').toggleClass('hidden'); + } + $('#user-saml-choose-saml').click(function(e) { e.preventDefault(); OCA.User_SAML.Admin.chooseSaml(); @@ -110,6 +114,9 @@ $(function() { } else { $(this).val("0"); } + if(key === 'require_provisioned_account') { + $('#user-saml-attribute-mapping').toggleClass('hidden'); + } OCA.User_SAML.Admin.setSamlConfigValue('general', key, $(this).val()); }); }); @@ -127,6 +134,18 @@ $(function() { }); }); + $('#user-saml-attribute-mapping input[type="text"], #user-saml-attribute-mapping textarea').change(function(e) { + var el = $(this); + $.when(el.focusout()).then(function() { + var key = $(this).attr('name'); + OCA.User_SAML.Admin.setSamlConfigValue('saml-attribute-mapping', key, $(this).val()); + }); + if (e.keyCode === 13) { + var key = $(this).attr('name'); + OCA.User_SAML.Admin.setSamlConfigValue('saml-attribute-mapping', key, $(this).val()); + } + }); + $('#user-saml').change(function() { if(type === 'saml') { // Checks on each request whether the settings make sense or not @@ -172,6 +191,13 @@ $(function() { text = 'Show Service Provider settings ...'; } break; + case 'user-saml-attribute-mapping': + if (nextSibling.hasClass('hidden')) { + text = 'Hide attribute mapping settings ...'; + } else { + text = 'Show attribute mapping settings ...'; + } + break; } el.html(t('user_saml', text)); diff --git a/lib/Controller/SAMLController.php b/lib/Controller/SAMLController.php index 8df7b65..3ed47a7 100644 --- a/lib/Controller/SAMLController.php +++ b/lib/Controller/SAMLController.php @@ -99,15 +99,19 @@ class SAMLController extends Controller { } $userExists = $this->userManager->userExists($uid); + $autoProvisioningAllowed = $this->userBackend->autoprovisionAllowed(); if($userExists === true) { + if($autoProvisioningAllowed) { + $this->userBackend->updateAttributes($uid, $auth); + } return; } - $autoProvisioningAllowed = $this->userBackend->autoprovisionAllowed(); if(!$userExists && !$autoProvisioningAllowed) { throw new NoUserFoundException(); } elseif(!$userExists && $autoProvisioningAllowed) { $this->userBackend->createUserIfNotExists($uid); + $this->userBackend->updateAttributes($uid, $auth); return; } } diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index b109d87..29b445d 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -87,6 +87,18 @@ class Admin implements ISettings { 'type' => 'checkbox', ], ]; + $attributeMappingSettings = [ + 'displayName_mapping' => [ + 'text' => $this->l10n->t('Attribute to map the displayname to.'), + 'type' => 'line', + 'required' => true, + ], + 'email_mapping' => [ + 'text' => $this->l10n->t('Attribute to map the email address to.'), + 'type' => 'line', + 'required' => true, + ], + ]; $type = $this->config->getAppValue('user_saml', 'type'); if($type === 'saml') { @@ -102,6 +114,7 @@ class Admin implements ISettings { 'security-required' => $securityRequiredFields, 'security-general' => $securityGeneral, 'general' => $generalSettings, + 'attributeMappings' => $attributeMappingSettings, 'type' => $type, ]; diff --git a/lib/userbackend.php b/lib/userbackend.php index e9cf45c..d87cf48 100644 --- a/lib/userbackend.php +++ b/lib/userbackend.php @@ -24,6 +24,7 @@ namespace OCA\User_SAML; use OCP\Authentication\IApacheBackend; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +use OCP\IUserManager; use OCP\UserInterface; use OCP\IUserBackend; use OCP\IConfig; @@ -39,6 +40,8 @@ class UserBackend implements IApacheBackend, UserInterface, IUserBackend { private $session; /** @var IDBConnection */ private $db; + /** @var IUserManager */ + private $userManager; /** @var \OCP\UserInterface[] */ private $backends; @@ -47,15 +50,18 @@ class UserBackend implements IApacheBackend, UserInterface, IUserBackend { * @param IURLGenerator $urlGenerator * @param ISession $session * @param IDBConnection $db + * @param IUserManager $userManager */ public function __construct(IConfig $config, IURLGenerator $urlGenerator, ISession $session, - IDBConnection $db) { + IDBConnection $db, + IUserManager $userManager) { $this->config = $config; $this->urlGenerator = $urlGenerator; $this->session = $session; $this->db = $db; + $this->userManager = $userManager; } /** @@ -109,8 +115,14 @@ class UserBackend implements IApacheBackend, UserInterface, IUserBackend { * @since 4.5.0 */ public function implementsActions($actions) { - return (bool)((\OC_User_Backend::CHECK_PASSWORD) - & $actions); + $availableActions = \OC_User_Backend::CHECK_PASSWORD; + if($this->autoprovisionAllowed() + && $this->config->getAppValue('user_saml', 'saml-attribute-mapping-displayName_mapping', '') !== '') { + + $availableActions |= \OC_User_Backend::GET_DISPLAYNAME; + } + + return (bool)($availableActions & $actions); } /** @@ -207,13 +219,48 @@ class UserBackend implements IApacheBackend, UserInterface, IUserBackend { } } + public function setDisplayName($uid, $displayName) { + if($backend = $this->getActualUserBackend($uid)) { + return $backend->setDisplayName($uid, $displayName); + } + + if ($this->userExistsInDatabase($uid)) { + $qb = $this->db->getQueryBuilder(); + $qb->update('user_saml_users') + ->set('displayname', $qb->createNamedParameter($displayName)) + ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) + ->execute(); + return true; + } + + return false; + } + /** - * get display name of the user + * Get display name of the user + * * @param string $uid user ID of the user * @return string display name * @since 4.5.0 */ public function getDisplayName($uid) { + if($backend = $this->getActualUserBackend($uid)) { + return $backend->getDisplayName($uid); + } else { + if($this->userExistsInDatabase($uid)) { + $qb = $this->db->getQueryBuilder(); + $qb->select('displayname') + ->from('user_saml_users') + ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) + ->setMaxResults(1); + $result = $qb->execute(); + $users = $result->fetchAll(); + if (isset($users[0]['displayname'])) { + return $users[0]['displayname']; + } + } + } + return false; } @@ -233,6 +280,9 @@ class UserBackend implements IApacheBackend, UserInterface, IUserBackend { ->where( $qb->expr()->iLike('uid', $qb->createNamedParameter('%' . $this->db->escapeLikeParameter($search) . '%')) ) + ->orWhere( + $qb->expr()->iLike('displayname', $qb->createNamedParameter('%' . $this->db->escapeLikeParameter($search) . '%')) + ) ->setMaxResults($limit); if($offset !== null) { $qb->setFirstResult($offset); @@ -360,4 +410,58 @@ class UserBackend implements IApacheBackend, UserInterface, IUserBackend { $this->backends = $backends; } + private function getAttributeValue($name, array $attributes) { + $keys = explode(' ', $this->config->getAppValue('user_saml', $name, '')); + + if(count($keys) === 1 && $keys[1] === '') { + throw new \InvalidArgumentException('Attribute is not configured'); + } + + $value = ''; + foreach($keys as $key) { + if (isset($attributes[$key])) { + if (is_array($attributes[$key])) { + if($value !== '') { + $value .= ' '; + } + $value .= $attributes[$key][0]; + } else { + if($value !== '') { + $value .= ' '; + } + $value .= $attributes[$key]; + } + } + } + + return $value; + } + + public function updateAttributes($uid, + array $attributes) { + $user = $this->userManager->get($uid); + try { + $newEmail = $this->getAttributeValue('saml-attribute-mapping-email_mapping', $attributes); + } catch (\InvalidArgumentException $e) { + $newEmail = null; + } + try { + $newDisplayname = $this->getAttributeValue('saml-attribute-mapping-displayName_mapping', $attributes); + } catch (\InvalidArgumentException $e) { + $newDisplayname = null; + } + + if ($user !== null) { + $currentEmail = (string)$user->getEMailAddress(); + if ($newEmail !== null + && $currentEmail !== $newEmail) { + $user->setEMailAddress($newEmail); + } + $currentDisplayname = (string)$this->getDisplayName($uid); + if($newDisplayname !== null + && $currentDisplayname !== $newDisplayname) { + $this->setDisplayName($uid, $newDisplayname); + } + } + } } diff --git a/templates/admin.php b/templates/admin.php index 5b8ab03..3e74568 100644 --- a/templates/admin.php +++ b/templates/admin.php @@ -79,6 +79,26 @@ style('user_saml', 'admin');

+ + +

t('Security settings')) ?>

diff --git a/tests/integration/features/EnvironmentVariable.feature b/tests/integration/features/EnvironmentVariable.feature index 497a645..eb4cf1e 100644 --- a/tests/integration/features/EnvironmentVariable.feature +++ b/tests/integration/features/EnvironmentVariable.feature @@ -6,7 +6,7 @@ Feature: EnvironmentVariable And The environment variable "REMOTE_USER" is set to "not-provisioned-user" When I send a GET request to "http://localhost/index.php/login" Then I should be redirected to "http://localhost/index.php/apps/files/" - Then I should be logged-in to Nextcloud as user "not-provisioned-user" + Then The user value "id" should be "not-provisioned-user" Scenario: Authenticating using environment variable with SSO and successful check if user exists on backend Given A local user with uid "provisioned-user" exists @@ -16,7 +16,7 @@ Feature: EnvironmentVariable And The environment variable "REMOTE_USER" is set to "provisioned-user" When I send a GET request to "http://localhost/index.php/login" Then I should be redirected to "http://localhost/index.php/apps/files/" - Then I should be logged-in to Nextcloud as user "provisioned-user" + Then The user value "id" should be "provisioned-user" Scenario: Authenticating using environment variable with SSO and unsuccessful check if user exists on backend Given The setting "type" is set to "environment-variable" diff --git a/tests/integration/features/Shibboleth.feature b/tests/integration/features/Shibboleth.feature index a825673..4d2a047 100644 --- a/tests/integration/features/Shibboleth.feature +++ b/tests/integration/features/Shibboleth.feature @@ -18,7 +18,7 @@ Feature: Shibboleth |student1 |password | | And The response should be a SAML redirect page that gets submitted And I should be redirected to "http://localhost/index.php/apps/files/" - And I should be logged-in to Nextcloud as user "student1" + And The user value "id" should be "student1" Scenario: Authenticating using Shibboleth with SAML and check if user exists on backend and not existing user Given The setting "type" is set to "saml" @@ -50,6 +50,7 @@ Feature: Shibboleth And The setting "idp-x509cert" is set to "MIIDnTCCAoWgAwIBAgIUGPx9uPjCu7c172IUgV84Tm94pBcwDQYJKoZIhvcNAQEL BQAwNzE1MDMGA1UEAwwsc2hpYmJvbGV0aC1pbnRlZ3JhdGlvbi1uZXh0Y2xvdWQu bG9jYWxkb21haW4wHhcNMTcwMTA0MTAxMTI3WhcNMzcwMTA0MTAxMTI3WjA3MTUw MwYDVQQDDCxzaGliYm9sZXRoLWludGVncmF0aW9uLW5leHRjbG91ZC5sb2NhbGRv bWFpbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKH+bPu45tk8/JRk XOxkyfbxocWZlY4mRumEUxscd3fn0oVzOrdWbHH7lCZV4bus4KxvJljc0Nm2K+Zr LoiRUUnf/LQ4LlehWVm5Kbc4kRgOXS0iGZN3SslAWPKyIg0tywg+TLOBPoS6EtST 1WuYg1JPMFxPfeFDWQ0dQYPlXIJWBFh6F2JMTb0FLECqA5l/ryYE13QisX5l+Mqo 6y3Dh7qIgaH0IJNobXoAcEWza7Kb2RnfhZRh9e0qjZIwBqTJUFM/6I86RYXn829s INUvYQQbez6VkGTdUQJ/GuXb/dD5sMQfOyK8hrRY5MozOmK32cz3JaAzSXpiSRS9 NxFwvicCAwEAAaOBoDCBnTAdBgNVHQ4EFgQUKn8+TV0WXSDeavvF0M8mWn1o8ukw fAYDVR0RBHUwc4Isc2hpYmJvbGV0aC1pbnRlZ3JhdGlvbi1uZXh0Y2xvdWQubG9j YWxkb21haW6GQ2h0dHBzOi8vc2hpYmJvbGV0aC1pbnRlZ3JhdGlvbi1uZXh0Y2xv dWQubG9jYWxkb21haW4vaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQELBQADggEB ABI6uzoIeLZT9Az2KTlLxIc6jZ4MDmhaVja4ZuBxTXEb7BFLfeASEJmQm1wgIMOn pJId3Kh3njW+3tOBWKm7lj8JxVVpAu4yMFSoQGPaVUgYB1AVm+pmAyPLzfJ/XGhf esCU2F/b0eHWcaIb3x+BZFX089Cd/PBtP84MNXdo+TccibxC8N39sr45qJM/7SC7 TfDYU0L4q2WZHJr4S7+0F+F4KaxLx9NzCvN4h6XaoWofZWir2iHO4NzbrVQGC0ei QybS/neBfni4A2g1lyzCb6xFB58JBvNCn7AAnDJULOE7S5XWUKsDAQVQrxTNkUq7 pnhlCQqZDwUdgmIXd1KB1So=" And The setting "security-authnRequestsSigned" is set to "1" And The setting "security-wantAssertionsEncrypted" is set to "1" + And The setting "saml-attribute-mapping-email_mapping" is set to "urn:oid:0.9.2342.19200300.100.1.3" And The setting "sp-x509cert" is set to "-----BEGIN CERTIFICATE-----MIIC+zCCAeOgAwIBAgIJAIgZuvWDBIrdMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNzAxMDQxMTM5MjFaFw0yNzAxMDIxMTM5MjFaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN3ESWaDH1JiJTy9yRJQV7kahPOxgBkIH2xwcYDL1k9deKNhSKLx7aGfxE244+HBcC6WLHKVUnOm0ld2qxQ4bMYiJXzZuqL67r07L5wxGAssv12lO92qohGmlHy3+VzRYUBmovu6upqOv3R2F8HBbo7Jc7Hvt7hOEJn/jPuFuF/fHit3mqU8l6IkrIZjpaW8T9fIWOXRq98U4+hkgWpqEZWsqlfE8BxAs9DeIMZab0GxO9stHLp+GYKx10uE4ezFcaDS8W+g2C8enCTt1HXGvcnj4o5zkC1lITGvcFTsiFqfIWyXeSufcxdc0W7HoG6J3ks0WJyK38sfFn0t2Ao6kX0CAwEAAaNQME4wHQYDVR0OBBYEFAoJzX6TVYAwC1GSPe6nObBG54zaMB8GA1UdIwQYMBaAFAoJzX6TVYAwC1GSPe6nObBG54zaMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAJia9R70uXdUZtgujUPjLas4+sVajzlBFmqhBqpLAo934vljf9HISsHrPtdBcbS0d0rucqXwabDf0MlR18ksnT/NYpsTwMbMx76CrXi4zYEEW5lISKEO65aIkzVTcqKWSuhjtSnRdB6iOLsFiKmNMWXaIKMR5T0+AbR9wdQgn08W+3EEeHGvafVQfE3STVsSgNb1ft7DvcSUnfPXGU7KzvmTpZa0Hfmc7uY4vpdEEhLAdRhgLReS7USZskov7ooiPSoD+JRFi2gM4klBxTemHdNUa9oFnHMXuYKOkLbkgFvHxyy+QlLq2ELQTga5e7I83ZyOfGctyf8Ul6vGw10vbQ=-----END CERTIFICATE-----" And The setting "sp-privateKey" is set to "-----BEGIN RSA PRIVATE KEY-----MIIEpAIBAAKCAQEA3cRJZoMfUmIlPL3JElBXuRqE87GAGQgfbHBxgMvWT114o2FIovHtoZ/ETbjj4cFwLpYscpVSc6bSV3arFDhsxiIlfNm6ovruvTsvnDEYCyy/XaU73aqiEaaUfLf5XNFhQGai+7q6mo6/dHYXwcFujslzse+3uE4Qmf+M+4W4X98eK3eapTyXoiSshmOlpbxP18hY5dGr3xTj6GSBamoRlayqV8TwHECz0N4gxlpvQbE72y0cun4ZgrHXS4Th7MVxoNLxb6DYLx6cJO3Udca9yePijnOQLWUhMa9wVOyIWp8hbJd5K59zF1zRbsegboneSzRYnIrfyx8WfS3YCjqRfQIDAQABAoIBAQC5CQAdcqZ9vLpJNilBCJxJLCFmm+HAAREHD8MErg9A5UK1P4S1wJp/0qieGPi68wXBOTgY2xKSwMycgb04/+NyZidVRu388takOW/+KNBg8pMxdZ6/05GqnI0kivSbR3CXpYuz8hekwhpo9+fWmKjApsHL47ItK6WaeKmPbAFsq1YJGzfp/DXg7LIvh9GA3C1LWWGV7SuCGOyX/2Moi8xRa7qBtH4hDo/0NRhTx7zjYjlBgNEr330pJUopc3+AtHE40R+xMr2zkGvq9RsCZxYxD2VWbLwQW0yNjWmQ2OTuMgJJvk2+N73QLHcB+tea82ZTszsNzRS9DLtc6qbsKEPZAoGBAO78U3vEuRyY56f/1hpo0xuCDwOkWGzgBQWkjJl6dlyVz/zKkhXBHpEYImyt8XRN0W3iGZYpZ2hCFJGTcDp32R6UiEyGLz0Uc8R/tva/TiRVW1FdNczzSHcB24b9OMK4vE9JLs8mA8Rp8YBgtLr5DDuMfYt/a/rZJbg/HIfIN98nAoGBAO2OInCX93t2I6zzRPIqKtI6q6FYNp64VIQjvw9Y8l0x3IdJZRP9H5C8ZhCeYPsgEqTXcXa4j5hL4rQzoUtxfxflBUUH60bcnd4LGaTCMYLS14G011E3GZlIP0sJi5OjEhy8fq3zt6jVzS9V/lPHB8i+w1D7CbPrMpW7B3k32vC7AoGAX/HvdkYhZyjAAEOG6m1hK68IZhbp5TP+8CgCxm9S65K9wKh3A8LXibrdvzIKOP4w8WOPkCipOkMlTNibeu24vj01hztr5aK7Y40+oEtnjNCz67N3MQQO+LBHOSeaTRqrh01DPKjvZECAU2D/zfzEe3fIw2Nxr3DUYub7hkvMmosCgYAzxbVVypjiLGYsDDyrdmsstCKxoDMPNmcdAVljc+QmUXaZeXJw/8qAVb78wjeqo1vM1zNgR2rsKyW2VkZB1fN39q7GU6qAIBa7zLmDAduegmr7VrlSduq6UFeS9/qWa4TIBICrUqFlR2tXdKtgANF+e6y/mmaL8qdsoH1JetXZfwKBgQC1vscRpdAXivjOOZAh+mzJWzS4BUl4CTJLYYIuOEXikmN5g0EdV2fhUEdkewmyKnXHsd0x83167bYgpTDNs71jUxDHy5NXlg2qIjLkf09X9wr19gBzDApfWzfh3vUqttyMZuQMLVNepGCWM2vjlY9KGl5OvZqY6d+7yO0mLV9GmQ==-----END RSA PRIVATE KEY-----" And The setting "security-wantAssertionsSigned" is set to "1" @@ -60,4 +61,29 @@ Feature: Shibboleth |student1 |password | | And The response should be a SAML redirect page that gets submitted And I should be redirected to "http://localhost/index.php/apps/files/" - And I should be logged-in to Nextcloud as user "student1" \ No newline at end of file + Then The user value "id" should be "student1" + Then The user value "email" should be "" + + Scenario: Authenticating using Shibboleth with SAML in provisioning mode and custom mapped attributes + Given The setting "type" is set to "saml" + And The setting "general-uid_mapping" is set to "urn:oid:0.9.2342.19200300.100.1.1" + And The setting "idp-entityId" is set to "https://shibboleth-integration-nextcloud.localdomain/idp/shibboleth" + And The setting "idp-singleSignOnService.url" is set to "https://localhost:4443/idp/profile/SAML2/Redirect/SSO" + And The setting "idp-x509cert" is set to "MIIDnTCCAoWgAwIBAgIUGPx9uPjCu7c172IUgV84Tm94pBcwDQYJKoZIhvcNAQEL BQAwNzE1MDMGA1UEAwwsc2hpYmJvbGV0aC1pbnRlZ3JhdGlvbi1uZXh0Y2xvdWQu bG9jYWxkb21haW4wHhcNMTcwMTA0MTAxMTI3WhcNMzcwMTA0MTAxMTI3WjA3MTUw MwYDVQQDDCxzaGliYm9sZXRoLWludGVncmF0aW9uLW5leHRjbG91ZC5sb2NhbGRv bWFpbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKH+bPu45tk8/JRk XOxkyfbxocWZlY4mRumEUxscd3fn0oVzOrdWbHH7lCZV4bus4KxvJljc0Nm2K+Zr LoiRUUnf/LQ4LlehWVm5Kbc4kRgOXS0iGZN3SslAWPKyIg0tywg+TLOBPoS6EtST 1WuYg1JPMFxPfeFDWQ0dQYPlXIJWBFh6F2JMTb0FLECqA5l/ryYE13QisX5l+Mqo 6y3Dh7qIgaH0IJNobXoAcEWza7Kb2RnfhZRh9e0qjZIwBqTJUFM/6I86RYXn829s INUvYQQbez6VkGTdUQJ/GuXb/dD5sMQfOyK8hrRY5MozOmK32cz3JaAzSXpiSRS9 NxFwvicCAwEAAaOBoDCBnTAdBgNVHQ4EFgQUKn8+TV0WXSDeavvF0M8mWn1o8ukw fAYDVR0RBHUwc4Isc2hpYmJvbGV0aC1pbnRlZ3JhdGlvbi1uZXh0Y2xvdWQubG9j YWxkb21haW6GQ2h0dHBzOi8vc2hpYmJvbGV0aC1pbnRlZ3JhdGlvbi1uZXh0Y2xv dWQubG9jYWxkb21haW4vaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQELBQADggEB ABI6uzoIeLZT9Az2KTlLxIc6jZ4MDmhaVja4ZuBxTXEb7BFLfeASEJmQm1wgIMOn pJId3Kh3njW+3tOBWKm7lj8JxVVpAu4yMFSoQGPaVUgYB1AVm+pmAyPLzfJ/XGhf esCU2F/b0eHWcaIb3x+BZFX089Cd/PBtP84MNXdo+TccibxC8N39sr45qJM/7SC7 TfDYU0L4q2WZHJr4S7+0F+F4KaxLx9NzCvN4h6XaoWofZWir2iHO4NzbrVQGC0ei QybS/neBfni4A2g1lyzCb6xFB58JBvNCn7AAnDJULOE7S5XWUKsDAQVQrxTNkUq7 pnhlCQqZDwUdgmIXd1KB1So=" + And The setting "security-authnRequestsSigned" is set to "1" + And The setting "security-wantAssertionsEncrypted" is set to "1" + And The setting "sp-x509cert" is set to "-----BEGIN CERTIFICATE-----MIIC+zCCAeOgAwIBAgIJAIgZuvWDBIrdMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNzAxMDQxMTM5MjFaFw0yNzAxMDIxMTM5MjFaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN3ESWaDH1JiJTy9yRJQV7kahPOxgBkIH2xwcYDL1k9deKNhSKLx7aGfxE244+HBcC6WLHKVUnOm0ld2qxQ4bMYiJXzZuqL67r07L5wxGAssv12lO92qohGmlHy3+VzRYUBmovu6upqOv3R2F8HBbo7Jc7Hvt7hOEJn/jPuFuF/fHit3mqU8l6IkrIZjpaW8T9fIWOXRq98U4+hkgWpqEZWsqlfE8BxAs9DeIMZab0GxO9stHLp+GYKx10uE4ezFcaDS8W+g2C8enCTt1HXGvcnj4o5zkC1lITGvcFTsiFqfIWyXeSufcxdc0W7HoG6J3ks0WJyK38sfFn0t2Ao6kX0CAwEAAaNQME4wHQYDVR0OBBYEFAoJzX6TVYAwC1GSPe6nObBG54zaMB8GA1UdIwQYMBaAFAoJzX6TVYAwC1GSPe6nObBG54zaMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAJia9R70uXdUZtgujUPjLas4+sVajzlBFmqhBqpLAo934vljf9HISsHrPtdBcbS0d0rucqXwabDf0MlR18ksnT/NYpsTwMbMx76CrXi4zYEEW5lISKEO65aIkzVTcqKWSuhjtSnRdB6iOLsFiKmNMWXaIKMR5T0+AbR9wdQgn08W+3EEeHGvafVQfE3STVsSgNb1ft7DvcSUnfPXGU7KzvmTpZa0Hfmc7uY4vpdEEhLAdRhgLReS7USZskov7ooiPSoD+JRFi2gM4klBxTemHdNUa9oFnHMXuYKOkLbkgFvHxyy+QlLq2ELQTga5e7I83ZyOfGctyf8Ul6vGw10vbQ=-----END CERTIFICATE-----" + And The setting "sp-privateKey" is set to "-----BEGIN RSA PRIVATE KEY-----MIIEpAIBAAKCAQEA3cRJZoMfUmIlPL3JElBXuRqE87GAGQgfbHBxgMvWT114o2FIovHtoZ/ETbjj4cFwLpYscpVSc6bSV3arFDhsxiIlfNm6ovruvTsvnDEYCyy/XaU73aqiEaaUfLf5XNFhQGai+7q6mo6/dHYXwcFujslzse+3uE4Qmf+M+4W4X98eK3eapTyXoiSshmOlpbxP18hY5dGr3xTj6GSBamoRlayqV8TwHECz0N4gxlpvQbE72y0cun4ZgrHXS4Th7MVxoNLxb6DYLx6cJO3Udca9yePijnOQLWUhMa9wVOyIWp8hbJd5K59zF1zRbsegboneSzRYnIrfyx8WfS3YCjqRfQIDAQABAoIBAQC5CQAdcqZ9vLpJNilBCJxJLCFmm+HAAREHD8MErg9A5UK1P4S1wJp/0qieGPi68wXBOTgY2xKSwMycgb04/+NyZidVRu388takOW/+KNBg8pMxdZ6/05GqnI0kivSbR3CXpYuz8hekwhpo9+fWmKjApsHL47ItK6WaeKmPbAFsq1YJGzfp/DXg7LIvh9GA3C1LWWGV7SuCGOyX/2Moi8xRa7qBtH4hDo/0NRhTx7zjYjlBgNEr330pJUopc3+AtHE40R+xMr2zkGvq9RsCZxYxD2VWbLwQW0yNjWmQ2OTuMgJJvk2+N73QLHcB+tea82ZTszsNzRS9DLtc6qbsKEPZAoGBAO78U3vEuRyY56f/1hpo0xuCDwOkWGzgBQWkjJl6dlyVz/zKkhXBHpEYImyt8XRN0W3iGZYpZ2hCFJGTcDp32R6UiEyGLz0Uc8R/tva/TiRVW1FdNczzSHcB24b9OMK4vE9JLs8mA8Rp8YBgtLr5DDuMfYt/a/rZJbg/HIfIN98nAoGBAO2OInCX93t2I6zzRPIqKtI6q6FYNp64VIQjvw9Y8l0x3IdJZRP9H5C8ZhCeYPsgEqTXcXa4j5hL4rQzoUtxfxflBUUH60bcnd4LGaTCMYLS14G011E3GZlIP0sJi5OjEhy8fq3zt6jVzS9V/lPHB8i+w1D7CbPrMpW7B3k32vC7AoGAX/HvdkYhZyjAAEOG6m1hK68IZhbp5TP+8CgCxm9S65K9wKh3A8LXibrdvzIKOP4w8WOPkCipOkMlTNibeu24vj01hztr5aK7Y40+oEtnjNCz67N3MQQO+LBHOSeaTRqrh01DPKjvZECAU2D/zfzEe3fIw2Nxr3DUYub7hkvMmosCgYAzxbVVypjiLGYsDDyrdmsstCKxoDMPNmcdAVljc+QmUXaZeXJw/8qAVb78wjeqo1vM1zNgR2rsKyW2VkZB1fN39q7GU6qAIBa7zLmDAduegmr7VrlSduq6UFeS9/qWa4TIBICrUqFlR2tXdKtgANF+e6y/mmaL8qdsoH1JetXZfwKBgQC1vscRpdAXivjOOZAh+mzJWzS4BUl4CTJLYYIuOEXikmN5g0EdV2fhUEdkewmyKnXHsd0x83167bYgpTDNs71jUxDHy5NXlg2qIjLkf09X9wr19gBzDApfWzfh3vUqttyMZuQMLVNepGCWM2vjlY9KGl5OvZqY6d+7yO0mLV9GmQ==-----END RSA PRIVATE KEY-----" + And The setting "security-wantAssertionsSigned" is set to "1" + And The setting "saml-attribute-mapping-email_mapping" is set to "urn:oid:0.9.2342.19200300.100.1.3" + And The setting "saml-attribute-mapping-displayName_mapping" is set to "urn:oid:2.5.4.42 urn:oid:2.5.4.4" + When I send a GET request to "http://localhost/index.php/login" + Then I should be redirected to "https://localhost:4443/idp/profile/SAML2/Redirect/SSO" + And I send a POST request to "https://localhost:4443/idp/profile/SAML2/Redirect/SSO?execution=e1s1" with the following data + |j_username|j_password|_eventId_proceed| + |student1 |password | | + And The response should be a SAML redirect page that gets submitted + And I should be redirected to "http://localhost/index.php/apps/files/" + And The user value "id" should be "student1" + And The user value "email" should be "student1@idptestbed.edu" + And The user value "display-name" should be "Stud Ent" diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index d9b4bea..af7cc51 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -181,10 +181,13 @@ class FeatureContext implements Context { } /** - * @Then I should be logged-in to Nextcloud as user :userId + * @Then The user value :key should be :value + * + * @param string $key + * @param string $value * @throws UnexpectedValueException */ - public function iShouldBeLoggedInToNextcloudAsUser($userId) { + public function thUserValueShouldBe($key, $value) { $this->response = $this->client->request( 'GET', 'http://localhost/ocs/v1.php/cloud/user', @@ -196,17 +199,24 @@ class FeatureContext implements Context { ); $xml = simplexml_load_string($this->response->getBody()); + /** @var array $responseArray */ $responseArray = json_decode(json_encode((array)$xml), true); - if($responseArray['data']['display-name'] !== $userId) { + foreach($responseArray['data'] as $arrayKey => $arrayValue) { + if(count($responseArray['data'][$arrayKey]) === 0) { + $responseArray['data'][$arrayKey] = ''; + } + } + + $actualValue = $responseArray['data'][$key]; + if($actualValue !== $value) { throw new UnexpectedValueException( sprintf( 'Expected %s as value but got %s', - $userId, - $responseArray['data']['display-name'] + $value, + $actualValue ) ); } - } /** diff --git a/tests/unit/Settings/AdminTest.php b/tests/unit/Settings/AdminTest.php index b1f1048..ae24c2a 100644 --- a/tests/unit/Settings/AdminTest.php +++ b/tests/unit/Settings/AdminTest.php @@ -95,6 +95,18 @@ class AdminTest extends \Test\TestCase { 'type' => 'checkbox', ], ]; + $attributeMappingSettings = [ + 'displayName_mapping' => [ + 'text' => $this->l10n->t('Attribute to map the displayname to.'), + 'type' => 'line', + 'required' => true, + ], + 'email_mapping' => [ + 'text' => $this->l10n->t('Attribute to map the email address to.'), + 'type' => 'line', + 'required' => true, + ], + ]; $params = [ 'sp' => $serviceProviderFields, @@ -102,6 +114,7 @@ class AdminTest extends \Test\TestCase { 'security-required' => $securityRequiredFields, 'security-general' => $securityGeneral, 'general' => $generalSettings, + 'attributeMappings' => $attributeMappingSettings, ]; return $params; diff --git a/tests/unit/UserBackendTest.php b/tests/unit/UserBackendTest.php index ff7a975..5967f4a 100644 --- a/tests/unit/UserBackendTest.php +++ b/tests/unit/UserBackendTest.php @@ -27,6 +27,7 @@ use OCP\IDBConnection; use OCP\ISession; use OCP\IURLGenerator; use OCP\IUserBackend; +use OCP\IUserManager; use Test\TestCase; class UserBackendTest extends TestCase { @@ -38,7 +39,9 @@ class UserBackendTest extends TestCase { private $session; /** @var IDBConnection|\PHPUnit_Framework_MockObject_MockObject */ private $db; - /** @var IUserBackend|\PHPUnit_Framework_MockObject_MockObject */ + /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */ + private $userManager; + /** @var IUserBackend */ private $userBackend; public function setUp() { @@ -48,12 +51,14 @@ class UserBackendTest extends TestCase { $this->urlGenerator = $this->createMock(IURLGenerator::class); $this->session = $this->createMock(ISession::class); $this->db = $this->createMock(IDBConnection::class); + $this->userManager = $this->createMock(IUserManager::class); $this->userBackend = new UserBackend( $this->config, $this->urlGenerator, $this->session, - $this->db + $this->db, + $this->userManager ); }