Alternc  latest
Alternc logiel libre pour l'hébergement
m_mysql.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  * MySQL user database management for AlternC.
23  * This class manage user's databases in MySQL, and user's MySQL accounts.
24  *
25  * @copyright AlternC-Team 2000-2017 https://alternc.com/
26  */
27 class DB_users extends DB_Sql {
28 
30 
31  /**
32  * Creator
33  */
34  function __construct() {
35  // Sometimes we need to create this object with empty parameters, but by default we fill them with those of the current user's DB
36  global $cuid, $db, $msg;
37 
38  $db->query("select db_servers.* from db_servers, membres where membres.uid= ? and membres.db_server_id=db_servers.id;", array($cuid));
39  if (!$db->next_record()) {
40  $msg->raise("ERROR", 'db_user', _("There are no databases in db_servers for this user. Please contact your administrator."));
41  die();
42  }
43 
44  // Create the object
45  $this->HumanHostname = $db->f('name');
46  $this->Host = $db->f('host');
47  $this->User = $db->f('login');
48  $this->Password = $db->f('password');
49  $this->Client = $db->f('client');
50  $this->Database = "mysql";
51 
52  parent::__construct("mysql", $db->f('host'), $db->f('login'), $db->f('password') );
53 
54  }
55 
56 }
57 
58 class m_mysql {
59 
60  var $dbus;
61 
62 
63  /**
64  * Constructor
65  * m_mysql([$mid]) Constructeur de la classe m_mysql, initialise le membre concerne
66  */
67  function __construct() {
68  global $cuid;
69  if (!empty($cuid)) {
70  $this->dbus = new DB_users();
71  }
72  variable_get('sql_allow_users_backups', 1, 'Set 1 to allow users to configure backup of their databases, 0 if you want do disable this feature. Warning: it will not stop configured backup made by sqlbackup.sh');
73  }
74 
75 
76  function reload_dbus() {
77  $this->dbus = new DB_users();
78  }
79 
80 
81  function list_db_servers() {
82  global $db;
83  $db->query("select d.*, IFNULL(count(m.uid),0) as nb_users from db_servers d left join membres m on d.id = m.db_server_id group by d.id,m.db_server_id order by d.name,d.id;");
84  $c = array();
85  while ($db->next_record()) {
86  $c[] = $db->Record;
87  }
88  return $c;
89  }
90 
91 
92  function hook_menu() {
93  global $quota;
94  $q = $quota->getquota("mysql");
95 
96  $obj = array(
97  'title' => _("MySQL"),
98  'ico' => 'images/mysql.png',
99  'link' => 'toggle',
100  'pos' => 100,
101  'links' => array(),
102  );
103 
104  $obj['links'][] = array(
105  'txt' => _("Databases"),
106  'url' => "sql_list.php",
107  );
108  $obj['links'][] = array(
109  'txt' => _("MySQL Users"),
110  'url' => "sql_users_list.php",
111  );
112  if ($q["u"] > 0) {
113  $obj['links'][] = array(
114  'txt' => _("PhpMyAdmin"),
115  'url' => "sql_pma_sso.php",
116  'target' => '_blank',
117  );
118  }
119  return $obj;
120  }
121 
122 
123  /**
124  * Password kind used in this class (hook for admin class)
125  */
127  return array("mysql" => "MySQL users");
128  }
129 
130 
131  /**
132  * Get the list of the database for the current user.
133  * @return array returns an associative array as follow : <br>
134  * "db" => database name "bck" => backup mode for this db
135  * "dir" => Backup folder.
136  * Returns an array (empty) if no databases
137  */
138  function get_dblist() {
139  global $db, $msg, $bro, $cuid;
140  $msg->debug("mysql", "get_dblist");
141  $db->free();
142  $db->query("SELECT login,pass,db, bck_mode, bck_dir FROM db WHERE uid= ? ORDER BY db;", array($cuid));
143  $c = array();
144  while ($db->next_record()) {
145  list($dbu, $dbn) = split_mysql_database_name($db->f("db"));
146  $c[] = array("db" => $db->f("db"), "name" => $db->f('db'), "bck" => $db->f("bck_mode"), "dir" => $db->f("bck_dir"), "login" => $db->f("login"), "pass" => $db->f("pass"));
147  }
148  return $c;
149  }
150 
151 
152  /**
153  * Get the login and password of the special user able to connect to phpmyadmin
154  * @return array returns an associative array with login and password
155  * Returns FALSE if error
156  */
157  function php_myadmin_connect() {
158  global $db, $cuid, $msg;
159  $msg->log("mysql", "php_myadmin_connect");
160  $db->query("SELECT count(0) as count from db where uid = ?;", array($cuid));
161  $db->next_record();
162  if ($db->f('count') == 0) {
163  $msg->raise("ERROR", "mysql", _("Cannot connect to PhpMyAdmin, no databases for user {$cuid}"));
164  return false;
165  }
166  $db->query("SELECT dbu.name,dbu.password, dbs.host FROM dbusers dbu, db_servers dbs, membres m WHERE dbu.uid= ? and enable='ADMIN' and dbs.id=m.db_server_id and m.uid= ? ;", array($cuid, $cuid));
167  if (!$db->num_rows()) {
168  $msg->raise("ERROR", "mysql", _("Cannot connect to PhpMyAdmin, no admin user for uid {$cuid}"));
169  return false;
170  }
171  $db->next_record();
172  $info = array(
173  "login" => $db->f("name"),
174  "pass" => $db->f("password"),
175  "host" => $db->f("host")
176  );
177  return $info;
178  }
179 
180 
181  /**
182  * Returns the details of a user's database.
183  * $dbn is the name of the database (after the _) or nothing for the database "$user"
184  * @return string returns an associative array as follow :
185  * "db" => Name of the database
186  * "bck" => Current bckup mode
187  * "dir" => Backup directory
188  * "size" => Size of the database (in bytes)
189  * "pass" => Password of the user
190  * "history" => Number of backup we keep
191  * "gzip" => Does we compress the dumps ?
192  * Returns FALSE if the user has no database of if the database does not exist.
193  */
194  function get_mysql_details($dbn) {
195  global $db, $msg, $cuid;
196  $root = getuserpath();
197  $msg->debug("mysql", "get_mysql_details");
198  $pos = strpos($dbn, '_');
199  if ($pos === false) {
200  $dbname = $dbn;
201  } else {
202  $dbncomp = explode('_', $dbn);
203  $dbname = $dbn;
204  $dbn = $dbncomp[1];
205  }
206  $size = $this->get_db_size($dbname);
207  $db->query("SELECT login,pass,db, bck_mode, bck_gzip, bck_dir, bck_history FROM db WHERE uid= ? AND db= ?;", array($cuid, $dbname));
208  if (!$db->num_rows()) {
209  $msg->raise("ERROR", "mysql", _("Database %s not found"), $dbn);
210  return array("enabled" => false);
211  }
212  $db->next_record();
213  list($dbu, $dbn) = split_mysql_database_name($db->f("db"));
214  return array("enabled" => true, "login" => $db->f("login"), "db" => $db->f("db"), "name" => $dbn, "bck" => $db->f("bck_mode"), "dir" => substr($db->f("bck_dir"), strlen($root)), "size" => $size, "pass" => $db->f("pass"), "history" => $db->f("bck_history"), "gzip" => $db->f("bck_gzip"));
215  }
216 
217 
218  /**
219  * Create a new database for the current user.
220  * @param $dbn string Database name ($user_$dbn is the mysql db name)
221  * @return boolean if the database $user_$db has been successfully created, or FALSE if
222  * an error occured, such as over quota user.
223  */
224  function add_db($dbn) {
225  global $db, $msg, $quota, $cuid, $admin;
226  $msg->log("mysql", "add_db", $dbn);
227  $password_user = "";
228  if (!$quota->cancreate("mysql")) {
229  $msg->raise("ERROR", "mysql", _("Your databases quota is over. You cannot create more databases"));
230  return false;
231  }
232  $pos = strpos($dbn, '_');
233  if ($pos === false) {
234  $dbname = $dbn;
235  } else {
236  $dbncomp = explode('_', $dbn);
237  $dbname = $dbn;
238  $dbn = $dbncomp[1];
239  if (empty($dbn)) { // If nothing after the '_'
240  $msg->raise("ERROR", "mysql", _("Database can't have empty suffix"));
241  return false;
242  }
243  }
244  if (!preg_match("#^[0-9a-z]*$#", $dbn)) {
245  $msg->raise("ERROR", "mysql", _("Database name can contain only letters and numbers"));
246  return false;
247  }
248 
249  $len=variable_get("sql_max_database_length", 64);
250  if (strlen($dbname) > $len) {
251  $msg->raise("ERROR", "mysql", _("Database name cannot exceed %d characters"), $len);
252  return false;
253  }
254  $db->query("SELECT * FROM db WHERE db= ? ;", array($dbname));
255  if ($db->num_rows()) {
256  $msg->raise("ERROR", "mysql", _("Database %s already exists"), $dbn);
257  return false;
258  }
259 
260  // We prevent the automatic creation of user account longer than the max allowed lenght of a MySQL username
261  $len=variable_get('sql_max_username_length', NULL);
262  if (strlen($dbname) <= $len) {
263  $db->query("SELECT name from dbusers where name= ? and enable='ACTIVATED' ;", array($dbname));
264  if (!$db->num_rows()) {
265  // We get the password complexity set in the policy and ensure we have that complexity in the create_pass() call
266  $c=$admin->listPasswordPolicies();
267  $passwd_classcount = $c['mysql']['classcount'];
268 
269  $password_user = create_pass(10, $passwd_classcount);
270  if ($this->add_user($dbn, $password_user, $password_user)) {
271  $msg->raise("INFO", "mysql", "L'utilisateur '$dbname' a été créé et les droits sur cette base de données lui ont été attribué.");
272  } else {
273  $msg->raise("ALERT", "mysql", "L'utilisateur '$dbname' n'a pas pu être créé.<br>Allez à la page 'Utilisateurs Mysql' pour en créer manuellement.<br>Et n'oubliez pas de lui donner les droits sur la base de données.");
274  }
275  }
276  } else {
277  $msg->raise("ALERT", "mysql", "L'utilisateur '$dbname' n'a pas été automatiquement créé car il dépasse la limite de taille pour les utilisateurs qui est à $len<br>Allez à la page 'Utilisateurs Mysql' pour en créer un avec le nom que vous voulez.<br>Et n'oubliez pas de lui donner les droits sur la base de données.");
278  }
279 
280  // checking for the phpmyadmin user
281  $db->query("SELECT * FROM dbusers WHERE uid= ? AND enable='ADMIN';", array($cuid));
282  if ($db->num_rows()) {
283  $db->next_record();
284  $myadm = $db->f("name");
285  $password = $db->f("password");
286  } else {
287  $msg->raise("ERROR", "mysql", _("There is a problem with the special PhpMyAdmin user. Contact the administrator"));
288  return false;
289  }
290 
291  // Grant the special user every rights.
292  if ($this->dbus->exec("CREATE DATABASE $dbname;")) { // secured: dbname is checked against ^[0-9a-z]*$
293  $msg->log("mysql", "add_db", "Success: ".$dbn);
294  // Ok, database does not exist, quota is ok and dbname is compliant. Let's proceed
295  $db->query("INSERT INTO db (uid,login,pass,db,bck_mode) VALUES (?, ?, ?, ? ,0)", array($cuid, $myadm, $password, $dbname));
296  $dbuser = $dbname;
297  $dbname = str_replace('_', '\_', $dbname);
298  $this->grant($dbname, $myadm, "ALL PRIVILEGES", $password);
299  if (!empty($password_user)) {
300  $this->grant($dbname, $dbuser, "ALL PRIVILEGES", $password_user);
301  }
302  $this->dbus->query("FLUSH PRIVILEGES;");
303  return true;
304  } else {
305  $msg->log("mysql", "add_db", "Error: ".$dbn);
306  $msg->raise("ERROR", "mysql", _("An error occured. The database could not be created"));
307  return false;
308  }
309  }
310 
311 
312  /**
313  * Delete a database for the current user.
314  * @param $dbname string Name of the database to delete. The db name is $user_$dbn
315  * @return boolean if the database $user_$db has been successfully deleted, or FALSE if
316  * an error occured, such as db does not exist.
317  */
318  function del_db($dbname) {
319  global $db, $msg, $cuid;
320  $msg->log("mysql", "del_db", $dbname);
321  $db->query("SELECT uid FROM db WHERE db= ?;", array($dbname));
322  if (!$db->next_record()) {
323  $msg->raise("ERROR", "mysql", _("The database was not found. I can't delete it"));
324  return false;
325  }
326 
327  // Ok, database exists and dbname is compliant. Let's proceed
328  $db->query("DELETE FROM size_db WHERE db= ?;", array($dbname));
329  $db->query("DELETE FROM db WHERE uid= ? AND db= ? ;", array($cuid, $dbname));
330  $this->dbus->query("DROP DATABASE $dbname;");
331 
332  $db_esc = str_replace('_', '\_', $dbname);
333  $this->dbus->query("DELETE FROM mysql.db WHERE Db= ? ;", array($db_esc));
334 
335  // We test if the user created with the database is associated with more than 1 database.
336  $this->dbus->query("select User from mysql.db where User= ? ;", array($dbname));
337  if (($this->dbus->num_rows()) == 0) {
338  // If not we can delete it.
339  $this->del_user($dbname, false, true );
340  }
341  return true;
342  }
343 
344 
345  /**
346  * Set the backup parameters for the database $db
347  * @param $dbn string database name
348  * @param $bck_mode integer Backup mode (0 = none 1 = daily 2 = weekly)
349  * @param $bck_history integer How many backup should we keep ?
350  * @param $bck_gzip boolean shall we compress the backup ?
351  * @param $bck_dir string Directory relative to the user account where the backup will be stored
352  * @return boolean true if the backup parameters has been successfully changed, false if not.
353  */
354  function put_mysql_backup($dbn, $bck_mode, $bck_history, $bck_gzip, $bck_dir) {
355  global $db, $msg, $bro, $cuid;
356  $msg->log("mysql", "put_mysql_backup");
357 
358  if (!variable_get('sql_allow_users_backups')) {
359  $msg->raise("ERROR", "mysql", _("User aren't allowed to configure their backups"));
360  return false;
361  }
362 
363  $pos = strpos($dbn, '_');
364  if ($pos === false) {
365  $dbname = $dbn;
366  } else {
367  $dbncomp = explode('_', $dbn);
368  $dbname = $dbn;
369  $dbn = $dbncomp[1];
370  }
371  if (!preg_match("#^[0-9a-z]*$#", $dbn)) {
372  $msg->raise("ERROR", "mysql", _("Database name can contain only letters and numbers"));
373  return false;
374  }
375  $db->query("SELECT * FROM db WHERE uid= ? AND db= ? ;", array($cuid, $dbname));
376  if (!$db->num_rows()) {
377  $msg->raise("ERROR", "mysql", _("Database %s not found"), $dbn);
378  return false;
379  }
380  $db->next_record();
381  $bck_mode = intval($bck_mode);
382  $bck_history = intval($bck_history);
383  if ($bck_gzip) {
384  $bck_gzip = "1";
385  } else {
386  $bck_gzip = "0";
387  }
388  if (!$bck_mode) {
389  $bck_mode = "0";
390  }
391  if (!$bck_history) {
392  $msg->raise("ALERT", "mysql", _("You have to choose how many backups you want to keep"));
393  return false;
394  }
395  if (($bck_dir = $bro->convertabsolute($bck_dir, 0)) === false) { // return a full path or FALSE
396  $msg->raise("ERROR", "mysql", _("Directory does not exist"));
397  return false;
398  }
399  $db->query("UPDATE db SET bck_mode= ? , bck_history= ?, bck_gzip= ?, bck_dir= ? WHERE uid= ? AND db= ? ;", array($bck_mode, $bck_history, $bck_gzip, $bck_dir, $cuid, $dbname));
400  return true;
401  }
402 
403 
404  /**
405  * Change the password of the user in MySQL
406  * @param $password string new password (cleartext)
407  * @return boolean TRUE if the password has been successfully changed, FALSE else.
408  */
410  global $db, $msg, $cuid, $admin;
411  $msg->log("mysql", "put_mysql_details");
412  $db->query("SELECT * FROM db WHERE uid= ?;", array($cuid));
413  if (!$db->num_rows()) {
414  $msg->raise("ERROR", "mysql", _("Database not found"));
415  return false;
416  }
417  $db->next_record();
418  $login = $db->f("login");
419 
420  if (!$password) {
421  $msg->raise("ERROR", "mysql", _("The password is mandatory"));
422  return false;
423  }
424 
425  $len=variable_get("sql_max_username_length", 16);
426  if (strlen($password) > $len) {
427  $msg->raise("ERROR", "mysql", _("MySQL password cannot exceed %d characters"), $len);
428  return false;
429  }
430 
431  // Check this password against the password policy using common API :
432  if (is_callable(array($admin, "checkPolicy"))) {
433  if (!$admin->checkPolicy("mysql", $login, $password)) {
434  return false; // The error has been raised by checkPolicy()
435  }
436  }
437 
438  // Update all the "pass" fields for this user :
439  $db->query("UPDATE db SET pass= ? WHERE uid= ?;", array($password, $cuid));
440  $this->dbus->query("SET PASSWORD FOR " .$login . "@" . $this->dbus->Client . " = PASSWORD(?);", array($password));
441  return true;
442  }
443 
444 
445  /**
446  * Function used to grant SQL rights to users:
447  * @base :database
448  * @user : database user
449  * @rights : rights to apply ( optional, every rights apply given if missing
450  * @pass : user password ( optional, if not given the pass stays the same, else it takes the new value )
451  * @table : sql tables to apply rights
452  * */
453  function grant($base, $user, $rights = null, $pass = null, $table = '*') {
454  global $msg, $db;
455  $msg->log("mysql", "grant", $base . "-" . $rights . "-" . $user);
456 
457  if (!preg_match("#^[0-9a-z_\\*\\\\]*$#", $base)) {
458  $msg->raise("ERROR", "mysql", _("Database name can contain only letters and numbers"));
459  return false;
460  } elseif (!$this->dbus->query("select db from db where db= ?;", array($base))) {
461  $msg->raise("ERROR", "mysql", _("Database not found"));
462  return false;
463  }
464 
465  if ($rights == null) {
466  $rights = 'ALL PRIVILEGES';
467  } elseif (!preg_match("#^[a-zA-Z,\s]*$#", $rights)) {
468  $msg->raise("ERROR", "mysql", _("Databases rights are not correct"));
469  return false;
470  }
471 
472  if (!preg_match("#^[0-9a-z]#", $user)) {
473  $msg->raise("ERROR", "mysql", _("The username can contain only letters and numbers."));
474  return false;
475  }
476  $db->query("select name from dbusers where name= ? ;", array($user));
477 
478  if (!$db->num_rows()) {
479  $msg->raise("ERROR", "mysql", _("Database user not found"));
480  return false;
481  }
482 
483  $grant = "grant " . $rights . " on `" . $base . "`." . $table . " to " . $db->quote($user) . "@" . $db->quote($this->dbus->Client);
484 
485  if ($pass) {
486  $grant .= " identified by " . $db->quote($pass) . ";";
487  } else {
488  $grant .= ";";
489  }
490 
491  if (!$this->dbus->query($grant)) {
492  $msg->raise("ERROR", "mysql", _("Could not grant rights"));
493  return false;
494  }
495  return true;
496  }
497 
498 
499  /**
500  * Restore a sql database.
501  * @param $file string The filename, relative to the user root dir, which contains a sql dump
502  * @param $stdout boolean shall-we dump the error to stdout ?
503  * @param $id integer The ID of the database to dump to.
504  * @return boolean TRUE if the database has been restored, or FALSE if an error occurred
505  */
506  function restore($file, $stdout, $id) {
507  global $msg, $bro;
508  if (empty($file)) {
509  $msg->raise("ERROR", "mysql", _("No file specified"));
510  return false;
511  }
512  if (!$r = $this->get_mysql_details($id)) {
513  return false;
514  }
515  if (!($fi = $bro->convertabsolute($file, 0))) {
516  $msg->raise("ERROR", "mysql", _("File not found"));
517  return false;
518  }
519  if (!file_exists($fi)) {
520  $msg->raise("ERROR", "mysql", _("File not found"));
521  return false;
522  }
523 
524  if (substr($fi, -3) == ".gz") {
525  $exe = "/bin/gzip -d -c <" . escapeshellarg($fi) . " | /usr/bin/mysql -h" . escapeshellarg($this->dbus->Host) . " -u" . escapeshellarg($r["login"]) . " -p" . escapeshellarg($r["pass"]) . " " . escapeshellarg($r["db"]);
526  } elseif (substr($fi, -4) == ".bz2") {
527  $exe = "/usr/bin/bunzip2 -d -c <" . escapeshellarg($fi) . " | /usr/bin/mysql -h" . escapeshellarg($this->dbus->Host) . " -u" . escapeshellarg($r["login"]) . " -p" . escapeshellarg($r["pass"]) . " " . escapeshellarg($r["db"]);
528  } else {
529  $exe = "/usr/bin/mysql -h" . escapeshellarg($this->dbus->Host) . " -u" . escapeshellarg($r["login"]) . " -p" . escapeshellarg($r["pass"]) . " " . escapeshellarg($r["db"]) . " <" . escapeshellarg($fi);
530  }
531  $exe .= " 2>&1";
532 
533  echo "<code><pre>";
534  $ret = 0;
535  if ($stdout) {
536  passthru($exe, $ret);
537  } else {
538  exec($exe, $ret);
539  }
540  echo "</pre></code>";
541  if ($ret != 0) {
542  return false;
543  } else {
544  return true;
545  }
546  }
547 
548 
549  /**
550  * Get the size of a database
551  * @param $dbname name of the database
552  * @return integer database size
553  * @access private
554  */
555  function get_db_size($dbname) {
556  $this->dbus->query("SHOW TABLE STATUS FROM $dbname;");
557  $size = 0;
558  while ($this->dbus->next_record()) {
559  $size += $this->dbus->f('Data_length') + $this->dbus->f('Index_length');
560  if ($this->dbus->f('Engine') != 'InnoDB') {
561  $size += $this->dbus->f('Data_free');
562  }
563  }
564  return $size;
565  }
566 
567 
568  /**
569  * Returns the list of database users of an account
570  */
571  function get_userslist($all = null) {
572  global $db, $msg, $cuid;
573  $msg->debug("mysql", "get_userslist");
574  $c = array();
575  if (!$all) {
576  $db->query("SELECT name FROM dbusers WHERE uid= ? and enable not in ('ADMIN','HIDDEN') ORDER BY name;", array($cuid));
577  } else {
578  $db->query("SELECT name FROM dbusers WHERE uid= ? ORDER BY name;", array($cuid));
579  }
580  while ($db->next_record()) {
581  $pos = strpos($db->f("name"), "_");
582  if ($pos === false) {
583  $c[] = array("name" => ($db->f("name")));
584  } else {
585  $c[] = array("name" => ($db->f("name")));
586  //$c[]=array("name"=>substr($db->f("name"),strpos($db->f("name"),"_")+1));
587  }
588  }
589 
590  return $c;
591  }
592 
593 
594  function get_defaultsparam($dbn) {
595  global $db, $msg, $cuid;
596  $msg->debug("mysql", "getdefaults");
597 
598  $dbu = $dbn;
599  $r = array();
600  $dbn = str_replace('_', '\_', $dbn);
601  $this->dbus->query("Select * from mysql.db where Db= ? and User!= ? ;", array($dbn, $cuid."_myadm"));
602 
603  if (!$this->dbus->num_rows()) {
604  $msg->raise("ERROR", "mysql",_("Database not found"));
605  return false;
606  }
607 
608  $listRights = array('Select', 'Insert', 'Update', 'Delete', 'Create', 'Drop', 'References', 'Index', 'Alter', 'Create_tmp_table', 'Lock_tables', 'Create_view', 'Show_view', 'Create_routine', 'Alter_routine', 'Execute', 'Event', 'Trigger');
609  while ($this->dbus->next_record()) {
610  // rTmp is the array where we put the informations from each loop, added to array $r
611  $rTmp = array();
612  $variable = $this->dbus->Record;
613 
614  $dbu = $variable['User'];
615 
616  $rTmp['Host'] = $this->dbus->f('Host');
617  $rTmp['Rights']='All';
618 
619  foreach ($listRights as $v) {
620  $right = $v."_priv";
621  if ($this->dbus->f($right) !== "Y") {
622  $rTmp['Rights'] = 'NotAll';
623  break;
624  }
625  }
626 
627  if (!$db->query("SELECT name,password from dbusers where name= ? ;", array($dbu))) {
628  $msg->raise("ERROR", "mysql",_("Database not found")." (3)");
629  return false;
630  }
631 
632  if (!$db->num_rows()) {
633  $msg->raise("ERROR", "mysql",_("Database not found")." (4)");
634  return false;
635  }
636 
637  $db->next_record();
638  $rTmp['user'] = $db->f('name');
639  $rTmp['password'] = $db->f('password');
640 
641  $r[] = $rTmp;
642 
643  } // endwhile
644  return $r;
645  }
646 
647 
648  /**
649  * Create a new user in MySQL rights tables
650  * @param $usern the username (we will add _[alternc-account] to it)
651  * @param string $password The password for this username
652  * @param string $passconf The password confirmation
653  * @return boolean if the user has been created in MySQL or FALSE if an error occurred
654  */
655  function add_user($usern, $password, $passconf) {
656  global $db, $msg, $mem, $cuid, $admin;
657  $msg->log("mysql", "add_user", $usern);
658 
659  $usern = trim($usern);
660  $login = $mem->user["login"];
661  if ($login != $usern) {
662  $user = $login . "_" . $usern;
663  } else {
664  $user = $usern;
665  }
666  if (!$usern) {
667  $msg->raise("ALERT", "mysql", _("The username is mandatory"));
668  return false;
669  }
670  if (!$password) {
671  $msg->raise("ALERT", "mysql", _("The password is mandatory"));
672  return false;
673  }
674  if (!preg_match("#^[0-9a-z]#", $usern)) {
675  $msg->raise("ERROR", "mysql", _("The username can contain only letters and numbers"));
676  return false;
677  }
678 
679  // We check the length of the COMPLETE username, not only the part after _
680  $len=variable_get("sql_max_username_length", 16);
681  if (strlen($user) > $len) {
682  $msg->raise("ERROR", "mysql", _("MySQL username cannot exceed %d characters"), $len);
683  return false;
684  }
685  $db->query("SELECT * FROM dbusers WHERE name= ? ;", array($user));
686  if ($db->num_rows()) {
687  $msg->raise("ERROR", "mysql", _("The database user already exists"));
688  return false;
689  }
690  if ($password != $passconf || !$password) {
691  $msg->raise("ERROR", "mysql", _("The passwords do not match"));
692  return false;
693  }
694 
695  // Check this password against the password policy using common API :
696  if (is_callable(array($admin, "checkPolicy"))) {
697  if (!$admin->checkPolicy("mysql", $user, $password)) {
698  return false; // The error has been raised by checkPolicy()
699  }
700  }
701 
702  // We add him to the user table
703  $db->query("INSERT INTO dbusers (uid,name,password,enable) VALUES( ?, ?, ?, 'ACTIVATED');", array($cuid, $user, $password));
704 
705  $this->grant("*", $user, "USAGE", $password);
706  return true;
707  }
708 
709 
710  /**
711  * Change a user's MySQL password
712  * @param $usern the username
713  * @param $password The password for this username
714  * @param $passconf The password confirmation
715  * @return boolean if the password has been changed in MySQL or FALSE if an error occurred
716  */
717  function change_user_password($usern, $password, $passconf) {
718  global $db, $msg, $cuid, $admin;
719  $msg->log("mysql", "change_user_pass", $usern);
720 
721  $usern = trim($usern);
722  if ($password != $passconf || !$password) {
723  $msg->raise("ERROR", "mysql", _("The passwords do not match"));
724  return false;
725  }
726 
727  // Check this password against the password policy using common API :
728  if (is_callable(array($admin, "checkPolicy"))) {
729  if (!$admin->checkPolicy("mysql", $usern, $password)) {
730  return false; // The error has been raised by checkPolicy()
731  }
732  }
733  $this->dbus->query("SET PASSWORD FOR " . $db->quote($usern) . "@" . $db->quote($this->dbus->Client) . " = PASSWORD(?);", array($password));
734  $db->query("UPDATE dbusers set password= ? where name= ? and uid= ? ;", array($password, $usern, $cuid));
735  return true;
736  }
737 
738 
739  /**
740  * Delete a user in MySQL rights tables
741  * @param $user the username (we will add "[alternc-account]_" to it) to delete
742  * @param integer $all
743  * @return boolean if the user has been deleted in MySQL or FALSE if an error occurred
744  */
745  function del_user($user, $all = false, $caller_is_deldb = false) {
746  global $db, $msg, $cuid;
747  $msg->log("mysql", "del_user", $user);
748  if (!preg_match("#^[0-9a-z]#", $user)) {
749  $msg->raise("ERROR", "mysql", _("The username can contain only letters and numbers"));
750  return false;
751  }
752  if (!$all) {
753  $db->query("SELECT name FROM dbusers WHERE name= ? and enable not in ('ADMIN','HIDDEN');", array($user));
754  } else {
755  $db->query("SELECT name FROM dbusers WHERE uid= ? ;", array($cuid));
756  }
757 
758  if (!$db->num_rows()) {
759  if (! $caller_is_deldb )
760  $msg->raise("ERROR", "mysql", _("The username was not found"));
761 
762  return false;
763  }
764  $db->next_record();
765  $login = $db->f("name");
766 
767  // Ok, database exists and dbname is compliant. Let's proceed
768  $this->dbus->query("REVOKE ALL PRIVILEGES ON *.* FROM " . $db->quote($user) . "@" . $db->quote($this->dbus->Client) . ";");
769  $this->dbus->query("DELETE FROM mysql.db WHERE User= ? AND Host= ? ;", array($user, $this->dbus->Client));
770  $this->dbus->query("DELETE FROM mysql.user WHERE User= ? AND Host= ? ;", array($user, $this->dbus->Client));
771  $this->dbus->query("FLUSH PRIVILEGES");
772 
773  $db->query("DELETE FROM dbusers WHERE uid= ? AND name= ? ;", array($cuid, $user));
774 
775  if ( $caller_is_deldb )
776  $msg->raise("INFO", "mysql", _("The user '%s' has been successfully deleted"), $user);
777 
778  return true;
779  }
780 
781 
782  /**
783  * Return the list of the database rights of user $user
784  * @param $user the username
785  * @return array An array of database name and rights
786  */
787  function get_user_dblist($user) {
788  global $db, $msg;
789 
790  $this->dbus->query("SELECT * FROM mysql.user WHERE User= ? AND Host= ? ;", array($user, $this->dbus->Client));
791  if (!$this->dbus->next_record()) {
792  $msg->raise("ERROR", 'mysql', _("This user does not exist in the MySQL/User database"));
793  return false;
794  }
795 
796  $r = array();
797  $db->free();
798  $dblist = $this->get_dblist();
799  foreach ($dblist as $tab) {
800  $dbname = str_replace('_', '\_', $tab["db"]);
801  $this->dbus->query("SELECT * FROM mysql.db WHERE User= ? AND Host= ? AND Db= ? ;", array($user, $this->dbus->Client, $dbname));
802  if ($this->dbus->next_record()) {
803  $r[] = array("db" => $tab["db"], "select" => $this->dbus->f("Select_priv"), "insert" => $this->dbus->f("Insert_priv"), "update" => $this->dbus->f("Update_priv"), "delete" => $this->dbus->f("Delete_priv"), "create" => $this->dbus->f("Create_priv"), "drop" => $this->dbus->f("Drop_priv"), "references" => $this->dbus->f("References_priv"), "index" => $this->dbus->f("Index_priv"), "alter" => $this->dbus->f("Alter_priv"), "create_tmp" => $this->dbus->f("Create_tmp_table_priv"), "lock" => $this->dbus->f("Lock_tables_priv"),
804  "create_view" => $this->dbus->f("Create_view_priv"),
805  "show_view" => $this->dbus->f("Show_view_priv"),
806  "create_routine" => $this->dbus->f("Create_routine_priv"),
807  "alter_routine" => $this->dbus->f("Alter_routine_priv"),
808  "execute" => $this->dbus->f("Execute_priv"),
809  "event" => $this->dbus->f("Event_priv"),
810  "trigger" => $this->dbus->f("Trigger_priv")
811  );
812  } else {
813  $r[] = array("db" => $tab['db'], "select" => "N", "insert" => "N", "update" => "N", "delete" => "N", "create" => "N", "drop" => "N", "references" => "N", "index" => "N", "alter" => "N", "create_tmp" => "N", "lock" => "N", "create_view" => "N", "show_view" => "N", "create_routine" => "N", "alter_routine" => "N", "execute" => "N", "event" => "N", "trigger" => "N");
814  }
815  }
816  return $r;
817  }
818 
819 
820  /**
821  * Set the access rights of user $user to database $dbn to be rights $rights
822  * @param $user the username to give rights to
823  * @param $dbn The database to give rights to
824  * @param $rights The rights as an array of MySQL keywords (insert, select ...)
825  * @return boolean TRUE if the rights has been applied or FALSE if an error occurred
826  */
827  function set_user_rights($user, $dbn, $rights) {
828  global $msg;
829  $msg->log("mysql", "set_user_rights");
830 
831  // We generate the rights array depending on the rights list:
832  $strrights = "";
833  for ($i = 0; $i < count($rights); $i++) {
834  switch ($rights[$i]) {
835  case "select":
836  $strrights.="SELECT,";
837  break;
838  case "insert":
839  $strrights.="INSERT,";
840  break;
841  case "update":
842  $strrights.="UPDATE,";
843  break;
844  case "delete":
845  $strrights.="DELETE,";
846  break;
847  case "create":
848  $strrights.="CREATE,";
849  break;
850  case "drop":
851  $strrights.="DROP,";
852  break;
853  case "references":
854  $strrights.="REFERENCES,";
855  break;
856  case "index":
857  $strrights.="INDEX,";
858  break;
859  case "alter":
860  $strrights.="ALTER,";
861  break;
862  case "create_tmp":
863  $strrights.="CREATE TEMPORARY TABLES,";
864  break;
865  case "lock":
866  $strrights.="LOCK TABLES,";
867  break;
868  case "create_view":
869  $strrights.="CREATE VIEW,";
870  break;
871  case "show_view":
872  $strrights.="SHOW VIEW,";
873  break;
874  case "create_routine":
875  $strrights.="CREATE ROUTINE,";
876  break;
877  case "alter_routine":
878  $strrights.="ALTER ROUTINE,";
879  break;
880  case "execute":
881  $strrights.="EXECUTE,";
882  break;
883  case "event":
884  $strrights.="EVENT,";
885  break;
886  case "trigger":
887  $strrights.="TRIGGER,";
888  break;
889  }
890  }
891 
892  // We reset all user rights on this DB :
893  $dbname = str_replace('_', '\_', $dbn);
894  $this->dbus->query("SELECT * FROM mysql.db WHERE User = ? AND Db = ?;", array($user, $dbname));
895 
896  if ($this->dbus->num_rows()) {
897  $this->dbus->query("REVOKE ALL PRIVILEGES ON `".$dbname."`.* FROM ".$this->dbus->quote($user)."@" . $this->dbus->quote($this->dbus->Client) . ";");
898  }
899  if ($strrights) {
900  $strrights = substr($strrights, 0, strlen($strrights) - 1);
901  $this->grant($dbname, $user, $strrights);
902  }
903  $this->dbus->query("FLUSH PRIVILEGES");
904  return TRUE;
905  }
906 
907  /**
908  * list of all possible SQL rights
909  */
910  function available_sql_rights() {
911  return Array('select', 'insert', 'update', 'delete', 'create', 'drop', 'references', 'index', 'alter', 'create_tmp', 'lock', 'create_view', 'show_view', 'create_routine', 'alter_routine', 'execute', 'event', 'trigger');
912  }
913 
914 
915  /**
916  * Hook function called by the lxc class to set mysql_host and port
917  * parameters
918  * @access private
919  */
920  function hook_lxc_params($params) {
921  global $msg;
922  $msg->log("mysql", "alternc_get_quota");
923  $p = array();
924  if (isset($this->dbus["Host"]) && $this->dbus["Host"] != "") {
925  $p["mysql_host"] = $this->dbus["Host"];
926  $p["mysql_port"] = 3306;
927  }
928  return $p;
929  }
930 
931 
932  /**
933  * Hook function called by the quota class to compute user used quota
934  * Returns the used quota for the $name service for the current user.
935  * @param $name string name of the quota
936  * @return integer the number of service used or false if an error occured
937  * @access private
938  */
939  function hook_quota_get() {
940  global $msg, $mem, $quota;
941  $msg->debug("mysql", "alternc_get_quota");
942  $q = Array("name" => "mysql", "description" => _("MySQL Databases"), "used" => 0);
943  $c = $this->get_dblist();
944  if (is_array($c)) {
945  $q['used'] = count($c);
946  $q['sizeondisk'] = $quota->get_size_db_sum_user($mem->user["login"])/1024;
947  }
948  return $q;
949  }
950 
951 
952  /**
953  * Hook function called when a user is created.
954  * AlternC's standard function that create a member
955  * @access private
956  */
957  function alternc_add_member() {
958  global $db, $msg, $cuid, $mem;
959  $msg->log("mysql", "alternc_add_member");
960  // checking for the phpmyadmin user
961  $db->query("SELECT name,password FROM dbusers WHERE uid= ? AND enable='ADMIN';", array($cuid));
962  if ($db->num_rows()) {
963  $myadm = $db->f("name");
964  $password = $db->f("password");
965  } else {
966  $myadm = $cuid . "_myadm";
968  }
969 
970  $db->query("INSERT INTO dbusers (uid,name,password,enable) VALUES (?, ?, ?, 'ADMIN');", array($cuid, $myadm, $password));
971 
972  return true;
973  }
974 
975 
976  /**
977  * Hook function called when a user is deleted.
978  * AlternC's standard function that delete a member
979  * @access private
980  */
981  function alternc_del_member() {
982  global $msg;
983  $msg->log("mysql", "alternc_del_member");
984  $c = $this->get_dblist();
985  if (is_array($c)) {
986  for ($i = 0; $i < count($c); $i++) {
987  $this->del_db($c[$i]["name"]);
988  }
989  }
990  $d = $this->get_userslist(1);
991  if (!empty($d)) {
992  for ($i = 0; $i < count($d); $i++) {
993  $this->del_user($d[$i]["name"], 1,true);
994  }
995  }
996  return true;
997  }
998 
999 
1000  /**
1001  * Hook function called when a user is logged out.
1002  * We just remove the cookie created in admin/sql_admin.php
1003  * a @access private
1004  */
1005  function alternc_del_session() {
1006  $_SESSION['PMA_single_signon_user'] = '';
1007  $_SESSION['PMA_single_signon_password'] = '';
1008  $_SESSION['PMA_single_signon_host'] = '';
1009  }
1010 
1011 
1012  /**
1013  * Exports all the mysql information of an account
1014  * @access private
1015  * EXPERIMENTAL 'sid' function ;)
1016  */
1017  function alternc_export_conf() {
1018  // TODO don't work with separated sql server for dbusers
1019  global $db, $msg, $cuid;
1020  $msg->log("mysql", "export");
1021  $db->query("SELECT login, pass, db, bck_mode, bck_dir, bck_history, bck_gzip FROM db WHERE uid= ? ;", array($cuid));
1022  $str = "";
1023  if ($db->next_record()) {
1024  $str.=" <sql>\n";
1025  $str.=" <login>" . $db->Record["login"] . "</login>\n";
1026  $str.=" <pass>" . $db->Record["pass"] . "</pass>\n";
1027  do {
1028  $filename = $tmpdir . "/mysql." . $db->Record["db"] . ".sql.gz"; // FIXME not used
1029  $str.=" <database>" . ($db->Record["db"]) . "</database>\n";
1030  $str.=" <password>" . ($db->Record["pass"]) . "</password>\n";
1031  if ($s["bck_mode"] != 0) { // FIXME what is $s ?
1032  $str.=" <backup-mode>" . ($db->Record["bck_mode"]) . "</backup-mode>\n";
1033  $str.=" <backup-dir>" . ($db->Record["bck_dir"]) . "</backup-dir>\n";
1034  $str.=" <backup-history>" . ($db->Record["bck_history"]) . "</backup-history>\n";
1035  $str.=" <backup-gzip>" . ($db->Record["bck_gzip"]) . "</backup-gzip>\n";
1036  }
1037  } while ($db->next_record());
1038  $str.=" </sql>\n";
1039  }
1040  return $str;
1041  }
1042 
1043 
1044  /**
1045  * Exports all the mysql databases a of give account to $dir directory
1046  * @access private
1047  * EXPERIMENTAL 'sid' function ;)
1048  */
1049  function alternc_export_data($dir) {
1050  global $db, $msg, $cuid;
1051  $msg->log("mysql", "export_data");
1052  $db->query("SELECT db.login, db.pass, db.db, dbusers.name FROM db,dbusers WHERE db.uid= ? AND dbusers.uid=db.uid;", array($cuid));
1053  $dir.="sql/";
1054  if (!is_dir($dir)) {
1055  if (!mkdir($dir)) {
1056  $msg->raise("ERROR", 'mysql', _("The directory could not be created"));
1057  }
1058  }
1059  // on exporte toutes les bases utilisateur.
1060  while ($db->next_record()) {
1061  $filename = $dir . "mysql." . $db->Record["db"] . "." . date("H:i:s") . ".sql.gz";
1062  exec("/usr/bin/mysqldump --defaults-file=/etc/alternc/my.cnf --add-drop-table --allow-keywords -Q -f -q -a -e " . escapeshellarg($db->Record["db"]) . " |/bin/gzip >" . escapeshellarg($filename));
1063  }
1064  }
1065 
1066 
1067  /**
1068  * Return the size of each databases in a SQL Host given in parameter
1069  * @param $db_name the human name of the host
1070  * @param $db_host the host hosting the SQL databases
1071  * @param $db_login the login to access the SQL db
1072  * @param $db_password the password to access the SQL db
1073  * @param $db_client the client to access the SQL db
1074  * @return an array associating the name of the databases to their sizes : array(dbname=>size)
1075  */
1076  function get_dbus_size($db_name, $db_host, $db_login, $db_password, $db_client) {
1077  global $msg;
1078  $msg->debug("mysql", "get_dbus_size", $db_host);
1079 
1080  $this->dbus = new DB_Sql("mysql",$db_host,$db_login,$db_password);
1081 
1082  $this->dbus->query("SHOW DATABASES;");
1083  $alldb=array();
1084  while ($this->dbus->next_record()) {
1085  $alldb[] = $this->dbus->f("Database");
1086  }
1087 
1088  $res = array();
1089  foreach($alldb as $dbname) {
1090  $c = $this->dbus->query("SHOW TABLE STATUS FROM $dbname;");
1091  $size = 0;
1092  while ($this->dbus->next_record()) {
1093  $size+=$this->dbus->f("Data_length") + $this->dbus->f("Index_length");
1094  }
1095  $res["$dbname"] = "$size";
1096  }
1097  return $res;
1098  }
1099 
1100 } /* Class m_mysql */
global $db
Definition: bootstrap.php:26
$mem
Definition: bootstrap.php:71
$msg
Definition: bootstrap.php:75
$root
Definition: bootstrap.php:20
$c
Definition: bootstrap.php:47
$cuid
Definition: bootstrap.php:43
$r
Definition: aws_add.php:75
$passwd_classcount
Definition: aws_pass.php:58
$d
$p
Definition: bro_editor.php:46
$res
Definition: index.php:111
variable_get($name, $default=null, $createit_comment=null)
Return a persistent variable.
Definition: variables.php:85
Mysql Database class.
Definition: db_mysql.php:27
MySQL user database management for AlternC.
Definition: m_mysql.php:27
__construct()
Creator.
Definition: m_mysql.php:34
$HumanHostname
Definition: m_mysql.php:29
get_dbus_size($db_name, $db_host, $db_login, $db_password, $db_client)
Return the size of each databases in a SQL Host given in parameter.
Definition: m_mysql.php:1076
alternc_password_policy()
Password kind used in this class (hook for admin class)
Definition: m_mysql.php:126
reload_dbus()
Definition: m_mysql.php:76
grant($base, $user, $rights=null, $pass=null, $table=' *')
Function used to grant SQL rights to users: @base :database @user : database user @rights : rights to...
Definition: m_mysql.php:453
change_user_password($usern, $password, $passconf)
Change a user's MySQL password.
Definition: m_mysql.php:717
get_dblist()
Get the list of the database for the current user.
Definition: m_mysql.php:138
__construct()
Constructor m_mysql([$mid]) Constructeur de la classe m_mysql, initialise le membre concerne.
Definition: m_mysql.php:67
del_user($user, $all=false, $caller_is_deldb=false)
Delete a user in MySQL rights tables.
Definition: m_mysql.php:745
add_db($dbn)
Create a new database for the current user.
Definition: m_mysql.php:224
php_myadmin_connect()
Get the login and password of the special user able to connect to phpmyadmin.
Definition: m_mysql.php:157
list_db_servers()
Definition: m_mysql.php:81
add_user($usern, $password, $passconf)
Create a new user in MySQL rights tables.
Definition: m_mysql.php:655
get_mysql_details($dbn)
Returns the details of a user's database.
Definition: m_mysql.php:194
put_mysql_details($password)
Change the password of the user in MySQL.
Definition: m_mysql.php:409
del_db($dbname)
Delete a database for the current user.
Definition: m_mysql.php:318
set_user_rights($user, $dbn, $rights)
Set the access rights of user $user to database $dbn to be rights $rights.
Definition: m_mysql.php:827
alternc_del_member()
Hook function called when a user is deleted.
Definition: m_mysql.php:981
get_userslist($all=null)
Returns the list of database users of an account.
Definition: m_mysql.php:571
hook_menu()
Definition: m_mysql.php:92
hook_quota_get()
Hook function called by the quota class to compute user used quota Returns the used quota for the $na...
Definition: m_mysql.php:939
put_mysql_backup($dbn, $bck_mode, $bck_history, $bck_gzip, $bck_dir)
Set the backup parameters for the database $db.
Definition: m_mysql.php:354
get_db_size($dbname)
Get the size of a database.
Definition: m_mysql.php:555
hook_lxc_params($params)
Hook function called by the lxc class to set mysql_host and port parameters @access private.
Definition: m_mysql.php:920
alternc_del_session()
Hook function called when a user is logged out.
Definition: m_mysql.php:1005
alternc_add_member()
Hook function called when a user is created.
Definition: m_mysql.php:957
get_user_dblist($user)
Return the list of the database rights of user $user.
Definition: m_mysql.php:787
restore($file, $stdout, $id)
Restore a sql database.
Definition: m_mysql.php:506
alternc_export_conf()
Exports all the mysql information of an account @access private EXPERIMENTAL 'sid' function ;)
Definition: m_mysql.php:1017
available_sql_rights()
list of all possible SQL rights
Definition: m_mysql.php:910
get_defaultsparam($dbn)
Definition: m_mysql.php:594
alternc_export_data($dir)
Exports all the mysql databases a of give account to $dir directory @access private EXPERIMENTAL 'sid...
Definition: m_mysql.php:1049
split_mysql_database_name($dbname)
split mysql database name between username and custom database name
Definition: functions.php:548
create_pass($length=10, $classcount=3)
Create a password compatible with the password policy.
Definition: functions.php:879
getuserpath($user=null)
get the home of the user
Definition: functions.php:329
$q
Definition: menu_aws.php:32
$user
Definition: bootstrap.php:84
$password
Definition: bootstrap.php:85
$bro
Definition: bootstrap.php:151
if(empty($site_name)) elseif($piwik->site_add( $site_name, $site_urls))
$login
$i
$len
if(!isset($is_include)) if(! $key &&! $crt) $id
if($error) $info