Alternc  latest
Alternc logiel libre pour l'hébergement
m_mem.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  ----------------------------------------------------------------------
5  LICENSE
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License (GPL)
9  as published by the Free Software Foundation; either version 2
10  of the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  To read the license please visit http://www.gnu.org/copyleft/gpl.html
18  ----------------------------------------------------------------------
19 */
20 
21 /**
22  * This class manage user sessions in the web desktop.
23  *
24  * This class manage user sessions and administration in AlternC.
25  *
26  * @copyright AlternC-Team 2000-2017 https://alternc.com/
27  */
28 class m_mem {
29 
30  /** Original uid for the temporary uid swapping (for administrators) */
31  var $olduid = 0;
32 
33  /**
34  * This array contains the Tableau contenant les champs de la table "membres" du membre courant
35  */
36  var $user;
37 
38  /**
39  * contains all the fields of the "local" table for an account in AlternC.
40  * they are specific to the hosting provider
41  */
42  var $local;
43 
44  /**
45  * Password kind used in this class (hook for admin class)
46  */
48  return array("mem" => "AlternC's account password");
49  }
50 
51 
52  /**
53  * hook called by the m_menu class to add menu to the left of the panel
54  */
55  function hook_menu() {
56  $obj = array(
57  'title' => _("Settings"),
58  'link' => 'mem_param.php',
59  'pos' => 160,
60  );
61 
62  return $obj;
63  }
64 
65 
66  /**
67  * Check that the current user is an admnistrator.
68  * @return boolean TRUE if we are super user, or FALSE if we are not.
69  */
70  function checkright() {
71  return ($this->user["su"] == "1");
72  }
73 
74 
75  /**
76  * Start a session in the web desktop. Check username and password.
77  * <b>Note : </b>If the user entered a bas password, the failure will be logged
78  * and told to the corresponding user on next successfull login.
79  * @param $username string Username that want to get connected.
80  * @param $password string User Password.
81  * @return boolean TRUE if the user has been successfully connected, or FALSE if an error occured.
82  */
83  function login($username, $password, $restrictip = 0, $authip_token = false) {
84  global $db, $msg, $cuid, $authip;
85  $msg->log("mem", "login", $username);
86  if ($msg->has_msgs("ERROR")) return false;
87 
88  $db->query("select * from membres where login= ? ;", array($username));
89  if ($db->num_rows() == 0) {
90  $msg->raise("ERROR", "mem", _("User or password incorrect"));
91  return false;
92  }
93  $db->next_record();
94  if (!password_verify($password, $db->f('pass'))) {
95  $db->query("UPDATE membres SET lastfail=lastfail+1 WHERE uid= ? ;", array($db->f("uid")));
96  $msg->raise("ERROR", "mem", _("User or password incorrect"));
97  return false;
98  }
99  if (!$db->f("enabled")) {
100  $msg->raise("ERROR", "mem", _("This account is locked, contact the administrator."));
101  return false;
102  }
103  $this->user = $db->Record;
104  $cuid = $db->f("uid");
105  // Transitional code to update md5 hashed passwords to those created
106  // with password_hash().
107  if (strncmp($db->f('pass'), '$1$', 3) == 0) {
108  $db->query("update membres set pass = ? where uid = ?",
109  array(password_hash($password, PASSWORD_BCRYPT), $cuid));
110  }
111 
112  if (panel_islocked() && $cuid != 2000) {
113  $msg->raise("ALERT", "mem", _("This website is currently under maintenance, login is currently disabled."));
114  return false;
115  }
116 
117  // AuthIP
118  $allowed_ip = false;
119  if ($authip_token) {
120  $allowed_ip = $this->authip_tokencheck($authip_token);
121  }
122 
123  $aga = $authip->get_allowed('panel');
124  foreach ($aga as $k => $v) {
125  if ($authip->is_in_subnet(get_remote_ip(), $v['ip'], $v['subnet'])) {
126  $allowed = true;
127  }
128  }
129 
130  // Error if there is rules, the IP is not allowed and it's not in the whitelisted IP
131  if (sizeof($aga) > 1 && !$allowed_ip && !$authip->is_wl(get_remote_ip())) {
132  $msg->raise("ERROR", "mem", _("Your IP isn't allowed to connect"));
133  return false;
134  }
135  // End AuthIP
136 
137  if ($restrictip) {
138  $ip = get_remote_ip();
139  } else {
140  $ip = "";
141  }
142  /* Close sessions that are more than 2 days old. */
143  $db->query("DELETE FROM sessions WHERE DATE_ADD(ts,INTERVAL 2 DAY)<NOW();");
144  /* Delete old impersonation */
145  if (isset($_COOKIE["oldid"])) {
146  setcookie('oldid', '', 0, '/');
147  }
148  /* Open the session : */
149  $sess = md5(mt_rand().mt_rand().mt_rand());
150  $_REQUEST["session"] = $sess;
151  $db->query("insert into sessions (sid,ip,uid) values (?, ?, ?);", array($sess, $ip, $cuid));
152  setcookie("session", $sess, 0, "/");
153  $msg->init_msgs();
154  /* Fill in $local */
155  $db->query("SELECT * FROM local WHERE uid= ? ;", array($cuid));
156  if ($db->num_rows()) {
157  $db->next_record();
158  $this->local = $db->Record;
159  }
160  $this->resetlast();
161  return true;
162  }
163 
164 
165  /**
166  * Start a session as another user from an administrator account.
167  * This function is not the same as su. setid connect the current user in the destination
168  * account (for good), and su allow any user to become another account for some commands only.
169  * (del_user, add_user ...) and allow to bring back admin rights with unsu
170  *
171  * @param $id integer User id where we will connect to.
172  * @return boolean TRUE if the user has been successfully connected, FALSE else.
173  */
174  function setid($id) {
175  global $db, $msg, $cuid, $mysql, $quota;
176  $msg->log("mem", "setid", $id);
177  $db->query("select * from membres where uid= ? ;", array($id));
178  if ($db->num_rows() == 0) {
179  $msg->raise("ERROR", "mem", _("User or password incorrect"));
180  return false;
181  }
182  $db->next_record();
183  $this->user = $db->Record;
184  $cuid = $db->f("uid");
185  // And recreate the $db->dbus
186  $mysql->reload_dbus();
187 
188  $ip = get_remote_ip();
189  $sess = md5(mt_rand().mt_rand().mt_rand());
190  $_REQUEST["session"] = $sess;
191  $db->query("insert into sessions (sid,ip,uid) values (?, ?, ?);", array($sess, $ip, $cuid));
192  setcookie("session", $sess, 0, "/");
193  $msg->init_msgs();
194  /* Fill in $local */
195  $db->query("SELECT * FROM local WHERE uid= ? ;", array($cuid));
196  if ($db->num_rows()) {
197  $db->next_record();
198  $this->local = $db->Record;
199  }
200  $quota->getquota('', true);
201  return true;
202  }
203 
204 
205  /**
206  * After a successful connection, reset the user's last connection date
207  */
208  function resetlast() {
209  global $db, $cuid;
210  $ip = getenv("REMOTE_HOST");
211  if (!$ip) {
212  $ip = get_remote_ip();
213  }
214  $db->query("UPDATE membres SET lastlogin=NOW(), lastfail=0, lastip= ? WHERE uid= ?;", array($ip, $cuid));
215  }
216 
217 
218  function authip_token($bis = false) {
219  global $db, $cuid;
220  $db->query("select pass from membres where uid= ?;", array($cuid));
221  $db->next_record();
222  $i = intval(time() / 3600);
223  if ($bis) {
224  ++$i;
225  }
226  return md5("$i--" . $db->f('pass'));
227  }
228 
229 
230  /**
231  * @param boolean $t
232  */
233  function authip_tokencheck($t) {
234  return ($t == $this->authip_token() || $t == $this->authip_token(true));
235  }
236 
237  /* Faut finir de l'implementer :) * /
238  function authip_class() {
239  global $cuid;
240  $c = Array();
241  $c['name']="Panel access";
242  $c['protocol']="mem";
243  $c['values']=Array($cuid=>'');
244 
245  return $c;
246  }
247  /* */
248 
249 
250  /**
251  * Check that the current session is correct (valid cookie)
252  * If necessary, and if we received username & password fields,
253  * create a new session for the user.
254  * This function MUST be called by each page to authenticate the user.
255  * and BEFORE sending any data (since a cookie can be sent)
256  * @global string $session the session cookie
257  * @global string $username & $password the login / pass of the user
258  * @return boolean TRUE if the session is OK, FALSE if it is not.
259  */
260  function checkid($show_msg = true) {
261  global $db, $msg, $cuid;
262 
263  // We may go here *twice* when login fails. We prevent this with a static variable;
264  static $already=false;
265  if ($already) return false;
266  $already=true;
267 
268  if (isset($_REQUEST["username"])) {
269  if (empty($_REQUEST['password'])) {
270  $msg->raise("ERROR", "mem", _("Missing password"));
271  return false;
272  }
273  if ($_REQUEST["username"] && $_REQUEST["password"]) {
274  return $this->login($_REQUEST["username"], $_REQUEST["password"], (isset($_REQUEST["restrictip"]) ? $_REQUEST["restrictip"] : 0));
275  }
276  } // end isset
277 
278  $_COOKIE["session"] = isset($_COOKIE["session"]) ? $_COOKIE["session"] : "";
279 
280  if (strlen($_COOKIE["session"]) != 32) {
281  if ($show_msg)
282  $msg->raise("ERROR", "mem", _("Identity lost or unknown, please login"));
283  return false;
284  }
285 
286  $ip = get_remote_ip();
287  $db->query("select uid, ? as me,ip from sessions where sid= ?;", array($ip, $_COOKIE["session"]));
288  if ($db->num_rows() == 0) {
289  if ($show_msg)
290  $msg->raise("ERROR", "mem", _("Identity lost or unknown, please login"));
291  return false;
292  }
293  $db->next_record();
294  $cuid = $db->f("uid");
295 
296  if (panel_islocked() && $cuid != 2000) {
297  $msg->raise("ALERT", "mem", _("This website is currently under maintenance, login is currently disabled."));
298  return false;
299  }
300 
301  $db->query("select * from membres where uid= ? ;", array($cuid));
302  $db->next_record();
303  $this->user = $db->Record;
304 
305  /* Fills $local */
306  $db->query("SELECT * FROM local WHERE uid= ? ;", array($cuid));
307  if ($db->num_rows()) {
308  $db->next_record();
309  $this->local = $db->Record;
310  }
311  return true;
312  }
313 
314 
315  /**
316  * Change the identity of the user temporarily (SUDO)
317  * @global string $uid User that we want to impersonate
318  * @return boolean TRUE if it's okay, FALSE if it's not.
319  */
320  function su($uid) {
321  global $cuid, $db, $msg, $mysql;
322  if (!$this->olduid) {
323  $this->olduid = $cuid;
324  }
325  $db->query("select * from membres where uid= ? ;", array($uid));
326  if ($db->num_rows() == 0) {
327  $msg->raise("ERROR", "mem", _("User or password incorrect"));
328  return false;
329  }
330  $db->next_record();
331  $this->user = $db->Record;
332  $cuid = $db->f("uid");
333 
334  // And recreate the $db->dbus
335  $mysql->reload_dbus();
336  return true;
337  }
338 
339 
340  /**
341  * Goes back to the original identity (of an admin, usually)
342  * @return boolean TRUE if it's okay, FALSE if it's not.
343  */
344  function unsu() {
345  global $mysql;
346  if (!$this->olduid) {
347  return false;
348  }
349  $this->su($this->olduid);
350  $this->olduid = 0;
351  // And recreate the $db->dbus
352  $mysql->reload_dbus();
353  return true;
354  }
355 
356 
357  /**
358  * Ends a session on the panel (logout)
359  * @return boolean TRUE if it's okay, FALSE if it's not.
360  */
361  function del_session() {
362  global $db, $user, $msg, $cuid, $hooks;
363  $_COOKIE["session"] = isset($_COOKIE["session"]) ? $_COOKIE["session"] : '';
364  setcookie("session", "", 0, "/");
365  setcookie("oldid", "", 0, "/");
366  if ($_COOKIE["session"] == "") {
367  return true;
368  }
369  if (strlen($_COOKIE["session"]) != 32) {
370  return false;
371  }
372  $ip = get_remote_ip();
373  $db->query("select uid, ? as me,ip from sessions where sid= ? ;", array($ip, $_COOKIE["session"]));
374  if ($db->num_rows() == 0) {
375  return false;
376  }
377  $db->next_record();
378  $cuid = $db->f("uid");
379  $db->query("delete from sessions where sid= ? ;", array($_COOKIE["session"]));
380 
381  $hooks->invoke("alternc_del_session");
382 
383  session_unset();
384  @session_destroy();
385  return true;
386  }
387 
388 
389  /**
390  * Change the password of the current user
391  * @param string $oldpass Old password
392  * @param string $newpass New password
393  * @param string $newpass2 New password (again)
394  * @return boolean TRUE if the password has been change, FALSE if not.
395  */
396  function passwd($oldpass, $newpass, $newpass2) {
397  global $db, $msg, $cuid, $admin;
398  $msg->log("mem", "passwd");
399  if (!$this->user["canpass"]) {
400  $msg->raise("ERROR", "mem", _("You are not allowed to change your password."));
401  return false;
402  }
403 
404  if ($this->requires_old_password_for_change()) {
405  if (!password_verify($oldpass, $this->user['pass'])) {
406  $msg->raise("ERROR", "mem", _("The old password is incorrect"));
407  return false;
408  }
409  }
410 
411  if ($newpass != $newpass2) {
412  $msg->raise("ERROR", "mem", _("The new passwords are differents, please retry"));
413  return false;
414  }
415  $db->query("SELECT login FROM membres WHERE uid= ? ;", array($cuid));
416  $db->next_record();
417  $login = $db->Record["login"];
418  if (!$admin->checkPolicy("mem", $login, $newpass)) {
419  return false; // The error has been raised by checkPolicy()
420  }
421  $newpass = password_hash($newpass, PASSWORD_BCRYPT);
422  $db->query("UPDATE membres SET pass= ? WHERE uid= ?;", array($newpass, $cuid));
423  $msg->init_msgs();
424  setcookie('require_old_password', '', 1);
425  return true;
426  }
427 
428 
429  /**
430  * Change the administrator preferences of an admin account
431  * @param integer $admlist visualisation mode of the account list (0=large 1=short)
432  * @return boolean TRUE if the preferences has been changed, FALSE if not.
433  */
434  function adminpref($admlist) {
435  global $db, $msg, $cuid;
436  $msg->log("mem", "admlist");
437  if (!$this->user["su"]) {
438  $msg->raise("ERROR", "mem", _("You must be a system administrator to do this."));
439  return false;
440  }
441  $db->query("UPDATE membres SET admlist= ? WHERE uid= ?;", array($admlist, $cuid));
442  $msg->init_msgs();
443  return true;
444  }
445 
446 
447  /**
448  * Send a mail with a password to an account
449  * <b>Note : </b>We can ask for a password only once a day
450  * TODO : Translate this mail into the localization program.
451  * TODO : Check this function's !
452  * @return boolean TRUE if the password has been sent, FALSE if not.
453  */
454  function send_pass($login) {
455  global $msg, $db, $L_HOSTING, $L_FQDN;
456  $msg->log("mem", "send_pass");
457  $db->query("SELECT * FROM membres WHERE login= ? ;", array($login));
458  if (!$db->num_rows()) {
459  $msg->raise("ERROR", "mem", _("This account is locked, contact the administrator."));
460  return false;
461  }
462  $db->next_record();
463  if (time() - $db->f("lastaskpass") < 86400) {
464  $msg->raise("ERROR", "mem", _("The new passwords are differents, please retry"));
465  return false;
466  }
467  $txt = sprintf(_("Hello,
468 
469 You requested the modification of your password for your
470 account %s on %s
471 Here are your username and password to access the panel :
472 
473 --------------------------------------
474 
475 Username : %s
476 Password : %s
477 
478 --------------------------------------
479 
480 Note : if you didn't requested that modification, it means that
481 someone did it instead of you. You can choose to ignore this message.
482 If it happens again, please contact your server's Administrator.
483 
484 Cordially.
485 "), $login, $L_HOSTING, $db->f("login"), $db->f("pass"));
486  mail($db->f("mail"), "Your password on $L_HOSTING", $txt, "From: postmaster@$L_FQDN\nReply-to: postmaster@$L_FQDN");
487  $db->query("UPDATE membres SET lastaskpass= ? WHERE login= ? ;", array(time(), $login));
488  return true;
489  }
490 
491 
492  /**
493  * Change the email of an account (first step: sending of a Cookie)
494  * TODO : insert this mail string into the localization system
495  * @param string $newmail New mail we want to set for this account
496  * @return boolean TRUE if the email with a link has been sent, FALSE if not
497  */
498  function ChangeMail1($newmail) {
499  global $msg, $db, $L_HOSTING, $L_FQDN, $cuid;
500  $msg->log("mem", "changemail1", $newmail);
501  $db->query("SELECT * FROM membres WHERE uid= ? ;", array($cuid));
502  if (!$db->num_rows()) {
503  $msg->raise("ERROR", "mem", _("This account is locked, contact the administrator."));
504  return false;
505  }
506  $db->next_record();
507 
508  // un cookie de 20 caract�res pour le mail
509  $COOKIE = substr(md5(mt_rand().mt_rand()), 0, 20);
510  // et de 6 pour la cl� � entrer. ca me semble suffisant...
511  $KEY = substr(md5(mt_rand().mt_rand()), 0, 6);
512  $link = "https://$L_FQDN/mem_cm.php?usr=$cuid&cookie=$COOKIE&cle=$KEY";
513  $txt = sprintf(_("Hello,
514 
515 Someone (maybe you) requested an email's address modification of the account
516 %s on %s
517 To confirm your request, go to this url :
518 
519 %s
520 
521 (Warning : if this address is displayed on 2 lines, don't forgot to
522 take it on one line).
523 The panel will ask you the key given when the email address
524 modification was requested.
525 
526 If you didn't asked for this modification, it means that someone
527 did it instead of you. You can choose to ignore this message. If it happens
528 again, please contact your server's administrator.
529 
530 Cordially.
531 "), $db->f("login"), $L_HOSTING, $link);
532  mail($newmail, "Email modification request on $L_HOSTING", $txt, "From: postmaster@$L_FQDN\nReply-to: postmaster@$L_FQDN");
533 
534  $db->query("DELETE FROM chgmail WHERE uid= ? ;", array($cuid));
535  $db->query("INSERT INTO chgmail (cookie,ckey,uid,mail,ts) VALUES ( ?, ?, ?, ?, ?);", array($COOKIE, $KEY, $cuid, $newmail, time()));
536 
537  $lts = time() - 86400;
538  $db->query("DELETE FROM chgmail WHERE ts< ? ;", array($lts));
539  return $KEY;
540  }
541 
542 
543  /**
544  * Change the email of a member (second step, Cookie + key change)
545  * @param string $COOKIE Cookie sent by mail
546  * @param string $KEY cle shown on the screen
547  * @param integer $uid User id (we may not be connected)
548  * @return boolean TRUE if the email has been changed, FALSE if not.
549  */
550  function ChangeMail2($COOKIE, $KEY, $uid) {
551  global $msg, $db;
552  $msg->log("mem", "changemail2", $uid);
553  $db->query("SELECT * FROM chgmail WHERE cookie= ? and ckey= ? and uid= ?;", array($COOKIE, $KEY, $uid));
554  if (!$db->num_rows()) {
555  $msg->raise("ERROR", "mem", _("The information you entered is incorrect."));
556  return false;
557  }
558  $db->next_record();
559 
560  // met a jour le compte :
561  $db->query("UPDATE membres SET mail= ? WHERE uid = ? ;", array($db->f("mail"), $uid));
562 
563  $db->query("DELETE FROM chgmail WHERE uid= ? ;", array($uid));
564  // Supprime les cookies de la veille :)
565  $lts = time() - 86400;
566  $db->query("DELETE FROM chgmail WHERE ts< ? ;", array($lts));
567  return true;
568  }
569 
570 
571  /**
572  * Change the help parameter
573  * @param integer $show Shall we (1) or not (0) show the online help
574  */
575  function set_help_param($show) {
576  global $db, $msg, $cuid;
577  $msg->log("mem", "set_help_param", $show);
578  $db->query("UPDATE membres SET show_help= ? WHERE uid= ? ;", array($show, $cuid));
579  }
580 
581 
582  /**
583  * tell if the help parameter is set
584  * @return boolean TRUE if the account want online help, FALSE if not.
585  */
586  function get_help_param() {
587  return $this->user["show_help"];
588  }
589 
590 
591  /**
592  * show (echo) a contextual help
593  * @param integer $file File number in the help system to show
594  * @return boolean TRUE if the help has been shown, FALSE if not.
595  */
596  function show_help($file, $force = false) {
597  if ($this->user["show_help"] || $force) {
598  $hlp = _("hlp_$file");
599  if ($hlp != "hlp_$file") {
600  $hlp = preg_replace(
601  "#HELPID_([0-9]*)#", "<a href=\"javascript:help(\\1);\"><img src=\"/aide/help.png\" width=\"17\" height=\"17\" style=\"vertical-align: middle;\" alt=\"" . _("Help") . "\" /></a>", $hlp);
602  echo "<p class=\"hlp\">" . $hlp . "</p>";
603  return true;
604  }
605  return false;
606  } else {
607  return true;
608  }
609  }
610 
611 
612  /**
613  * @param integer $uid
614  */
615  function get_creator_by_uid($uid) {
616  global $db, $msg;
617  $msg->debug("dom", "get_creator_by_uid");
618  $db->query("select creator from membres where uid = ? ;", array($uid));
619  if (!$db->next_record()) {
620  return false;
621  }
622  return intval($db->f('creator'));
623  }
624 
625 
626  /**
627  * Exports all the personal user related information for an account.
628  * @access private
629  */
630  function alternc_export_conf() {
631  global $db, $msg;
632  $msg->log("mem", "export");
633  $str = " <member>\n";
634  $users = $this->user;
635  $str.=" <uid>" . $users["uid"] . "</uid>\n";
636  $str.=" <login>" . $users["login"] . "</login>\n";
637  $str.=" <enabled>" . $users["enabled"] . "</enabled>\n";
638  $str.=" <su>" . $users["su"] . "</su>\n";
639  $str.=" <password>" . $users["pass"] . "</password>\n";
640  $str.=" <mail>" . $users["mail"] . "</mail>\n";
641  $str.=" <created>" . $users["created"] . "</created>\n";
642  $str.=" <lastip>" . $users["lastip"] . "</lastip>\n";
643  $str.=" <lastlogin>" . $users["lastlogin"] . "</lastlogin>\n";
644  $str.=" <lastfail>" . $users["lastfail"] . "</lastfail>\n";
645  $str.=" </member>\n";
646  return $str;
647  }
648 
650  global $uid;
651  if (empty($_COOKIE['session'])) {
652  return false;
653  }
654  $sid = $_COOKIE['session'];
655  if (empty($_SESSION[$sid . '-' . $uid])) { // si pas de session de params tempo
656  return false;
657  }
658  $j = $_SESSION[$sid . '-' . $uid];
659  $j = json_decode($j, true);
660  if (!empty($j[$v])) { // si on a bien qque chose a retourner :)
661  return $j[$v];
662  }
663  return false;
664  }
665 
666  function session_tempo_params_set($k, $v, $ecrase = false) {
667  global $uid;
668  if (empty($_COOKIE['session'])) {
669  return false;
670  }
671  $sid = $_COOKIE['session'];
672  $p = Array();
673  if (!empty($_SESSION[$sid . '-' . $uid])) {
674  $p = json_decode($_SESSION[$sid . '-' . $uid], true);
675  }
676  if (!$ecrase && (isset($p[$k]) && is_array($p[$k])) && is_array($v)) {
677  $v = array_merge($p[$k], $v); // overwrite entry with the same name
678  }
679 
680  $p[$k] = $v;
681  $_SESSION[$sid . '-' . $uid] = json_encode($p);
682  return true;
683  }
684 
685  /**
686  * Sends a password-reset URL.
687  */
688  public function send_reset_url($email_or_login) {
689  global $msg, $L_FQDN, $L_HOSTING, $db;
690  // Look up user by email_or_login.
691  $db->query("SELECT * FROM membres WHERE login = ? OR mail = ? ;", array($email_or_login, $email_or_login));
692 
693  $msg->log('mem', 'send_reset_url', 'Password reset requested for: ' . $email_or_login);
694  // Give user feedback, even if we don't have an account stored.
695  $msg->raise('INFO', 'mem', _('An e-mail with information on how to connect has been sent to the owner of the account if one exists'));
696 
697  // It is possible here that a user could have multiple accounts for a
698  // single e-mail since 'mail' is not a uniqe key in the membres table.
699  // For the moment we'll just take the first account.
700  if (!$db->num_rows()) {
701  $msg->log('mem', 'send_reset_url', 'No member found with login or mail ' . $email_or_login);
702  return FALSE;
703  }
704  if ($db->num_rows()) {
705  $db->next_record();
706  // Get a reset URL for the current timestamp.
707  $url = $this->generate_reset_url($db->f('uid'));
708  $mail = $db->f('mail');
709  }
710  if (!$url || !$mail) {
711  return FALSE;
712  }
713  $duration = variable_get('password_reset_expiration', 86400, 'The number of seconds for which a password reset link is valid');
714  $duration_hours = ($duration / 3600.0) . ' ' . _('hours');
715  $message = sprintf(_('
716 Hi,
717 
718 someone requested a password reset for your account at %s (%s).
719 
720 You may connect to your account and change your account by clicking on the following URL or copying it into your browser :
721 
722 %s
723 
724 This link may only be used once. You should change your password in your account settings once connected. This link will only be valid for %s, and no changes will be made if it is not used.
725 '), $L_HOSTING, $L_FQDN, $url, $duration_hours);
726  mail($mail, "Password reset request on {$L_HOSTING}", $message, "From: postmaster@{$L_FQDN}\nReply-to: postmaster@{$L_FQDN}");
727  $msg->log('mem', 'send_reset_url', "Password reset e-mail sent for account {$uid} at {$mail}");
728  }
729 
730  /**
731  * Generate a reset URL for an account given it's e-mail or login.
732  *
733  * @param $email_or_login
734  * A string with the email or login.
735  *
736  * @returns string|boolean
737  * A reset URL or FALSE in case of error.
738  */
739  function generate_reset_url($uid) {
740  global $db;
741  $db->query("SELECT * FROM membres WHERE uid = ? ;", array($uid));
742  if (!$db->num_rows()) {
743  return FALSE;
744  }
745  if ($db->num_rows()) {
746  $db->next_record();
747  // Get a reset URL for the current timestamp.
748  return $this->_get_reset_url(time(), $db->f('uid'), $db->f('login'), $db->f('pass'));
749  }
750  return FALSE;
751  }
752 
753  /**
754  * Builds a full reset URL from the uid, login, password and timestamp.
755  *
756  * @returns string
757  * A full URL.
758  */
759  function _get_reset_url($timestamp, $uid, $login, $password) {
760  global $db, $L_FQDN;
761  $salt = variable_get('salt_password_reset', base64_encode(random_bytes(128)), 'The salt used when hasing password resets - change to invalidate all existing reset tokens') . $password;
762  $data = $timestamp . $uid . $login;
763  $token = hash_hmac('sha512', $data, $salt);
764  // @TODO: Not sure where the bureau's preferred protocol is stored, but
765  // since 3.5.0 https seems to be the default.
766  return 'https://' . $L_FQDN . '/reset.php?' . http_build_query(array(
767  'uid' => $uid,
768  'timestamp' => $timestamp,
769  'token' => $token,
770  ));
771  }
772 
773  /**
774  * Logs a user in from a one-time login link.
775  */
776  function temporary_login($uid, $timestamp, $token, $restrictip = 0, $authip_token = false) {
777  global $db, $msg, $cuid, $authip;
778  if (!$this->validate_reset_url($uid, $timestamp, $token)) {
779  return FALSE;
780  }
781  $msg->log("mem", "temporary_login", $username);
782  if ($msg->has_msgs("ERROR")) {
783  return FALSE;
784  }
785 
786  $db->query("select * from membres where uid= ? ;", array($uid));
787  if ($db->num_rows() == 0) {
788  return FALSE;
789  }
790  $db->next_record();
791 
792  // No password verification for temporary logins, the validation
793  // is in validate_reset_url instead.
794  if (!$db->f("enabled")) {
795  $msg->raise("ERROR", "mem", _("This account is locked, contact the administrator."));
796  return FALSE;
797  }
798 
799  $this->user = $db->Record;
800  $cuid = $db->f("uid");
801  if (panel_islocked() && $cuid != 2000) {
802  $msg->raise("ALERT", "mem", _("This website is currently under maintenance, login is currently disabled."));
803  return FALSE;
804  }
805 
806  // AuthIP
807  $allowed_ip = FALSE;
808  if ($authip_token) {
809  $allowed_ip = $this->authip_tokencheck($authip_token);
810  }
811 
812  $aga = $authip->get_allowed('panel');
813  foreach ($aga as $k => $v) {
814  if ($authip->is_in_subnet(get_remote_ip(), $v['ip'], $v['subnet'])) {
815  $allowed = TRUE;
816  }
817  }
818 
819  // Error if there is rules, the IP is not allowed and it's not in the whitelisted IP
820  if (sizeof($aga) > 1 && !$allowed_ip && !$authip->is_wl(get_remote_ip())) {
821  $msg->raise("ERROR", "mem", _("Your IP isn't allowed to connect"));
822  return FALSE;
823  }
824  // End AuthIP
825 
826  if ($restrictip) {
827  $ip = get_remote_ip();
828  } else {
829  $ip = "";
830  }
831 
832  // Close sessions that are more than 2 days old.
833  $db->query("DELETE FROM sessions WHERE DATE_ADD(ts,INTERVAL 2 DAY)<NOW();");
834 
835  // Delete old impersonations.
836  if (isset($_COOKIE["oldid"])) {
837  setcookie('oldid', '', 0, '/');
838  }
839 
840  // Open the session
841  $sess = md5(mt_rand().mt_rand().mt_rand());
842  $_REQUEST["session"] = $sess;
843  $db->query("insert into sessions (sid,ip,uid) values (?, ?, ?);", array($sess, $ip, $cuid));
844  setcookie("session", $sess, 0, "/");
845  $msg->init_msgs();
846 
847  // Fill in $local.
848  $db->query("SELECT * FROM local WHERE uid= ? ;", array($cuid));
849  if ($db->num_rows()) {
850  $db->next_record();
851  $this->local = $db->Record;
852  }
853  $this->resetlast();
854 
855  // Set a cookie parameter to allow password change without requiring
856  // previous one.
857  $db->query('select lastlogin, pass from membres where uid = ?;', array($uid));
858  if ($db->num_rows()) {
859  $db->next_record();
860  $cookie_data = $cuid . $db->f('lastlogin');
861  $salt = variable_get('salt_password_reset', base64_encode(random_bytes(128))) . $db->f('pass');
862  $c = setcookie('require_old_password', hash_hmac('sha512', $cookie_data, $salt), 0, '/');
863  if (!$c) {
864  $msg->log('mem', 'temporary_login', 'Failed to set cookie require_old_password');
865  }
866  }
867  return TRUE;
868  }
869 
871  global $cuid, $db;
872  $cookie = $_COOKIE['require_old_password'];
873  if (!$cookie) {
874  return TRUE;
875  }
876  $db->query('select lastlogin, pass from membres where uid = ?;', array($cuid));
877  if ($db->num_rows()) {
878  $db->next_record();
879  $cookie_data = $cuid . $db->f('lastlogin');
880  $salt = variable_get('salt_password_reset', base64_encode(random_bytes(128))) . $db->f('pass');
881  if ($cookie == hash_hmac('sha512', $cookie_data, $salt)) {
882  return FALSE;
883  }
884  }
885  return TRUE;
886  }
887 
888  /**
889  * Validates a reset URL that has been received.
890  */
891  function validate_reset_url($uid, $timestamp, $token) {
892  global $cuid, $db, $msg;
893  // Do not log a person in if they are logged in already.
894  if ($this->checkid(false)) {
895  $msg->raise('ERROR', 'mem', _('You are already logged in, you may not use a one-time login link'));
896  $msg->log('mem', 'validate_reset_url', 'Refused one-time log-in since the user is already connected');
897  return FALSE;
898  }
899 
900  // The timestamp is older than the age limit - invalid.
901  $fail_message = _('The login-link has already been used or is expired');
902  $duration = variable_get('password_reset_expiration', 86400, 'The number of seconds for which a password reset link is valid');
903  if (time() - $timestamp >= $duration) {
904  $msg->raise('ERROR', 'mem', $fail_message);
905  $msg->log('mem', 'validate_reset_url', 'Refused one-time log-in since the time elapsed is greater than limit of ' . $duration);
906  return FALSE;
907  }
908 
909  $db->query("SELECT * FROM membres WHERE uid = ? ;", array($uid));
910  if (!$db->num_rows()) {
911  $msg->raise('ERROR', 'mem', $fail_message);
912  $msg->log('mem', 'validate_reset_url', 'Refused one-time log-in since a user with ID ' . $uid. ' does not exist');
913  return FALSE;
914  }
915  $db->next_record();
916  $last_login = strtotime($db->f('lastlogin'));
917  // The timestamp is older than the most recent login - invalid.
918  if ($last_login >= time() || $last_login >= $timestamp) {
919  $msg->raise('ERROR', 'mem', $fail_message);
920  $msg->log('mem', 'validate_reset_url', "Refused one-time log-in since the most recent login was more recent than the timestamp in the log-in link. Last: {$last_login}, Timestamp: {$timestamp}");
921  return FALSE;
922  }
923 
924  // The account is locked or cannot change pass - invalid.
925  if (!$db->f('enabled') || !$db->f('canpass')) {
926  $msg->raise('ERROR', 'mem', $fail_message);
927  $msg->log('mem', 'validate_reset_url', 'Refused one-time log-in since the user account is disabled or cannot change it\'s password.');
928  return FALSE;
929  }
930 
931  // Using the current user info and timestamp the tokens generated
932  // do not match - invalid. (Eg. user password changed, salt changed).
933  $salt = variable_get('salt_password_reset', base64_encode(random_bytes(128))) . $db->f('pass');
934  $data = $timestamp . $uid . $db->f('login');
935  $ref_token = hash_hmac('sha512', $data, $salt);
936  if ($token != $ref_token) {
937  $msg->raise('ERROR', 'mem', $fail_message);
938  return FALSE;
939  }
940 
941  $msg->raise('INFO', 'mem', _('You have used a one-time login link. Please set a new password now.'));
942  return TRUE;
943  }
944 
945 } /* Class m_mem */
This class manage user sessions in the web desktop.
Definition: m_mem.php:28
authip_tokencheck($t)
Definition: m_mem.php:233
$user
This array contains the Tableau contenant les champs de la table "membres" du membre courant.
Definition: m_mem.php:36
hook_menu()
hook called by the m_menu class to add menu to the left of the panel
Definition: m_mem.php:55
temporary_login($uid, $timestamp, $token, $restrictip=0, $authip_token=false)
Logs a user in from a one-time login link.
Definition: m_mem.php:776
get_help_param()
tell if the help parameter is set
Definition: m_mem.php:586
ChangeMail2($COOKIE, $KEY, $uid)
Change the email of a member (second step, Cookie + key change)
Definition: m_mem.php:550
alternc_password_policy()
Password kind used in this class (hook for admin class)
Definition: m_mem.php:47
setid($id)
Start a session as another user from an administrator account.
Definition: m_mem.php:174
checkright()
Check that the current user is an admnistrator.
Definition: m_mem.php:70
alternc_export_conf()
Exports all the personal user related information for an account.
Definition: m_mem.php:630
generate_reset_url($uid)
Generate a reset URL for an account given it's e-mail or login.
Definition: m_mem.php:739
session_tempo_params_get($v)
Definition: m_mem.php:649
unsu()
Goes back to the original identity (of an admin, usually)
Definition: m_mem.php:344
$olduid
Original uid for the temporary uid swapping (for administrators)
Definition: m_mem.php:31
validate_reset_url($uid, $timestamp, $token)
Validates a reset URL that has been received.
Definition: m_mem.php:891
del_session()
Ends a session on the panel (logout)
Definition: m_mem.php:361
login($username, $password, $restrictip=0, $authip_token=false)
Start a session in the web desktop.
Definition: m_mem.php:83
ChangeMail1($newmail)
Change the email of an account (first step: sending of a Cookie) TODO : insert this mail string into ...
Definition: m_mem.php:498
requires_old_password_for_change()
Definition: m_mem.php:870
set_help_param($show)
Change the help parameter.
Definition: m_mem.php:575
get_creator_by_uid($uid)
Definition: m_mem.php:615
$local
contains all the fields of the "local" table for an account in AlternC.
Definition: m_mem.php:42
authip_token($bis=false)
Definition: m_mem.php:218
su($uid)
Change the identity of the user temporarily (SUDO)
Definition: m_mem.php:320
checkid($show_msg=true)
Check that the current session is correct (valid cookie) If necessary, and if we received username & ...
Definition: m_mem.php:260
passwd($oldpass, $newpass, $newpass2)
Change the password of the current user.
Definition: m_mem.php:396
resetlast()
After a successful connection, reset the user's last connection date.
Definition: m_mem.php:208
adminpref($admlist)
Change the administrator preferences of an admin account.
Definition: m_mem.php:434
send_reset_url($email_or_login)
Sends a password-reset URL.
Definition: m_mem.php:688
send_pass($login)
Send a mail with a password to an account Note : We can ask for a password only once a day TODO : Tra...
Definition: m_mem.php:454
_get_reset_url($timestamp, $uid, $login, $password)
Builds a full reset URL from the uid, login, password and timestamp.
Definition: m_mem.php:759
show_help($file, $force=false)
show (echo) a contextual help
Definition: m_mem.php:596
session_tempo_params_set($k, $v, $ecrase=false)
Definition: m_mem.php:666
$c
Definition: mem_param.php:46