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