From: Vincent Zanotti Date: Sun, 15 Jun 2008 15:28:01 +0000 (+0200) Subject: Merge branch 'platal-0.9.17' into hruid X-Git-Tag: xorg/0.10.0~86^2~87 X-Git-Url: http://git.polytechnique.org/?a=commitdiff_plain;h=22801bd99c3622a107c7fa0b4338b1447b169973;hp=116e335ab867e35a3bda08aea731cd61410d7e70;p=platal.git Merge branch 'platal-0.9.17' into hruid --- diff --git a/classes/pluser.php b/classes/pluser.php new file mode 100644 index 0000000..f18bac1 --- /dev/null +++ b/classes/pluser.php @@ -0,0 +1,147 @@ +results = $results; + parent::__construct(); + } +} + +// Represents an user of the system, with a special focus on its identification +// (hruid, forlife, and bestalias). +// Note: each implementation of platal-core MUST create a subclass 'User' of +// this abstract PlUser class. +abstract class PlUser +{ + // User's data storage. By convention, null means the information hasn't + // been fetched yet, and false means the information is not available. + + // Main (unique) identifiers. + protected $user_id = null; + protected $hruid = null; + + // User's emails (bestalias should be preferred when sending emails). + protected $forlife = null; + protected $bestalias = null; + + + // Constructs the object from an identifier (hruid/uid/email alias/email + // redirection) and an optionnal array of known user properties. + public function __construct($login, $values = array()) + { + list($this->user_id, $this->hruid) = $this->getLogin($login); + $this->fillFromArray($values); + } + + + // Properties accessors. + public function id() { return $this->user_id; } + public function login() { return $this->hruid; } + abstract public function bestEmail(); + abstract public function forlifeEmail(); + + + // Determines if the @p id is a valid identifier; if so, returns the user_id + // and the hruid. Otherwise raises UserNotFoundException. + abstract protected function getLogin($login); + + // Fills the object from associative arrays containing our data. + // The use case is for arrays got directly from anoter SQL request. + protected function fillFromArray(array $values) + { + foreach ($values as $key => $value) { + if (property_exists($this, $key) && !isset($this->$key)) { + $this->$key = $value; + } + } + } + + + // Returns a valid User object built from the @p id and optionnal @p values, + // or returns false and calls the callback if the @p id is not valid. + public static function get($login, $callback = false) + { + return User::getWithValues($login, array(), $callback); + } + + public static function getWithValues($login, $values, $callback = false) + { + if (!$callback) { + $callback = array(__CLASS__, '_default_user_callback'); + } + + try { + return new User($login, $values); + } catch (UserNotFoundException $e) { + return call_user_func($callback, $login, $e->results); + } + } + + // Alias on get() with the silent callback. + public static function getSilent($login) + { + return User::getWithValues($login, array(), array(__CLASS__, '_silent_user_callback')); + } + + // Returns the forlife emails for @p members. If @p strict mode is enabled, + // it returns the list of validated forlife emails. If strict mode is not, + // it also returns unvalidated values (but still call the callback for them). + public static function getBulkForlifeEmails($logins, $strict = true, $callback = false) + { + if (!is_array($logins)) { + if (strlen(trim($logins)) == 0) { + return null; + } + $logins = explode(' ', $logins); + } + + if ($logins) { + $list = array(); + foreach ($logins as $i => $login) { + if (($user = User::get($login, $callback))) { + $list[$i] = $user->forlifeEmail(); + } else if(!$strict) { + $list[$i] = $login; + } + } + return $list; + } + return null; + } + + // Silent callback for the user lookup -- does nothing. + public static function _silent_user_callback($login, $results) + { + return; + } + + // Default callback for user lookup -- displays an error message w.r.t. the + // number of matching users found. + abstract public static function _default_user_callback($login, $results); +} + +// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: +?> diff --git a/classes/user.php b/classes/user.php new file mode 100644 index 0000000..2d41c43 --- /dev/null +++ b/classes/user.php @@ -0,0 +1,150 @@ +bestalias)) { + global $globals; + $res = XDB::query("SELECT CONCAT(alias, '@{$globals->mail->domain}') + FROM aliases + WHERE FIND_IN_SET('bestalias', flags) + AND id = {?}", $this->user_id); + $this->bestalias = $res->numRows() ? $res->fetchOneCell() : false; + } + return $this->bestalias; + } + + public function forlifeEmail() + { + if (!isset($this->forlife)) { + global $globals; + $res = XDB::query("SELECT CONCAT(alias, '@{$globals->mail->domain}') + FROM aliases + WHERE type = 'a_vie' AND id = {?}", $this->user_id); + $this->forlife = $res->numRows() ? $res->fetchOneCell() : false; + } + return $this->forlife; + } + + // Implementation of the login to array(user_id, hruid) function. + protected function getLogin($login) + { + global $globals; + + // If $data is an integer, fetches directly the result. + if (is_numeric($login)) { + $res = XDB::query("SELECT user_id, hruid FROM auth_user_md5 WHERE user_id = {?}", $login); + if ($res->numRows()) { + return $res->fetchOneRow(); + } + + throw new UserNotFoundException(); + } + + // Checks whether $login is a valid hruid or not. + $res = XDB::query("SELECT user_id, hruid FROM auth_user_md5 WHERE hruid = {?}", $login); + if ($res->numRows()) { + return $res->fetchOneRow(); + } + + // From now, $login can only by an email alias, or an email redirection. + // If it doesn't look like a valid address, appends the plat/al's main domain. + $login = trim(strtolower($login)); + if (strstr($login, '@') === false) { + $login = $login . '@' . $globals->mail->domain; + } + + // Checks if $login is a valid alias on the main domains. + list($mbox, $fqdn) = explode('@', $login); + if ($fqdn == $globals->mail->domain || $fqdn == $globals->mail->domain2) { + $res = XDB::query("SELECT u.user_id, u.hruid + FROM auth_user_md5 AS u + INNER JOIN aliases AS a ON (a.id = u.user_id AND a.type IN ('alias', 'a_vie')) + WHERE a.alias = {?}", $mbox); + if ($res->numRows()) { + return $res->fetchOneRow(); + } + + if (preg_match('/^(.*)\.([0-9]{4})$/u', $mbox, $matches)) { + $res = XDB::query("SELECT u.user_id, u.hruid + FROM auth_user_md5 AS u + INNER JOIN aliases AS a ON (a.id = u.user_id AND a.type IN ('alias', 'a_vie')) + WHERE a.alias = {?} AND u.promo = {?}", $matches[1], $matches[2]); + if ($res->numRows() == 1) { + return $res->fetchOneRow(); + } + } + + throw new UserNotFoundException(); + } + + // Looks for $login as an email alias from the dedicated alias domain. + if ($fqdn == $globals->mail->alias_dom || $fqdn == $globals->mail->alias_dom2) { + $res = XDB::query("SELECT redirect + FROM virtual_redirect + INNER JOIN virtual USING(vid) + WHERE alias = {?}", $mbox . '@' . $globals->mail->alias_dom); + if ($redir = $res->fetchOneCell()) { + // We now have a valid alias, which has to be translated to an hruid. + list($alias, $alias_fqdn) = explode('@', $redir); + $res = XDB::query("SELECT u.user_id, u.hruid + FROM auth_user_md5 AS u + LEFT JOIN aliases AS a ON (a.id = u.user_id AND a.type IN ('alias', 'a_vie')) + WHERE a.alias = {?}", $alias); + if ($res->numRows()) { + return $res->fetchOneRow(); + } + } + + throw new UserNotFoundException(); + } + + // Otherwise, we do suppose $login is an email redirection. + $res = XDB::query("SELECT u.user_id, u.hruid + FROM auth_user_md5 AS u + LEFT JOIN emails AS e ON (e.uid = u.user_id) + WHERE e.email = {?}", $login); + if ($res->numRows() == 1) { + return $res->fetchOneRow(); + } + + throw new UserNotFoundException($res->fetchColumn(1)); + } + + // Implementation of the default user callback. + public static function _default_user_callback($login, $results) + { + global $page; + + $result_count = count($results); + if ($result_count == 0 || !S::has_perms()) { + $page->trigError("Il n'y a pas d'utilisateur avec l'identifiant : $login"); + } else { + $page->trigError("Il y a $result_count utilisateurs avec cet identifiant : " . join(', ', $results)); + } + } +} + +// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: +?> diff --git a/include/user.func.inc.php b/include/user.func.inc.php index b903654..09ffcc1 100644 --- a/include/user.func.inc.php +++ b/include/user.func.inc.php @@ -97,113 +97,158 @@ function _silent_user_callback($login) return; } +// Returns an unique identifier corresponding to the @p data. This piece of data +// can be a numerical id, an hruid, an email alias (any), or a redirection +// email address. If @p get_forlife is set to true, the user's forlife is +// returned, otherwise the user's hruid is returned. +// When no user is found, calls @p callback, and eventually returns false. function get_user_login($data, $get_forlife = false, $callback = '_default_user_callback') { global $globals, $page; + // In order to reduce the code size & complexity, we define once for all the + // field to be returned. By convention if will be "u.hruid" for the hruid + // (thus implying the auth_user_md5 will be aliased on u), and "a.alias" for + // the forlife (thus implying the forlife aliases table will be aliased on a). + $field = ($get_forlife ? "CONCAT(a.alias, '@" . $globals->mail->domain . "')" : "u.hruid"); + + // If $data is an integer, fetches directly the result. if (is_numeric($data)) { - $res = XDB::query("SELECT alias FROM aliases WHERE type='a_vie' AND id={?}", $data); + $res = XDB::query("SELECT $field + FROM auth_user_md5 AS u + LEFT JOIN aliases AS a ON (a.id = u.user_id AND type = 'a_vie') + WHERE u.user_id = {?}", $data); if ($res->numRows()) { return $res->fetchOneCell(); - } else { - call_user_func($callback, $data); - return false; } + + call_user_func($callback, $data); + return false; } - $data = trim(strtolower($data)); + // Checks whether $data is a valid hruid or not. + $res = XDB::query("SELECT $field + FROM auth_user_md5 AS u + LEFT JOIN aliases AS a ON (a.id = u.user_id AND a.type = 'a_vie') + WHERE u.hruid = {?}", $data); + if ($res->numRows()) { + return $res->fetchOneCell(); + } + // From now, $data can only by an email alias, or an email redirection. + // If it doesn't look like a valid address, appends the plat/al's main domain. + $data = trim(strtolower($data)); if (strstr($data, '@')===false) { - $data = $data.'@'.$globals->mail->domain; + $data = $data . '@' . $globals->mail->domain; } + // Checks if $data is a valid alias on the main domains. list($mbox, $fqdn) = explode('@', $data); if ($fqdn == $globals->mail->domain || $fqdn == $globals->mail->domain2) { - - $res = XDB::query("SELECT a.alias - FROM aliases AS a - INNER JOIN aliases AS b ON (a.id = b.id AND b.type IN ('alias', 'a_vie') AND b.alias={?}) - WHERE a.type = 'a_vie'", $mbox); + $res = XDB::query("SELECT $field + FROM auth_user_md5 AS u + INNER JOIN aliases AS a ON (a.id = u.user_id AND a.type = 'a_vie') + INNER JOIN aliases AS b ON (b.id = u.user_id AND b.type IN ('alias', 'a_vie')) + WHERE b.alias = {?}", $mbox); if ($res->numRows()) { - return $get_forlife ? $res->fetchOneCell() : $mbox; + return $res->fetchOneCell(); } if (preg_match('/^(.*)\.([0-9]{4})$/u', $mbox, $matches)) { $res = XDB::query("SELECT a.alias - FROM aliases AS a - INNER JOIN aliases AS b ON (a.id = b.id AND b.type IN ('alias', 'a_vie') AND b.alias={?}) - INNER JOIN auth_user_md5 AS u ON (a.id = u.user_id AND promo = {?}) - WHERE a.type = 'a_vie'", $matches[1], $matches[2]); + FROM auth_user_md5 AS u + INNER JOIN aliases AS a ON (a.id = u.user_id AND a.type = 'a_vie') + INNER JOIN aliases AS b ON (b.id = u.user_id AND b.type IN ('alias', 'a_vie')) + WHERE b.alias = {?} AND u.promo = {?}", $matches[1], $matches[2]); if ($res->numRows() == 1) { return $res->fetchOneCell(); } } + call_user_func($callback, $data); return false; + // Looks for $data as an email alias from the dedicated alias domain. } elseif ($fqdn == $globals->mail->alias_dom || $fqdn == $globals->mail->alias_dom2) { - $res = XDB::query("SELECT redirect FROM virtual_redirect INNER JOIN virtual USING(vid) - WHERE alias={?}", $mbox.'@'.$globals->mail->alias_dom); + WHERE alias = {?}", $mbox . '@' . $globals->mail->alias_dom); if ($redir = $res->fetchOneCell()) { - list($alias) = explode('@', $redir); - } else { - call_user_func($callback, $data); - $alias = false; - } - return $alias; - } else { - - $res = XDB::query("SELECT alias - FROM aliases AS a - INNER JOIN emails AS e ON e.uid=a.id - WHERE e.email={?} AND a.type='a_vie'", $data); - switch ($i = $res->numRows()) { - case 0: - call_user_func($callback, $data); - return false; + list($alias, $alias_fqdn) = explode('@', $redir); + if ($get_forlife) { + // It might happen that the "secondary" forlife alias (the one + // based on the secondary domaine name) is used as a target; we + // then need to canonicalize it to the main domain. + if ($alias_fqdn == $globals->mail->domain2) { + return $alias . "@" . $globals->mail->domain; + } + return $redir; + } - case 1: + // We now have a valid alias, which has to be translated to an hruid. + $res = XDB::query("SELECT u.hruid + FROM auth_user_md5 AS u + LEFT JOIN aliases AS a ON (a.id = u.user_id AND a.type IN ('alias', 'a_vie')) + WHERE a.alias = {?}", $alias); + if ($res->numRows()) { return $res->fetchOneCell(); + } + } - default: - if (S::has_perms()) { - $aliases = $res->fetchColumn(); - $page->trigError("Il y a $i utilisateurs avec cette adresse mail : ".join(', ', $aliases)); - } else { - $res->free(); - } + call_user_func($callback, $data); + return false; + + // Otherwise, we do suppose $data is an email redirection. + } else { + $res = XDB::query("SELECT $field + FROM auth_user_md5 AS u + LEFT JOIN aliases AS a ON (a.id = u.user_id AND a.type = 'a_vie') + LEFT JOIN emails AS e ON (e.uid = u.user_id) + WHERE e.email = {?}", $data); + if ($res->numRows() == 1) { + return $res->fetchOneCell(); + } else if ($res->numRows() > 0) { + if (S::has_perms()) { + $page->trigError("Il y a $user_count utilisateurs avec cette adresse mail : " . join(', ', $res->fetchColumn())); + } else { + $res->free(); + } + } else { + call_user_func($callback, $data); } + + return false; } return false; } // }}} -// {{{ function get_user_forlife() - -function get_user_forlife($data, $callback = '_default_user_callback') +// {{{ function get_users_login_list() + +// Returns an array of valid forlife/hruid based on the @p members list. The +// list can be an array (in this case the ouput will retain the keys), or a +// space separated list. +// The @p strict indicates if the input alias should be retain in output when +// no valid forlife is found (incompatible with $get_forlife = false). +function get_users_login_list($members, $strict = false, $get_forlife = false, $callback = '_default_user_callback') { - return get_user_login($data, true, $callback); -} - -// }}} -// {{{ function get_users_forlife_list() + if (!$get_forlife) { + $strict = true; + } -function get_users_forlife_list($members, $strict = false, $callback = '_default_user_callback') -{ if (!is_array($members)) { if (strlen(trim($members)) == 0) { return null; } $members = explode(' ', $members); } + if ($members) { $list = array(); foreach ($members as $i => $alias) { - if (($login = get_user_forlife($alias, $callback)) !== false) { + if (($login = get_user_login($alias, $get_forlife, $callback)) !== false) { $list[$i] = $login; } else if(!$strict) { $list[$i] = $alias; @@ -215,6 +260,38 @@ function get_users_forlife_list($members, $strict = false, $callback = '_default } // }}} +// {{{ function get_user_forlife() + +function get_user_forlife($data, $callback = '_default_user_callback') +{ + return get_user_login($data, true, $callback); +} + +// }}} +// {{{ function get_users_forlife_list() + +function get_users_forlife_list($members, $strict = false, $callback = '_default_user_callback') +{ + return get_users_login_list($members, $strict, true, $callback); +} + +// }}} +// {{{ function get_user_hruid() + +function get_user_hruid($data, $callback = '_default_user_callback') +{ + return get_user_login($data, false, $callback); +} + +// }}} +// {{{ function get_users_hruid_list() + +function get_users_hruid_list($members, $strict = false, $callback = '_default_user_callback') +{ + return get_users_login_list($members, true, false, $callback); +} + +// }}} // {{{ function has_user_right() function has_user_right($pub, $view = 'private') { if ($pub == $view) return true; diff --git a/include/xnet/session.inc.php b/include/xnet/session.inc.php index 92c189a..1441f19 100644 --- a/include/xnet/session.inc.php +++ b/include/xnet/session.inc.php @@ -119,11 +119,11 @@ class XnetSession $res = XDB::query(" SELECT u.user_id AS uid, prenom, nom, perms, promo, password, FIND_IN_SET('femme', u.flags) AS femme, - a.alias AS forlife, a2.alias AS bestalias, q.core_mail_fmt AS mail_fmt, q.core_rss_hash + u.hruid, a.alias AS forlife, a2.alias AS bestalias, q.core_mail_fmt AS mail_fmt, q.core_rss_hash FROM auth_user_md5 AS u INNER JOIN auth_user_quick AS q USING(user_id) - INNER JOIN aliases AS a ON (u.user_id = a.id AND a.type='a_vie') - INNER JOIN aliases AS a2 ON (u.user_id = a2.id AND FIND_IN_SET('bestalias',a2.flags)) + INNER JOIN aliases AS a ON (u.user_id = a.id AND a.type = 'a_vie') + INNER JOIN aliases AS a2 ON (u.user_id = a2.id AND FIND_IN_SET('bestalias', a2.flags)) WHERE u.user_id = {?} AND u.perms IN('admin','user') LIMIT 1", Get::i('uid')); $_SESSION = array_merge($_SESSION, $res->fetchOneAssoc()); diff --git a/include/xorg/session.inc.php b/include/xorg/session.inc.php index 34581a1..58850c9 100644 --- a/include/xorg/session.inc.php +++ b/include/xorg/session.inc.php @@ -65,7 +65,7 @@ class XorgSession // ou passwordpromptscreenlogged.tpl if (S::has('suid')) { $suid = S::v('suid'); - $login = $uname = $suid['forlife']; + $login = $uname = $suid['hruid']; $redirect = false; } else { $uname = Env::v('username'); @@ -241,13 +241,13 @@ function start_connexion ($uid, $identified) $res = XDB::query(" SELECT u.user_id AS uid, prenom, prenom_ini, nom, nom_ini, nom_usage, perms, promo, promo_sortie, matricule, password, FIND_IN_SET('femme', u.flags) AS femme, - a.alias AS forlife, a2.alias AS bestalias, + u.hruid, a.alias AS forlife, a2.alias AS bestalias, q.core_mail_fmt AS mail_fmt, UNIX_TIMESTAMP(q.banana_last) AS banana_last, q.watch_last, q.core_rss_hash, FIND_IN_SET('watch', u.flags) AS watch_account, q.last_version FROM auth_user_md5 AS u INNER JOIN auth_user_quick AS q USING(user_id) - INNER JOIN aliases AS a ON (u.user_id = a.id AND a.type='a_vie') - INNER JOIN aliases AS a2 ON (u.user_id = a2.id AND FIND_IN_SET('bestalias',a2.flags)) + INNER JOIN aliases AS a ON (u.user_id = a.id AND a.type = 'a_vie') + INNER JOIN aliases AS a2 ON (u.user_id = a2.id AND FIND_IN_SET('bestalias', a2.flags)) WHERE u.user_id = {?} AND u.perms IN('admin','user')", $uid); $sess = $res->fetchOneAssoc(); $res = XDB::query("SELECT UNIX_TIMESTAMP(s.start) AS lastlogin, s.host @@ -262,7 +262,7 @@ function start_connexion ($uid, $identified) if ($suid) { $logger = new CoreLogger($uid, $suid['uid']); - $logger->log("suid_start", S::v('forlife')." by {$suid['uid']}"); + $logger->log("suid_start", S::v('hruid') . " by {$suid['uid']}"); $sess['suid'] = $suid; } else { $logger = S::v('log', new CoreLogger($uid)); diff --git a/modules/carnet.php b/modules/carnet.php index febcf3b..39e645e 100644 --- a/modules/carnet.php +++ b/modules/carnet.php @@ -236,7 +236,7 @@ class CarnetModule extends PLModule case 'ajouter': require_once('user.func.inc.php'); - if (($login = get_user_login($user)) !== false) { + if (($login = get_user_forlife($user)) !== false) { if (XDB::execute( 'REPLACE INTO contacts (uid, contact) SELECT {?}, id diff --git a/upgrade/0.9.17/03_hruid.sql b/upgrade/0.9.17/03_hruid.sql new file mode 100644 index 0000000..49b0f78 --- /dev/null +++ b/upgrade/0.9.17/03_hruid.sql @@ -0,0 +1,13 @@ +# Creates a new column for the hruid field, and adds an index on it. +ALTER TABLE auth_user_md5 + ADD COLUMN hruid VARCHAR(255) DEFAULT NULL AFTER user_id, + ADD UNIQUE INDEX hruid(hruid); + + +# Pre-fills the hruid field with the current forlife. + UPDATE auth_user_md5 AS u +LEFT JOIN aliases AS a ON (a.id = u.user_id AND a.type = 'a_vie') + SET u.hruid = a.alias + WHERE a.alias IS NOT NULL AND u.hruid IS NULL; + +# vim:set syntax=mysql: diff --git a/upgrade/0.9.17/connect.db.inc.php b/upgrade/0.9.17/connect.db.inc.php new file mode 100644 index 0000000..2e66895 --- /dev/null +++ b/upgrade/0.9.17/connect.db.inc.php @@ -0,0 +1,28 @@ + diff --git a/upgrade/0.9.17/hruid.update.php b/upgrade/0.9.17/hruid.update.php new file mode 100755 index 0000000..8edb887 --- /dev/null +++ b/upgrade/0.9.17/hruid.update.php @@ -0,0 +1,20 @@ +#!/usr/bin/php5 +next()) { + $forlife = make_forlife($prenom, $nom, $promo); + if (!XDB::execute("UPDATE auth_user_md5 SET hruid = {?} WHERE user_id = {?}", $forlife, $user_id)) { + echo "WARNING: Duplicate forlife for user $user_id and forlife '$forlife'. Please check manually the entry.\n"; + } +} +?> diff --git a/upgrade/0.9.17/update.sh b/upgrade/0.9.17/update.sh index f351939..0e99620 100755 --- a/upgrade/0.9.17/update.sh +++ b/upgrade/0.9.17/update.sh @@ -16,4 +16,9 @@ do done ########################################################### +echo "Creating forlife ids for unregistered user (takes a while)." + +./hruid.update.php + +###########################################################