Skip to content

Commit 7251aa8

Browse files
committed
Add users management page
1 parent af28792 commit 7251aa8

File tree

11 files changed

+630
-12
lines changed

11 files changed

+630
-12
lines changed

docs/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# Free MongoDB GUI powered by PHP
22

3-
Visually administrate your MongoDB database. Create, read, update & delete your documents...<br>
3+
Visually administrate your MongoDB database. Create, read, update & delete your documents.<br>
44
Query your MongoDB database with SELECT SQL statements. You can also create & drop indexes.<br>
55
Autocompletion is available for fields, MongoDB & SQL keywords via `Ctrl` `Space` combination.<br>
6-
Additional features: Export documents to JSON. Import documents from JSON. ISODate support.
6+
Additional features: Export documents to JSON. Import documents from JSON. Manage users.
77

88
Screenshots
99
-----------

routes.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,26 @@
141141
CollectionController::class . '@dropIndexAction'
142142
);
143143

144+
$router->get(
145+
MPG_SERVER_PATH . '/manageUsers',
146+
DatabaseController::class . '@renderUsersViewAction'
147+
);
148+
149+
$router->post(
150+
MPG_SERVER_PATH . '/ajaxDatabaseCreateUser',
151+
DatabaseController::class . '@createUserAction'
152+
);
153+
154+
$router->post(
155+
MPG_SERVER_PATH . '/ajaxDatabaseListUsers',
156+
DatabaseController::class . '@listUsersAction'
157+
);
158+
159+
$router->post(
160+
MPG_SERVER_PATH . '/ajaxDatabaseDropUser',
161+
DatabaseController::class . '@dropUserAction'
162+
);
163+
144164
$router->get(
145165
MPG_SERVER_PATH . '/logout',
146166
LoginController::class . '@logoutAction'

src/Controllers/DatabaseController.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,102 @@ public function createCollectionAction() : Response {
201201

202202
}
203203

204+
public function renderUsersViewAction() : Response {
205+
206+
LoginController::ensureUserIsLogged();
207+
208+
return new Response(200, $this->renderView('database.users', [
209+
'databaseNames' => self::getDatabaseNames()
210+
]));
211+
212+
}
213+
214+
/**
215+
* @see https://docs.mongodb.com/manual/reference/command/createUser/
216+
*/
217+
public function createUserAction() : Response {
218+
219+
try {
220+
$decodedRequestBody = $this->getDecodedRequestBody();
221+
} catch (\Throwable $th) {
222+
return new JsonResponse(400, ErrorNormalizer::normalize($th, __METHOD__));
223+
}
224+
225+
try {
226+
227+
$database = MongoDBHelper::getClient()->selectDatabase(
228+
$decodedRequestBody['databaseName']
229+
);
230+
231+
// TODO: Check createUser result?
232+
$database->command([
233+
'createUser' => $decodedRequestBody['userName'],
234+
'pwd' => $decodedRequestBody['userPassword'],
235+
'roles' => $decodedRequestBody['userRoles']
236+
]);
237+
238+
} catch (\Throwable $th) {
239+
return new JsonResponse(500, ErrorNormalizer::normalize($th, __METHOD__));
240+
}
241+
242+
return new JsonResponse(200, true);
243+
244+
}
245+
246+
/**
247+
* @see https://docs.mongodb.com/manual/reference/command/usersInfo/
248+
*/
249+
public function listUsersAction() : Response {
250+
251+
try {
252+
$decodedRequestBody = $this->getDecodedRequestBody();
253+
} catch (\Throwable $th) {
254+
return new JsonResponse(400, ErrorNormalizer::normalize($th, __METHOD__));
255+
}
256+
257+
try {
258+
259+
$database = MongoDBHelper::getClient()->selectDatabase(
260+
$decodedRequestBody['databaseName']
261+
);
262+
263+
$usersInfoCommandResult = $database->command(['usersInfo' => 1]);
264+
$usersInfo = $usersInfoCommandResult->toArray()[0];
265+
266+
} catch (\Throwable $th) {
267+
return new JsonResponse(500, ErrorNormalizer::normalize($th, __METHOD__));
268+
}
269+
270+
return new JsonResponse(200, $usersInfo);
271+
272+
}
273+
274+
/**
275+
* @see https://docs.mongodb.com/manual/reference/command/dropUser/
276+
*/
277+
public function dropUserAction() : Response {
278+
279+
try {
280+
$decodedRequestBody = $this->getDecodedRequestBody();
281+
} catch (\Throwable $th) {
282+
return new JsonResponse(400, ErrorNormalizer::normalize($th, __METHOD__));
283+
}
284+
285+
try {
286+
287+
$database = MongoDBHelper::getClient()->selectDatabase(
288+
$decodedRequestBody['databaseName']
289+
);
290+
291+
// TODO: Check dropUser result?
292+
$database->command(['dropUser' => $decodedRequestBody['userName']]);
293+
294+
} catch (\Throwable $th) {
295+
return new JsonResponse(500, ErrorNormalizer::normalize($th, __METHOD__));
296+
}
297+
298+
return new JsonResponse(200, true);
299+
300+
}
301+
204302
}

