Marius Cramer
2015-08-06 37b29231e47a0c4458dc1c15d98588f16f07e1e2
commit | author | age
bd68aa 1 <?php
MC 2
3 /*
4 Copyright (c) 2007, Till Brehm, projektfarm Gmbh
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without modification,
8 are permitted provided that the following conditions are met:
9
10     * Redistributions of source code must retain the above copyright notice,
11       this list of conditions and the following disclaimer.
12     * Redistributions in binary form must reproduce the above copyright notice,
13       this list of conditions and the following disclaimer in the documentation
14       and/or other materials provided with the distribution.
15     * Neither the name of ISPConfig nor the names of its contributors
16       may be used to endorse or promote products derived from this software without
17       specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26 OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /**
32  * The MongoDB client plugin is used by ISPConfig to control the management of MongoDB.
33  * If handles everything from creating DBs/Users, update them or delete them.
34  */
b1a6a5 35
MC 36
bd68aa 37 class mongo_clientdb_plugin {
MC 38
39     /**
40      * ISPConfig internal identifiers.
41      */
42     var $plugin_name = 'mongo_clientdb_plugin';
43     var $class_name  = 'mongo_clientdb_plugin';
44
45
46     /**
47      * This function is called during ISPConfig installation.
48      * It determines if a symlink shall be created for this plugin.
49      *
50      * @return bool true if symlink should be created
51      */
52     function onInstall() {
53         global $conf;
fa9c29 54         
TB 55         if($conf['services']['db'] == true && class_exists('MongoClient')) {
56             return true;
57         } else {
58             return false;
59         }
bd68aa 60     }
MC 61
b1a6a5 62
bd68aa 63     /**
MC 64      * This function is called when the plugin is loaded.
65      * Each plugin/module needs to register itself to ISPConfig events from which
66      * it want to receive changes/get notified.
67      *
68      * Since this is a MongoDB plugin we are interested in DB changes and everything related
69      * to it, like users.
70      */
71     function onLoad() {
72         global $app;
73
74         //* Databases
b1a6a5 75         $app->plugins->registerEvent('database_insert', $this->plugin_name, 'db_insert');
MC 76         $app->plugins->registerEvent('database_update', $this->plugin_name, 'db_update');
77         $app->plugins->registerEvent('database_delete', $this->plugin_name, 'db_delete');
bd68aa 78
MC 79         //* Database users
b1a6a5 80         $app->plugins->registerEvent('database_user_insert', $this->plugin_name, 'db_user_insert');
MC 81         $app->plugins->registerEvent('database_user_update', $this->plugin_name, 'db_user_update');
82         $app->plugins->registerEvent('database_user_delete', $this->plugin_name, 'db_user_delete');
bd68aa 83     }
MC 84
85
86     /**
87      * MongoDB
88      * ------------------------------------------------------------------------
89      * The following needs to be done before using this plugin:
90      * - 1. install MongoDB server from 10gen sources (or another one with >= 2.4)
91      * - 2. install php5-dev package (apt-get install php5-dev)
92      * - 3. install mongo PECL extension (pecl install mongo)
93      * - 4. enable mongo (echo "extension=mongo.so" > /etc/php5/mods-available/mongo.ini && php5enmod mongo)
94      * - 5. create administrative user manager in Mongo (mongo -> use admin -> db.addUser({user: "root", pwd: "123456", roles: [ "userAdminAnyDatabase", "readWriteAnyDatabase", "dbAdminAnyDatabase", "clusterAdmin" ]}))
95      * - 6. enable auth for Mongo (nano /etc/mongodb.conf -> auth = true)
96      * - 7. restart MongoDB (service mongodb restart)
97      *
98      * Unlike MySQL, MongoDB manages users per database.
99      * Therefor we cannot use one user for multiple databases. Instead, we have to add him each time via the admin user.
100      */
101
102     /**
103      * Stores the MongoDB connection.
104      * @var object
105      */
106     private $_connection = null;
107
108     /**
109      * Stores the MongoDB admin user.
110      * @var string
111      */
112     const USER = "root";
113
114     /**
115      * Stores the MongoDB admin password.
116      * @var string
117      */
118     const PW = "123456";
119
120     /**
121      * Stores the MongoDB host address.
122      * @var string
123      */
124     const HOST = "127.0.0.1";
125
126     /**
127      * Stores the MongoDB port.
128      * @var int
129      */
130     const PORT = 27017;
131
132     /**
133      * Adds the user to given database.
134      * If no connection exists, the user already exists or the database doesn't exist,
135      * null is returned.
136      *
137      * @param string $db the database to use
138      * @param array $user the user to add
139      * @return bool true if user added
140      */
141     private function addUser($db, $user) {
142         if ($this->isConnected() && !$this->userExists($db, $user)) {
143             $roles = "";
144
145             foreach ($user['roles'] as $index => $role) {
146                 $roles .= "\"".$role."\"";
147
148                 if ($index !== count($user['roles']) - 1) {
149                     $roles .= ", ";
150                 }
151             }
152
153             return $this->exec($db, "db.system.users.insert({ user: \"".$user['username']."\", pwd: \"".$user['password']."\", roles: [ ".$roles." ] })");
154             //return $this->exec($db, "db.addUser({ user: \"".$user['username']."\", pwd: \"".$user['password']."\", roles: [ ".$roles." ] })");
155         }
156
157         return null;
158     }
159
160     /**
161      * Changes the users password in given DB.
162      * If no connection exists, the user doesn't exist or the DB doesn't exist,
163      * null is returned.
164      *
165      * @param string $db the database name
166      * @param string $user the user to change
167      * @param string $password the new password
168      * @return bool true if password changes
169      */
170     private function changePassword($db, $user, $password) {
171         if ($this->isConnected() && $this->dbExists($db) && $this->userExists($db, $user)) {
172             $old_user = $this->getUser($db, $user);
173
174             if ($this->dropUser($user, $db)) {
175                 return $this->addUser($db, array(
b1a6a5 176                         'username' => $user,
MC 177                         'password' => $password,
178                         'roles' => $old_user['roles']
179                     ));
bd68aa 180             }
MC 181
182             return false;
183         }
184
185         return null;
186     }
187
188     /**
189      * Connects to the server and authentificates.
190      * If the authentificaten goes wrong or another error encounters,
191      * false is returned.
192      * If we already have an open connection we try to disconnect and connect again.
193      * If this fails, false is returned.
194      *
195      * @return object $connection the MongoDB connection
196      */
197     private function connect() {
198         try {
199             if ($this->isConnected() && !$this->disconnect()) {
200                 return false;
201             }
202
203             $this->_connection = new MongoClient("mongodb://".self::USER.":".self::PW."@".self::HOST.":".self::PORT."/admin");
204
205             return $this->_connection;
206         } catch (MongoConnnectionException $e) {
207             $app->log('Unable to connect to MongoDB: '.$e, LOGLEVEL_ERROR);
208             $this->_connection = null;
209
210             return false;
211         }
212     }
213
214     /**
215      * Checks if the database exists.
216      * If no connection exists,
217      * null is returned.
218      *
219      * @param string $db the database name
220      * @return bool true if exists
221      */
222     private function dbExists($db) {
223         if ($this->isConnected()) {
224             return in_array($db, $this->getDBs());
225         }
226
227         return null;
228     }
229
230     /**
231      * Closes the MongoDB connection.
232      * If no connection exists and nothing is done,
233      * null is returned.
234      *
235      * @return bool true if closed
236      */
237     private function disconnect() {
238         if ($this->isConnected()) {
239             $status = $this->_connection->close();
240
241             if ($status) {
242                 $this->_connection = null;
243             }
244
245             return $status;
246         }
247
248         return null;
249     }
250
251     /**
252      * Drops the given database.
253      * If no connection exists or the database doesn't exist,
254      * null is returned.
255      *
256      * @param string $db the database's to drop name
257      */
258     private function dropDB($db) {
259         if ($this->isConnected() && $this->dbExists($db)) {
260             return (bool) $this->_connection->dropDB($db)['ok'];
261         }
262
263         return null;
264     }
265
266     /**
267      * Drops the given user from database.
268      * If no DB is defined, the user is dropped from all databases.
269      * If there's an error when dropping the user from all DBs, an array containing the
270      * names of the failed DBs is returned.
271      * If no connection exists, the database doesn't exist or the user is not in DB,
272      * null is returned.
273      *
274      * @param string $user the user to drop
275      * @param string $db the database name
276      * @return bool true if dropped
277      */
278     private function dropUser($user, $db = null) {
279         if ($this->isConnected()) {
280             if ($db !== null && $this->dbExists($db) && $this->userExists($db, $user)) {
281                 return $this->exec($db, "db.removeUser(\"".$user."\")");
282             } else {
283                 $dbs = $this->getDBs();
284
285                 if ((bool) $dbs) {
286                     $failures = array();
287
288                     foreach ($dbs as $db) {
289                         $exists = $this->userExists($db, $user);
290
291                         if ($exists) {
292                             if (!$this->dropUser($user, $db)) {
293                                 $failures[] = $db;
294                             }
295                         }
296                     }
297                 }
298
299                 return (bool) $failures ? $failures : true;
300             }
301         }
302
303         return null;
304     }
305
306     /**
307      * Executed the command on the MongoDB server.
308      * If no connection exists and thus nothing can be done,
309      * null is returned.
310      *
311      * @param string $db the database to query
312      * @param string $query the command to execute
313      * @return array the result of the query
314      */
315     private function exec($db, $query) {
316         if ($this->isConnected()) {
317             $db = $this->selectDB($db);
318             $result = $db->execute($query);
319
320             if ((bool) $result['ok']) {
321                 return $result;
322             }
323
324             return false;
325         }
326
327         return null;
328     }
329
330     /**
331      * Checks if the connection exists.
332      *
333      * @return true if connected
334      */
335     private function isConnected() {
336         return $this->_connection !== null;
337     }
338
339     /**
340      * Generates a MongoDB compatible password.
341      *
342      * @param string $user the username
343      * @param string $password the user password
344      * @return string the MD5 string
345      */
346     private function generatePassword($user, $password) {
347         return md5($user.":mongo:".$password);
348     }
349
350     /**
351      * Returns the databases found on connection.
352      * If no connection exists and therefor no DBs can be found,
353      * null is returned.
354      *
355      * @return array $names the databases's name
356      */
357     private function getDBs() {
358         if ($this->isConnected()) {
359             $dbs = $this->_connection->listDBs();
360
361             if ((bool) $dbs && isset($dbs['databases'])) {
362                 $names = array();
363
364                 foreach ($dbs['databases'] as $db) {
365                     $names[] = $db['name'];
366                 }
367
368                 return $names;
369             }
370         }
371
372         return null;
373     }
374
375     /**
376      * Returns the user entry for given database.
377      * If no connection exists, the database doesn't exist or the user doesn't exist
378      * null is returned.
379      *
380      * @param string $db the database name
381      * @param string $user the user to return
382      * @return array $user the user in DB
383      */
384     private function getUser($db, $user) {
385         if ($this->isConnected() && $this->dbExists($db) && $this->userExists($db, $user)) {
386             $result = $this->selectDB($db)->selectCollection("system.users")->find(array( 'user' => $user ));
387
388             // ugly fix to return user
389             foreach ($result as $user) {
390                 return $user;
391             }
392         }
393
394         return null;
395     }
396
397     /**
398      * Returns the users for given database.
399      * If no connection exists or the database doesn't exist,
400      * null is returned.
401      *
402      * @param string $db the database name
403      * @return array $users the users in DB
404      */
405     private function getUsers($db) {
406         if ($this->isConnected() && $this->dbExists($db)) {
407             $result = $this->selectDB($db)->selectCollection("system.users")->find();
408
409             $users = array();
410
411             foreach ($result as $record) {
412                 $users[] = $record['user'];
413             }
414
415             return $users;
416         }
417
418         return null;
419     }
420
421     /**
422      * Checks if the given user exists in given database.
423      * If no connection exists or the given database doesn't exist
424      * null is returned.
425      *
426      * @param string $db the database name
427      * @param string $user the user to check
428      * @return bool true if user exists
429      */
430     private function userExists($db, $user) {
431         if ($this->isConnected() && $this->dbExists($db)) {
432             $users = $this->getUsers($db);
433
434             return in_array($user, $users);
435         }
436
437         return null;
438     }
439
440     /**
441      * Renames the MongoDB database to provided name.
442      * If no connection exists, the source DB doesn't exist or the target DB already exists,
443      * null is returned.
444      *
445      * @param string $old_name the old database name
446      * @param string $new_name the new database name
447      * @return bool true if renamed
448      */
449     private function renameDB($old_name, $new_name) {
450         if ($this->isConnected() && $this->dbExists($old_name) && !$this->dbExists($new_name)) {
451             if ($this->exec($old_name, "db.copyDatabase(\"".$old_name."\", \"".$new_name."\", \"".self::HOST."\", \"".self::USER."\", \"".self::PW."\")")) {
452                 $this->dropDB($old_name);
453
454                 return true;
455             }
456
457             return false;
458         }
459
460         return null;
461     }
462
463     /**
464      * Switched the selected database.
465      * MongoDB acts on a per-DB level (user management) and we always need to
466      * ensure we have the right DB selected.
467      * If no connection exists and thus nothing is done,
468      * null is returned.
469      *
470      * @param string $db the database to use
471      * @return object the MongoDB database object
472      */
473     private function selectDB($db) {
474         if ($this->isConnected()) {
475             return $this->_connection->selectDB($db);
476         }
477
478         return null;
479     }
480
481
482     /**
483      * This function is called when a DB is created from within the ISPConfig3 interface.
484      * We need to create the DB and allow all users to connect to it that are choosen.
485      * Since MongoDB doesn't create a DB before any data is stored in it, it's important
486      * to store the users so it contains data -> is created.
487      *
488      * @param string $event_name the name of the event (insert, update, delete)
489      * @param array $data the event data (old and new)
490      * @return only if something is wrong
491      */
492     function db_insert($event_name, $data) {
493         global $app, $conf;
494
495         // beside checking for MongoDB we also check if the DB is active because only then we add users
496         // -> MongoDB needs users to create the DB
497         if ($data['new']['type'] == 'mongo' && $data['new']['active'] == 'y') {
498             if ($this->connect() === false) {
499                 $app->log("Unable to connect to MongoDB: Connecting using connect() failed.", LOGLEVEL_ERROR);
500                 return;
501             }
502
2af58c 503             $db_user = $app->db->queryOneRecord("SELECT `database_user`, `database_password_mongo` FROM `web_database_user` WHERE `database_user_id` = ?", $data['new']['database_user_id']);
MC 504             $db_ro_user = $app->db->queryOneRecord("SELECT `database_user`, `database_password_mongo` FROM `web_database_user` WHERE `database_user_id` = ?", $data['new']['database_ro_user_id']);
bd68aa 505
MC 506             $user = $db_user['database_user'];
507             $password = $db_user['database_password_mongo'];
508
509             $ro_user = $db_ro_user['database_user'];
510             $ro_password = $db_ro_user['database_password_mongo'];
511
512             $db = $data['new']['database_name'];
513
514             if ((bool) $db_user) {
b1a6a5 515                 if ($user == 'root') {
MC 516                     $app->log("User root not allowed for client databases", LOGLEVEL_WARNING);
517                 } else {
518                     if (!$this->addUser($db, array(
519                                 'username' => $user,
520                                 'password' => $password,
521                                 'roles' => array(
522                                     "readWrite",
523                                     "dbAdmin"
524                                 )
525                             ))) {
526                         $app->log("Error while adding user: ".$user." to DB: ".$db, LOGLEVEL_WARNING);
527                     }
bd68aa 528                 }
MC 529             }
530
531             if ($db_ro_user && $data['new']['database_user_id'] != $data['new']['database_ro_user_id']) {
b1a6a5 532                 if ($user == 'root') {
MC 533                     $app->log("User root not allowed for client databases", LOGLEVEL_WARNING);
534                 } else {
535                     if (!$this->addUser($db, array(
536                                 'username' => $ro_user,
537                                 'password' => $ro_password,
538                                 'roles' => array(
539                                     "read"
540                                 )
541                             ))) {
542                         $app->log("Error while adding read-only user: ".$user." to DB: ".$db, LOGLEVEL_WARNING);
543                     }
bd68aa 544                 }
MC 545             }
546
547             $this->disconnect();
548         }
549     }
b1a6a5 550
bd68aa 551
MC 552     /**
553      * This function is called when a DB is updated from within the ISPConfig interface.
554      * Updating the DB needs a lot of changes. First, we need to recheck all users that
555      * have permissions to access the DB. Maybe we also need to rename the DB and change
556      * it's type (MySQL, MongoDB etc.)...hard work here :)
557      *
558      * @param string $event_name the name of the event (insert, update, delete)
559      * @param array $data the event data (old and new)
560      * @return only if something is wrong
561      */
b1a6a5 562     function db_update($event_name, $data) {
bd68aa 563         global $app, $conf;
MC 564
565         if ($data['old']['active'] == 'n' && $data['new']['active'] == 'n') {
566             return;
567         }
568
569         // currently switching from MongoDB <-> MySQL isn't supported
570         if ($data['old']['type'] == 'mongo' && $data['new']['type'] == 'mongo') {
571             if ($this->connect() === false) {
572                 $app->log("Unable to connect to MongoDB: Connecting using connect() failed.", LOGLEVEL_ERROR);
573                 return;
574             }
575
2af58c 576             $db_user = $app->db->queryOneRecord("SELECT `database_user`, `database_password_mongo` FROM `web_database_user` WHERE `database_user_id` = ?", $data['new']['database_user_id']);
MC 577             $db_ro_user = $app->db->queryOneRecord("SELECT `database_user`, `database_password_mongo` FROM `web_database_user` WHERE `database_user_id` = ?", $data['new']['database_ro_user_id']);
bd68aa 578
MC 579             $user = $db_user['database_user'];
580             $password = $db_user['database_password_mongo'];
581
582             $ro_user = $db_ro_user['database_user'];
583             $ro_password = $db_ro_user['database_password_mongo'];
584
585             $db = $data['new']['database_name'];
586
587             // create the database user if database was disabled before
588             if ($data['new']['active'] == 'y' && $data['old']['active'] == 'n') {
589                 // since MongoDB creates DBs on-the-fly we can use the db_insert method which takes care of adding
590                 // users to a given DB
591                 $this->db_insert($event_name, $data);
592             } else if ($data['new']['active'] == 'n' && $data['old']['active'] == 'y') {
b1a6a5 593                     $users = $this->getUsers($db);
bd68aa 594
b1a6a5 595                     if ((bool) $users) {
MC 596                         foreach ($users as $user) {
597                             $this->dropUser($user, $db);
598                         }
bd68aa 599                     }
b1a6a5 600                 } else {
bd68aa 601                 // selected user has changed -> drop old one
MC 602                 if ($data['new']['database_user_id'] != $data['old']['database_user_id']) {
2af58c 603                     $old_db_user = $app->db->queryOneRecord("SELECT `database_user`, `database_password_mongo` FROM `web_database_user` WHERE `database_user_id` = ?", $data['old']['database_user_id']);
bd68aa 604
MC 605                     if ((bool) $old_db_user) {
606                         if ($old_db_user['database_user'] == 'root') {
607                             $app->log("User root not allowed for client databases", LOGLEVEL_WARNING);
608                         } else {
609                             $this->dropUser($old_db_user['database_user'], $db);
610                         }
611                     }
612                 }
613
614                 // selected read-only user has changed -> drop old one
615                 if ($data['new']['database_ro_user_id'] != $data['old']['database_ro_user_id']) {
2af58c 616                     $old_db_user = $app->db->queryOneRecord("SELECT `database_user`, `database_password_mongo` FROM `web_database_user` WHERE `database_user_id` = ?", $data['old']['database_ro_user_id']);
bd68aa 617
MC 618                     if ((bool) $old_db_user) {
619                         if ($old_db_user['database_user'] == 'root') {
b1a6a5 620                             $app->log("User root not allowed for client databases", LOGLEVEL_WARNING);
bd68aa 621                         } else {
MC 622                             $this->dropUser($old_db_user['database_user'], $db);
623                         }
624                     }
625                 }
626
627                 // selected user has changed -> add new one
628                 if ($data['new']['database_user_id'] != $data['old']['database_user_id']) {
629                     if ((bool) $db_user) {
630                         if ($user == 'root') {
631                             $app->log("User root not allowed for client databases", LOGLEVEL_WARNING);
632                         } else {
633                             $this->addUser($db, array(
b1a6a5 634                                     'username' => $user,
MC 635                                     'password' => $password,
636                                     'roles' => array(
637                                         "readWrite",
638                                         "dbAdmin"
639                                     )
640                                 ));
bd68aa 641                         }
MC 642                     }
643                 }
644
645                 // selected read-only user has changed -> add new one
646                 if ($data['new']['database_ro_user_id'] != $data['old']['database_ro_user_iduser_id']) {
647                     if ((bool) $db_ro_user && $data['new']['database_user_id'] != $data['new']['database_ro_user_id']) {
648                         if ($ro_user == 'root') {
649                             $app->log("User root not allowed for client databases", LOGLEVEL_WARNING);
650                         } else {
651                             $this->addUser($db, array(
b1a6a5 652                                     'username' => $ro_user,
MC 653                                     'password' => $ro_password,
654                                     'roles' => array(
655                                         "read"
656                                     )
657                                 ));
bd68aa 658                         }
MC 659                     }
660                 }
661
662                 // renamed?
663                 /*
664                 if ($data['old']['database_name'] != $data['new']['database_name']) {
665                     $old_name = $data['old']['database_name'];
666                     $new_name = $data['new']['database_name'];
667
668                     if ($this->renameDB($oldName, $newName)) {
669                         $app->log("Renamed MongoDB database: ".$old_name." -> ".$new_name, LOGLEVEL_DEBUG);
670                     } else {
671                         $app->log("Renaming MongoDB database failed: ".$old_name." -> ".$new_name, LOGLEVEL_WARNING);
672                     }
673                 }
674                 */
675             }
676
677             // switching from MySQL <-> Mongo isn't supported
678             // no idea what we should do here...would be best to permit in interface?
679
680             // remote access isn't supported by MongoDB (limiting to IP),
681             // we therefor don't listen for it's changes
682         }
683
684         $this->disconnect();
685     }
686
b1a6a5 687
bd68aa 688     /**
MC 689      * This function is called when a DB is deleted from within the ISPConfig interface.
690      * All we need to do is to delete the database.
691      *
692      * @param string $event_name the name of the event (insert, update, delete)
693      * @param array $data the event data (old and new)
694      * @return only if something is wrong
695      */
b1a6a5 696     function db_delete($event_name, $data) {
bd68aa 697         global $app, $conf;
MC 698
699         if ($data['old']['type'] == 'mongo') {
700             if ($this->connect() === false) {
701                 $app->log("Unable to connect to MongoDB: Connecting using connect() failed.", LOGLEVEL_ERROR);
702                 return;
703             }
704
705             $db_to_drop = $data['old']['database_name'];
706
707             if ($this->dropDB($db_to_drop)) {
708                 $app->log("Dropping MongoDB database: ".$db_to_drop, LOGLEVEL_DEBUG);
709             } else {
710                 $app->log("Error while dropping MongoDB database: ".$db_to_drop, LOGLEVEL_WARNING);
711             }
712
713             $this->disconnect();
714         }
715     }
716
717
718     /**
719      * This function is called when a user is inserted from within the ISPConfig interface.
720      * Since users are separated from databases we don't do anything here.
721      * As soon as an user is associated to a DB, we add him there.
722      *
723      * @param string $event_name the name of the event (insert, update, delete)
724      * @param array $data the event data (old and new)
725      */
b1a6a5 726     function db_user_insert($event_name, $data) {}
MC 727
bd68aa 728
MC 729     /**
730      * This function is called when a user is updated from within the ISPConfig interface.
731      * The only thing we need to listen for here are password changes.
732      * We than need to change those in all databases the user uses.
733      *
734      * @param string $event_name the name of the event (insert, update, delete)
735      * @param array $data the event data (old and new)
736      * @return only if something is wrong
737      */
b1a6a5 738     function db_user_update($event_name, $data) {
bd68aa 739         global $app, $conf;
MC 740
741         if ($data['old']['database_user'] == $data['new']['database_user']
b1a6a5 742             && ($data['old']['database_password'] == $data['new']['database_password']
MC 743                 || $data['new']['database_password'] == '')) {
744             return;
bd68aa 745         }
MC 746
747         if ($this->connect() === false) {
748             $app->log("Unable to connect to MongoDB: Connecting using connect() failed.", LOGLEVEL_ERROR);
749             return;
750         }
751
752         if ($data['old']['database_user'] != $data['new']['database_user']) {
753             // username has changed
754             $dbs = $this->getDBs();
755
756             if ((bool) $dbs) {
757                 foreach ($dbs as $db) {
758                     if ($this->userExists($db, $data['old']['database_user'])) {
759                         if (!$this->userExists($db, $data['new']['database_user'])) {
760                             $user = $this->getUser($db, $data['old']['database_user']);
761
762                             if ($this->dropUser($data['old']['database_user'], $db)) {
763                                 if ($this->addUser($db, array(
b1a6a5 764                                             'username' => $data['new']['database_user'],
MC 765                                             'password' => md5($data['new']['database_password_mongo']),
766                                             'roles' => $user['roles']
767                                         ))) {
bd68aa 768                                     $app->log("Created user: ".$data['new']['database_user']." in DB: ".$db, LOGLEVEL_DEBUG);
MC 769                                 } else {
770                                     $app->log("Couldn't create user: ".$data['new']['database_user']." in DB: ".$db, LOGLEVEL_WARNING);
771                                 }
772                             } else {
773                                 $app->log("Couldn't drop user: ".$data['old']['database_user']." in DB: ".$db, LOGLEVEL_WARNING);
774                             }
775                         } else {
776                             $app->log("User: ".$data['new']['database_user']." already exists in DB: ".$db, LOGLEVEL_WARNING);
777                         }
778                     }
779                 }
780             }
781         }
782
783         if ($data['old']['database_password'] != $data['new']['database_password']
784             || $data['old']['database_user'] != $data['new']['database_user']) {
785             // password only has changed
786             $dbs = $this->getDBs();
787
788             if ((bool) $dbs) {
789                 foreach ($dbs as $db) {
790                     if ($this->userExists($db, $data['new']['database_user'])) {
791                         if ($this->changePassword($db, $data['new']['database_user'], md5($data['new']['database_password_mongo']))) {
792                             $app->log("Changed user's: ".$data['new']['database_user']." password in DB: ".$db, LOGLEVEL_DEBUG);
793                         } else {
794                             $app->log("Couldn't change user's: ".$data['new']['database_user']." password in DB: ".$db, LOGLEVEL_WARNING);
795                         }
796                     }
797                 }
798             }
799         }
800
801         $this->disconnect();
802     }
803
b1a6a5 804
bd68aa 805     /**
MC 806      * This function is called when a user is deleted from within the ISPConfig interface.
807      * Since MongoDB uses per-DB user management, we have to find every database where the user is
808      * activated and delete him there.
809      *
810      * @param string $event_name the name of the event (insert, update, delete)
811      * @param array $data the event data (old and new)
812      * @return only if something is wrong
813      */
814     function db_user_delete($event_name, $data) {
815         global $app, $conf;
816
817         if ($this->connect() === false) {
818             $app->log("Unable to connect to MongoDB: Connecting using connect() failed.", LOGLEVEL_ERROR);
819             return;
820         }
821
822         if ($this->dropUser($data['old']['database_user']) === true) {
823             $app->log("Dropped MongoDB user: ".$data['old']['database_user'], LOGLEVEL_DEBUG);
824         } else {
825             $app->log("Error while dropping MongoDB user: ".$data['old']['database_user'], LOGLEVEL_WARNING);
826         }
827
828         $this->disconnect();
829     }
830
831 }