[commits] [Wiki] changed: ImapSelect

Wiki Guest wikiguest at horde.org
Sat Nov 17 02:14:38 UTC 2012


guest [71.83.164.200]  Sat, 17 Nov 2012 02:14:38 +0000

Modified page: http://wiki.horde.org/ImapSelect
New Revision:  14
Change log:  Revert

@@ -1,195 +1,149 @@
-+ Dynamically selecting an IMAP server for authentication
++++ Introduction
+
+
+
+During a migration from one IMAP server to another, the need arose to  
run both the old and the new IMAP servers in paralell. By default,
+
+Horde only allows a single primary server to be enabled in  
servers.php. Of course, we could have allowed our users to simply select
+
+their server by enabling IMAP server selection, but there is a better  
way. What follows are instructions on using a !MySQL backend to
+
+select an IMAP server for authentication, given a username:
+
+
+
++++ The SQL table
+
+
+

-++ Introduction

-During a migration from one IMAP server to another, the need arose to  
run both the old and the new IMAP servers in parallel. By default,  
Horde only allows a single primary server to be enabled in  
servers.php. Of course, we could have allowed our users to simply  
select their server by enabling IMAP server selection, but there is a  
better way.
-The first example shows a way using a !MySQL backend to select an  
IMAP server for authentication, given a username.
-The second example instead uses the realmed username to select the  
IMAP server \
-(i.e. the username used by Horde internally to prevent username clashes).
-----

-++ The SQL table

  There are many ways to do this of course. In this example, we'll  
just be using a table with two rows:
+
+

  <code>
+
  ---------------------------------------------------------
-|   username        |     cyrus                         |
+
+|   username        |     server.domain.com             |
+
  ---------------------------------------------------------
+
  </code>

-Your table can be constructed however you like. It can even be a part  
of your existing Horde DB. The important point is that you need the  
table to be constructed in such a way as to be able to query a  
username and have the lookup return a server name from  
{{imp/config/servers.php}}.

-++ Writing a hook
+
+Your table can be constructed however you like. It can even be a part  
of your existing Horde DB. The important point is that you need the
+
+table to be constructed in such a way as to be able to query a  
username and have the lookup return a servername. Our example
+
+uses a table called 'host'.
+
+
+
++++ Writing a hook
+
+
+
+
+
+

  Here's some sample code for a hook placed inside horde/config/hooks.php:

