From 1b44eeec337af57720b64e1512a8fa1566118f90 Mon Sep 17 00:00:00 2001 From: Agustin Villalba <avillalbacasas@gmail.com> Date: Mon, 16 Oct 2017 14:39:54 +0200 Subject: [PATCH] Implement "Remember my Organization" in multiple-ldap This new feature allows the users to have a checkbox in the login form which works in the same way than "Remember my username" but for the organization selected in the dropdown --- config-templates/authsources.php | 5 +++ dictionaries/login.definition.json | 5 ++- modules/core/lib/Auth/UserPassOrgBase.php | 40 ++++++++++++++++++ modules/core/templates/loginuserpass.php | 10 +++++ modules/core/www/loginuserpassorg.php | 13 ++++++ .../core/lib/Auth/UserPassOrgBaseTest.php | 41 +++++++++++++++++++ 6 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 tests/modules/core/lib/Auth/UserPassOrgBaseTest.php diff --git a/config-templates/authsources.php b/config-templates/authsources.php index 4b0dbc596..d06715952 100644 --- a/config-templates/authsources.php +++ b/config-templates/authsources.php @@ -276,6 +276,11 @@ $config = array( //'remember.username.enabled' => FALSE, //'remember.username.checked' => FALSE, + // Give the user an option to save their organization choice for future login + // attempts. And when enabled, what should the default be, checked or not. + //'remember.organization.enabled' => false, + //'remember.organization.checked' => false, + // The way the organization as part of the username should be handled. // Three possible values: // - 'none': No handling of the organization. Allows '@' to be part diff --git a/dictionaries/login.definition.json b/dictionaries/login.definition.json index be3b12d4e..15969601b 100644 --- a/dictionaries/login.definition.json +++ b/dictionaries/login.definition.json @@ -64,5 +64,8 @@ }, "remember_me": { "en": "Remember me" - } + }, + "remember_organization": { + "en": "Remember my organization" + } } diff --git a/modules/core/lib/Auth/UserPassOrgBase.php b/modules/core/lib/Auth/UserPassOrgBase.php index c87df897c..518f7ec00 100644 --- a/modules/core/lib/Auth/UserPassOrgBase.php +++ b/modules/core/lib/Auth/UserPassOrgBase.php @@ -59,6 +59,22 @@ abstract class UserPassOrgBase extends \SimpleSAML\Auth\Source */ protected $rememberUsernameChecked = FALSE; + /** + * Storage for authsource config option remember.organization.enabled + * loginuserpassorg.php page/template use this option to present users + * with a checkbox to save their organization choice for the next login request. + * @var bool + */ + protected $rememberOrganizationEnabled = false; + + /** + * Storage for authsource config option remember.organization.checked + * loginuserpassorg.php page/template use this option to + * default the remember organization checkbox to checked or not. + * @var bool + */ + protected $rememberOrganizationChecked = false; + /** * Constructor for this authentication source. @@ -85,6 +101,15 @@ abstract class UserPassOrgBase extends \SimpleSAML\Auth\Source $this->rememberUsernameChecked = (bool) $config['remember.username.checked']; unset($config['remember.username.checked']); } + // Get the remember organization config options + if (isset($config['remember.organization.enabled'])) { + $this->rememberOrganizationEnabled = (bool) $config['remember.organization.enabled']; + unset($config['remember.organization.enabled']); + } + if (isset($config['remember.organization.checked'])) { + $this->rememberOrganizationChecked = (bool) $config['remember.organization.checked']; + unset($config['remember.organization.checked']); + } $this->usernameOrgMethod = 'none'; } @@ -139,6 +164,21 @@ abstract class UserPassOrgBase extends \SimpleSAML\Auth\Source return $this->rememberUsernameChecked; } + /** + * Getter for the authsource config option remember.organization.enabled + * @return bool + */ + public function getRememberOrganizationEnabled() { + return $this->rememberOrganizationEnabled; + } + + /** + * Getter for the authsource config option remember.organization.checked + * @return bool + */ + public function getRememberOrganizationChecked() { + return $this->rememberOrganizationChecked; + } /** * Initialize login. diff --git a/modules/core/templates/loginuserpass.php b/modules/core/templates/loginuserpass.php index d957fc43a..210db853a 100644 --- a/modules/core/templates/loginuserpass.php +++ b/modules/core/templates/loginuserpass.php @@ -142,6 +142,16 @@ if ($this->data['errorcode'] !== null) { } ?> </select></td> + <td style="padding: .4em;"> + <?php + if ($this->data['rememberOrganizationEnabled']) { + echo str_repeat("\t", 4); + echo '<input type="checkbox" id="remember_organization" tabindex="5" name="remember_organization" value="Yes" '; + echo ($this->data['rememberOrganizationChecked'] ? 'checked="Yes" /> ' : '/> '); + echo $this->t('{login:remember_organization}'); + } + ?> + </td> </tr> <?php } diff --git a/modules/core/www/loginuserpassorg.php b/modules/core/www/loginuserpassorg.php index ea7050c37..36791e16b 100644 --- a/modules/core/www/loginuserpassorg.php +++ b/modules/core/www/loginuserpassorg.php @@ -41,6 +41,8 @@ if (array_key_exists('password', $_REQUEST)) { if (array_key_exists('organization', $_REQUEST)) { $organization = $_REQUEST['organization']; +} elseif ($source->getRememberOrganizationEnabled() && array_key_exists($source->getAuthId() . '-organization', $_COOKIE)) { + $organization = $_COOKIE[$source->getAuthId() . '-organization']; } elseif (isset($state['core:organization'])) { $organization = (string)$state['core:organization']; } else { @@ -60,6 +62,14 @@ if ($organizations === NULL || !empty($organization)) { \SimpleSAML\Utils\HTTP::setCookie($source->getAuthId() . '-username', $username, $params, FALSE); } + if ($source->getRememberOrganizationEnabled()) { + $sessionHandler = SimpleSAML_SessionHandler::getSessionHandler(); + $params = $sessionHandler->getCookieParams(); + $params['expire'] = time(); + $params['expire'] += (isset($_REQUEST['remember_organization']) && $_REQUEST['remember_organization'] == 'Yes' ? 31536000 : -300); + setcookie($source->getAuthId() . '-organization', $organization, $params['expire'], $params['path'], $params['domain'], $params['secure'], $params['httponly']); + } + try { \SimpleSAML\Module\core\Auth\UserPassOrgBase::handleLogin($authStateId, $username, $password, $organization); } catch (\SimpleSAML\Error\Error $e) { @@ -80,6 +90,9 @@ $t->data['rememberUsernameChecked'] = $source->getRememberUsernameChecked(); $t->data['rememberMeEnabled'] = false; $t->data['rememberMeChecked'] = false; if (isset($_COOKIE[$source->getAuthId() . '-username'])) $t->data['rememberUsernameChecked'] = TRUE; +$t->data['rememberOrganizationEnabled'] = $source->getRememberOrganizationEnabled(); +$t->data['rememberOrganizationChecked'] = $source->getRememberOrganizationChecked(); +if (isset($_COOKIE[$source->getAuthId() . '-organization'])) $t->data['rememberOrganizationChecked'] = true; $t->data['errorcode'] = $errorCode; $t->data['errorcodes'] = \SimpleSAML\Error\ErrorCodes::getAllErrorCodeMessages(); $t->data['errorparams'] = $errorParams; diff --git a/tests/modules/core/lib/Auth/UserPassOrgBaseTest.php b/tests/modules/core/lib/Auth/UserPassOrgBaseTest.php new file mode 100644 index 000000000..6fb9c523f --- /dev/null +++ b/tests/modules/core/lib/Auth/UserPassOrgBaseTest.php @@ -0,0 +1,41 @@ +<?php + /** + * Created by PhpStorm. + * User: agustin + * Date: 16.10.2017 + * Time: 12:17 + */ + + namespace SimpleSAML\Test\Module\core\Auth; + + use SimpleSAML\Module\core\Auth\UserPassOrgBase; + + class UserPassOrgBaseTest extends \PHPUnit_Framework_TestCase + { + public function testRememberOrganizationEnabled() + { + $config = array( + 'ldap:LDAPMulti', + + 'remember.organization.enabled' => true, + 'remember.organization.checked' => false, + + 'my-org' => array( + 'description' => 'My organization', + // The rest of the options are the same as those available for + // the LDAP authentication source. + 'hostname' => 'ldap://ldap.myorg.com', + 'dnpattern' => 'uid=%username%,ou=employees,dc=example,dc=org', + // Whether SSL/TLS should be used when contacting the LDAP server. + 'enable_tls' => false, + ) + ); + + $mockUserPassOrgBase = $this->getMockBuilder(\sspmod_core_Auth_UserPassOrgBase::class) + ->setConstructorArgs(array(array('AuthId' => 'my-org'), &$config)) + ->setMethods(array()) + ->getMockForAbstractClass(); + + $this->assertTrue($mockUserPassOrgBase->getRememberOrganizationEnabled()); + } + } -- GitLab