[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