-<code type="php">
-if (!function_exists('_horde_hook_preauthenticate')) {
-    function _horde_hook_preauthenticate($userID, $credential, $realm)
-    {
-        require dirname(__FILE__) . '/../imp/config/servers.php';

-        // Strip domain part from user.
-        $userID = substr($userID, 0, strpos($userID, '@'));
-        // Connect to database server.
-        $db = mysql_connect('hostname', 'dbuser', 'dbpasswd') or  
die('Can not connect to database.');
-        // Select database.
-        mysql_select_db('dbname') or die('Can not select database.');
-        // Execute the query
-        $sth = mysql_query('SELECT server_name FROM users WHERE  
user=\'' . mysql_real_escape_string($userID) . '\'') or die ('Can not  
query database.');
-        // Fetch the server name.
-        $server = mysql_fetch_row($sth);
-        if ($server === false) {
-            die('Can not read user from database.');
-        }

-        // Set IMAP server values.
-        foreach (array('server', 'folders', 'namespace') as $key) {
-            $_SESSION['imp'][$key] = $servers[$server[0]][$key];
-        }
+<code type="php">

-        return true;
-    }
-}
+if (!function_exists('_imp_hook_imap')) {

-</code>
+        function _imp_hook_imap($userName)

-All that's left to do, is to activate the preauthenticate hook in  
Horde's configuration.
-----
+        {

-++ IMAP server selection by realmed username
-+++ Requirements
-* IMP is used to do the authentication
-* The usernames to authenticate against the mailserver may not have a  
domain part (separated with '@' from the username).
-* There is more than one active server entry in {{imp/config/servers.php}}. _
- The realm values are different and the default server has an empty  
string as realm. _
- //**Note:**// The default server is either the first server entry in  
the list or the one with the **preferred** value _
- set.
-* Realm values are only lowercase strings (e.g. 'server1.example.com').
+                global $conf;

-++++ Examplary snippet of {{imp/config/servers.php}}
-<code>/* Example configuration: */
+                include_once 'DB.php';

-$servers['imap'] = array(
-    'name' => 'IMAP Server',
-    'server' => 'imap.example.com',
-    'hordeauth' => true,
-    'protocol' => 'imap/notls',
-    'port' => 143,
-    'maildomain' => 'example.com',
-    'smtphost' => 'smtp.example.com',
-    'smtpport' => 25,
-    'realm' => '',
-    'preferred' => '',
-);
+                $_db = &DB::connect($conf['sql'], true);

-$servers['imap1'] = array(
-    'name' => 'IMAP Server 1',
-    'server' => 'imap1.example.com',
-    'hordeauth' => true,
-    'protocol' => 'imap/ssl/novalidate-cert',
-    'port' => 993,
-    'maildomain' => 'example.com',
-    'smtphost' => 'smtp.example.com',
-    'smtpport' => 25,
-    'realm' => 'imap1.example.com',
-    'preferred' => '',
-);
-</code>
+                $query = sprintf('SELECT server FROM host WHERE  
user=%s', $_db->quote($userName));

-++++ Functionality of the hook
-Entering the username {{smith}} in the login screen selects the  
necessary values given by {{$servers['imap']}}:
-* Authentication against {{imap.example.com}}
-* using protocol {{imap/notls}}
-* ...
+                $result = $_db->getOne($query);

-Entering the username {{smith at imap1.example.com}} instead selects the  
values of {{$servers['imap1']}}:
-* Authentication against {{imap1.example.com}}
-* using protocol {{imap/ssl/novalidate-cert}}
-* ...
+                if (!is_a($result, 'PEAR_Error')) {

-//**Note:**//
-* Horde treats them as different users with their own preferences.
-* The selection of the server is done by the **realm** value **NOT**  
by the **server** value.
+                        return $result;

-+++ The Preauthenticate-Hook
-The following hook has to be inserted in {{config/hooks.php}}:
-<code type="php">if (!function_exists('_horde_hook_preauthenticate')) {
-    function _horde_hook_preauthenticate($userID, $credential, $realm)
-    {
-        require dirname(__FILE__) . '/../imp/config/servers.php';
+                } else {
+
+                        return false;

-        // Convert all to lower chars (even the possible domain part)
-        $userID=strtolower($userID);
-        // Strip domain part from user, if it exists.
-        if (($domainpart = strpos($userID, '@'))) {
-          $server=substr($userID, $domainpart+1);
-          $userID=substr($userID, 0, $domainpart);
-          // Change the values only if a domain part was found ...
-          if (!empty($server)) {
-            foreach ($servers as $serverkey => $curServer) {
-              if (!empty($curServer['realm']) && $server ==  
$curServer['realm']) {
-                // We found an entry, now set IMAP server values.
-                foreach (array('server', 'folders', 'namespace',
-                  'protocol', 'port', 'smtphost', 'smtpport',  
'maildomain') as $key) {
-                  if (isset($servers[$serverkey][$key])) {
-                    $_SESSION['imp'][$key] = $servers[$serverkey][$key];
-                  }
                  }
-                // Now use only the stripped version of the userID to  
logon to the server
-                $_SESSION['imp']['user'] = $userID;
-                // Setup the correct base_protocol
-                $_SESSION['imp']['base_protocol'] =  
$_SESSION['imp']['protocol'];
-                if (($pos = strpos($_SESSION['imp']['protocol'], '/'))) {
-                  $_SESSION['imp']['base_protocol'] =  
substr($_SESSION['imp']['protocol'], 0, $pos);
-                }
-              }
-            }
-          }
+
          }
-        return true;
-    }
-}
+
+}
+
+
+
  </code>

-Like in the first example, the hook has to be activated inside  
{{config/conf.php}}.
-* Either use the administration user interface of Horde (recommended)
-* or set the following entry in {{config/conf.php}} with your favorite editor
-<code>...
-$conf['hooks']['preauthenticate'] = true;
-...
-</code>

-++ The same IMAP server selector hook function in Hode 4  
{{imp/config/hooks.php}}
+
++++ Placing the hook into the session
+
+
+
+
+
+
+
+The above hook returns a single line containing a server name. The  
results are injected into /horde/imp/lib/Session.php as follows:
+
+
+
  <code type="php">
-class IMP_Hooks
-{

-public function preauthenticate($userId, $credentials)
-{
-    //Horde::logMessage('authM: '.$credentials['authMethod'].'  
id='.$userId, 'ERROR');
-    //return true;

-    /* when no userId given */
-    if (empty($userId)) return true;

-    /* list of ALL remote users */
-    $remote_users = array('username at remoteserver.hu' =>  
'remote-imap-servers-key');
+        if (!empty($GLOBALS['conf']['hooks']['imap'])) {
+
+                require_once HORDE_BASE . '/config/hooks.php';
+
+                if (function_exists('_imp_hook_imap')) {
+
+                        $_SESSION['imp']['server'] =  
call_user_func('_imp_hook_imap',  $_SESSION['imp']['user']);
+
+                        }
+
+        }

-    /* local user */
-    if (!array_key_exists($userId, $remote_users)) return true;

-    /* remote user */
-    return array('credentials' => array('server' => $remote_users[$userId],
-                                        'transparent' => true,
-                                        'password' =>  
$GLOBALS['registry']->getAuthCredential('password')
-                                  )
-    );
-}

-}
  </code>

-Set server list to 'hidden' in Imp prefs, and all backend's hordeauth  
to full.

+
+On our setup, this is done right before authentication is attempted  
(right around line 223). As best I can tell, you're fine so long
+
+as you don't have $_SESSION['imp']['server']  overwritten by anything  
else before you get to the call to &Auth::singleton(array('imp',  
'imp'));
+
+
+
++++ Disclaimer
+
+
+
+
+
+
+
+This code works on our setup thus far. It could be wrong, uneccesary,  
foolish, or otherwise idiotic. In short, YMMV.
+
+
+
+Questions? Send them to mp at xmission.com



More information about the commits mailing list