static/css/mpg.css

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55

66
}
77

8+
.navbar.sticky-top {
9+
10+
z-index: 1060;
11+
12+
}
13+
814
@media screen and (min-width: 768px) {
915

1016
.navbar {
@@ -239,7 +245,7 @@ button, input {
239245

240246
}
241247

242-
#mpg-export-button {
248+
#mpg-export-button, #mpg-open-create-user-modal-button {
243249

244250
position: relative;
245251
top: -5px;
@@ -282,3 +288,29 @@ button, input {
282288
}
283289

284290
}
291+
292+
#mpg-create-user-modal {
293+
294+
top: 50px;
295+
296+
}
297+
298+
@media screen and (min-width: 768px) {
299+
300+
#mpg-create-user-modal {
301+
302+
top: 45px;
303+
304+
}
305+
306+
}
307+
308+
@media screen and (min-width: 768px) {
309+
310+
#mpg-create-user-modal .modal-dialog {
311+
312+
max-width: 300px;
313+
314+
}
315+
316+
}

static/js/mpg.collection.indexes.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,6 @@ MPG.reloadCollectionIndexes = function() {
179179

180180
MPG.collectionIndexes = JSON.parse(response);
181181

182-
document.querySelector('#mpg-indexes-column').classList.remove('d-none');
183-
184182
var indexesTableBody = document.querySelector('#mpg-indexes-table tbody');
185183
indexesTableBody.innerHTML = '';
186184

@@ -214,10 +212,12 @@ MPG.reloadCollectionIndexes = function() {
214212
+ '<td>' + (collectionIndex.isUnique ? 'Yes' : 'No') + '</td>'
215213
+ '<td>' + collectionIndexDropButton + '</td></tr>';
216214

217-
MPG.eventListeners.addDropIndex();
218-
219215
});
220216

217+
MPG.eventListeners.addDropIndex();
218+
219+
document.querySelector('#mpg-indexes-column').classList.remove('d-none');
220+
221221
},
222222
JSON.stringify(requestBody)
223223
);
@@ -408,9 +408,9 @@ MPG.eventListeners.addDropIndex = function() {
408408
*
409409
* @returns {void}
410410
*/
411-
MPG.eventListeners.addDismissible = function() {
411+
MPG.eventListeners.addDismissibleAlerts = function() {
412412

413-
document.querySelectorAll('.alert .close[data-dismiss="alert"]')
413+
document.querySelectorAll('.alert [data-dismiss="alert"]')
414414
.forEach(function(alertCloseButton) {
415415

416416
alertCloseButton.addEventListener('click', function(event) {
@@ -430,6 +430,6 @@ window.addEventListener('DOMContentLoaded', function(_event) {
430430
MPG.eventListeners.addMenuToggle();
431431
MPG.eventListeners.addDatabases();
432432
MPG.eventListeners.addCreateIndex();
433-
MPG.eventListeners.addDismissible();
433+
MPG.eventListeners.addDismissibleAlerts();
434434

435435
});

static/js/mpg.collection.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ MPG.eventListeners.addDropColl = function() {
271271
var dropConfirmation = window.confirm(
272272
'Do you REALLY want to DROP collection: '
273273
+ MPG.databaseName + '.' + MPG.collectionName
274-
)
274+
);
275275

276276
if ( dropConfirmation === false ) {
277277
return;

0 commit comments

Comments
 (0)