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