From f73a41753d2608187a0c85a28e419b8ea839d671 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 23 Feb 2018 15:26:17 +0100 Subject: [adduser] Extend module to simple user manager (add/edit/remove) TODO: Assign roles --- modules-available/adduser/config.json | 1 + modules-available/adduser/lang/de/messages.json | 7 +- modules-available/adduser/lang/de/module.json | 3 + .../adduser/lang/de/template-tags.json | 14 +- modules-available/adduser/lang/en/messages.json | 7 +- modules-available/adduser/lang/en/module.json | 3 + .../adduser/lang/en/template-tags.json | 14 +- modules-available/adduser/page.inc.php | 198 +++++++++++++++++---- .../adduser/permissions/permissions.json | 14 ++ modules-available/adduser/style.css | 15 ++ modules-available/adduser/templates/header.html | 3 + .../adduser/templates/page-adduser.html | 35 ++-- .../adduser/templates/page-edituser.html | 72 ++++++++ .../adduser/templates/page-userlist.html | 73 ++++++++ .../systemstatus/templates/_page.html | 2 +- style/default.css | 14 -- 16 files changed, 404 insertions(+), 71 deletions(-) create mode 100644 modules-available/adduser/lang/de/module.json create mode 100644 modules-available/adduser/lang/en/module.json create mode 100644 modules-available/adduser/permissions/permissions.json create mode 100644 modules-available/adduser/style.css create mode 100644 modules-available/adduser/templates/header.html create mode 100644 modules-available/adduser/templates/page-edituser.html create mode 100644 modules-available/adduser/templates/page-userlist.html diff --git a/modules-available/adduser/config.json b/modules-available/adduser/config.json index 2c63c085..706412d0 100644 --- a/modules-available/adduser/config.json +++ b/modules-available/adduser/config.json @@ -1,2 +1,3 @@ { + "category":"main.content" } diff --git a/modules-available/adduser/lang/de/messages.json b/modules-available/adduser/lang/de/messages.json index 4f62dcbf..0ab4571f 100644 --- a/modules-available/adduser/lang/de/messages.json +++ b/modules-available/adduser/lang/de/messages.json @@ -1,5 +1,8 @@ { + "adduser-success": "Benutzer erfolgreich hinzugef\u00fcgt", + "db-error": "Datenbankfehler: {{0}}", + "password-changed": "Passwort ge\u00e4ndert", "password-mismatch": "Passwort und Passwortbest\u00e4tigung stimmen nicht \u00fcberein", - "adduser-disabled": "Keine ausreichenden Rechte, um weitere Benutzer hinzuzuf\u00fcgen", - "adduser-success": "Benutzer erfolgreich hinzugef\u00fcgt" + "user-edited": "Benutzerdaten wurden ge\u00e4ndert", + "user-not-found": "Benutzer mit ID {{0}} nicht gefunden" } \ No newline at end of file diff --git a/modules-available/adduser/lang/de/module.json b/modules-available/adduser/lang/de/module.json new file mode 100644 index 00000000..e4c785b5 --- /dev/null +++ b/modules-available/adduser/lang/de/module.json @@ -0,0 +1,3 @@ +{ + "module_name": "Nutzerverwaltung" +} \ No newline at end of file diff --git a/modules-available/adduser/lang/de/template-tags.json b/modules-available/adduser/lang/de/template-tags.json index 99d17947..0b6b2530 100644 --- a/modules-available/adduser/lang/de/template-tags.json +++ b/modules-available/adduser/lang/de/template-tags.json @@ -1,6 +1,16 @@ { + "lang_addUser": "Nutzer hinzuf\u00fcgen", + "lang_changeLoginHint": "Sie k\u00f6nnen den Namen, den der Nutzer beim Login angeben muss, \u00e4ndern. Dies ist nur bei lokalen Nutzern m\u00f6glich, die nicht \u00fcber LDAP\/AD authentifiziert werden.", + "lang_changeOwnPasswordHint": "Ihr eigenes Passwort k\u00f6nnen Sie \u00fcber den Button \"Passwort \u00e4ndern\" im Men\u00fc \u00e4ndern.", + "lang_changePassword": "Neues Passwort", "lang_confirmation": "Wiederholen", "lang_createUser": "Benutzer anlegen", + "lang_editUser": "Benutzer bearbeiten", + "lang_email": "E-Mail", "lang_fullName": "Vollst\u00e4ndiger Name", - "lang_telephone": "Telefon" -} + "lang_login": "Login", + "lang_name": "Name", + "lang_phone": "Telefon", + "lang_userManagement": "Benutzerverwaltung", + "lang_userlist": "Benutzerliste" +} \ No newline at end of file diff --git a/modules-available/adduser/lang/en/messages.json b/modules-available/adduser/lang/en/messages.json index 3a402278..69af3227 100644 --- a/modules-available/adduser/lang/en/messages.json +++ b/modules-available/adduser/lang/en/messages.json @@ -1,5 +1,8 @@ { + "adduser-success": "User successfully added", + "db-error": "Database error: {{0}}", + "password-changed": "Password changed", "password-mismatch": "Password and password confirmation do not match", - "adduser-disabled": "Insufficient privileges to add more users", - "adduser-success": "User successfully added" + "user-edited": "User data has been updated", + "user-not-found": "User with ID {{0}} not found" } \ No newline at end of file diff --git a/modules-available/adduser/lang/en/module.json b/modules-available/adduser/lang/en/module.json new file mode 100644 index 00000000..f376db34 --- /dev/null +++ b/modules-available/adduser/lang/en/module.json @@ -0,0 +1,3 @@ +{ + "module_name": "User management" +} \ No newline at end of file diff --git a/modules-available/adduser/lang/en/template-tags.json b/modules-available/adduser/lang/en/template-tags.json index 24f8cd42..2c9b8b53 100644 --- a/modules-available/adduser/lang/en/template-tags.json +++ b/modules-available/adduser/lang/en/template-tags.json @@ -1,6 +1,16 @@ { + "lang_addUser": "Add user", + "lang_changeLoginHint": "You can change the login identifier used for logging in. This is only enabled for local acounts that are not linked to LDAP\/AD servers.", + "lang_changeOwnPasswordHint": "You can change your own password by clicking the \"change password\" button in the menu.", + "lang_changePassword": "Change password", "lang_confirmation": "Confirm Password", "lang_createUser": "Create User", + "lang_editUser": "Edit user", + "lang_email": "Email", "lang_fullName": "Full Name", - "lang_telephone": "Telephone" -} + "lang_login": "Login", + "lang_name": "Name", + "lang_phone": "Phone", + "lang_userManagement": "User management", + "lang_userlist": "User list" +} \ No newline at end of file diff --git a/modules-available/adduser/page.inc.php b/modules-available/adduser/page.inc.php index 87aaeef1..a4edcf59 100644 --- a/modules-available/adduser/page.inc.php +++ b/modules-available/adduser/page.inc.php @@ -7,51 +7,177 @@ class Page_AddUser extends Page { User::load(); - if (isset($_POST['action']) && $_POST['action'] === 'adduser') { - // Check required fields - if (empty($_POST['user']) || empty($_POST['pass1']) || empty($_POST['pass2']) || empty($_POST['fullname'])) { - Message::addError('main.empty-field'); - Util::redirect('?do=AddUser'); - } elseif ($_POST['pass1'] !== $_POST['pass2']) { - Message::addError('password-mismatch'); - Util::redirect('?do=AddUser'); - } elseif (!User::hasPermission('superadmin') && Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false) { - Message::addError('adduser-disabled'); - Util::redirect('?do=Session&action=login'); + $action = Request::post(('action'), false, 'string'); + + if ($action === 'adduser') { + $this->addUser(); + } elseif ($action === 'edituser') { + $this->editUser(); + } elseif ($action === 'deleteuser') { + $this->deleteUser(); + } + if (Request::isPost()) { + Util::redirect('?do=adduser'); + } + } + + private function addUser() + { + // Check required fields + $login = Request::post('login', '', 'string'); + $pass1 = Request::post('pass1', '', 'string'); + $pass2 = Request::post('pass2', '', 'string'); + $fullname = Request::post('fullname', '', 'string'); + $phone = Request::post('phone', '', 'string'); + $email = Request::post('email', '', 'string'); + if (empty($login) || empty($pass1) || empty($pass2) || empty($fullname)) { + Message::addError('main.empty-field'); + return; + } elseif ($pass1 !== $pass2) { + Message::addError('password-mismatch'); + return; + } else { + if (Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false) { + User::assertPermission('user.add'); + } + $data = array( + 'login' => $login, + 'pass' => Crypto::hash6($pass1), + 'fullname' => $fullname, + 'phone' => $phone, + 'email' => $email, + ); + Database::exec('INSERT INTO user SET login = :login, passwd = :pass, fullname = :fullname, phone = :phone, email = :email', $data); + $id = Database::lastInsertId(); + // Make it superadmin if first user. This method sucks as it's a race condition but hey... + $ret = Database::queryFirst('SELECT Count(*) AS num FROM user'); + if ($ret !== false && $ret['num'] == 1) { + $ret = Database::exec('UPDATE user SET permissions = 1, userid = 1 WHERE userid = :id', ['id' => $id], true); + if ($ret !== false) { + EventLog::clear(); + } + EventLog::info('Created first user ' . $login); + } else { + EventLog::info(User::getName() . ' created user ' . $login); + } + Message::addInfo('adduser-success'); + return; + } + } + + private function editUser() + { + User::assertPermission('user.edit'); + $userid = Request::post('userid', false, 'int'); + if ($userid === false) { + Message::addError('main.parameter-missing', 'userid'); + return; + } + $user = Database::queryFirst('SELECT userid, login, fullname, phone, email + FROM user WHERE userid = :userid', compact('userid')); + if ($user === false) { + Message::addError('user-not-found', $userid); + return; + } + // Check required fields + $login = Request::post('login', '', 'string'); + $pass1 = Request::post('pass1', '', 'string'); + $pass2 = Request::post('pass2', '', 'string'); + $fullname = Request::post('fullname', '', 'string'); + $phone = Request::post('phone', '', 'string'); + $email = Request::post('email', '', 'string'); + if (empty($login) || empty($fullname)) { + Message::addError('main.empty-field'); + } elseif (!(empty($pass1) && empty($pass2)) && $pass1 !== $pass2) { + Message::addError('password-mismatch'); + } else { + $data = array( + 'login' => $login, + 'fullname' => $fullname, + 'phone' => $phone, + 'email' => $email, + 'userid' => $userid, + ); + $ret = Database::exec('UPDATE user SET login = :login, fullname = :fullname, phone = :phone, email = :email WHERE userid = :userid', $data, true); + if ($ret === false) { + Message::addError('db-error', Database::lastError()); } else { - $data = array( - 'user' => $_POST['user'], - 'pass' => Crypto::hash6($_POST['pass1']), - 'fullname' => $_POST['fullname'], - 'phone' => $_POST['phone'], - 'email' => $_POST['email'], - ); - if (Database::exec('INSERT INTO user SET login = :user, passwd = :pass, fullname = :fullname, phone = :phone, email = :email', $data) != 1) { - Util::traceError('Could not create new user in DB'); + if ($ret > 0) { + Message::addSuccess('user-edited'); } - // Make it superadmin if first user. This method sucks as it's a race condition but hey... - $ret = Database::queryFirst('SELECT Count(*) AS num FROM user'); - if ($ret !== false && $ret['num'] == 1) { - Database::exec('UPDATE user SET permissions = 1'); - EventLog::clear(); - EventLog::info('Created first user ' . $_POST['user']); - } else { - EventLog::info(User::getName() . ' created user ' . $_POST['user']); + if (!empty($pass1)) { + $data = [ + 'pass' => Crypto::hash6($pass1), + 'userid' => $userid, + ]; + Database::exec('UPDATE user SET passwd = :pass WHERE userid = :userid', $data); + Message::addSuccess('password-changed'); } - Message::addInfo('adduser-success'); - Util::redirect('?do=Session&action=login'); } } + Util::redirect('?do=adduser&show=edituser&userid=' . $userid); + } + + private function deleteUser() + { + User::assertPermission('user.remove'); + $userid = Request::post('userid', false, 'int'); + if ($userid === false) { + Message::addError('main.parameter-missing', 'userid'); + return; + } + //\\ + $user = Database::queryFirst('SELECT userid, login, fullname, phone, email + FROM user WHERE userid = :userid', compact('userid')); + if ($user === false) { + Message::addError('user-not-found', $userid); + return; + } + if ($user['userid'] == 1 || $user['userid'] == User::getId()) { + Message::addError('cannot-delete-1-self'); + return; + } + Database::exec('DELETE FROM user WHERE userid = :userid', compact('userid')); + Message::addSuccess('user-deleted', $userid); } protected function doRender() { - // No user was added, check if current user is allowed to add a new user - // Currently you can only add users if there is no user yet. :) - if (!User::hasPermission('superadmin') && Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false) { - Message::addError('adduser-disabled'); - } else { - Render::addTemplate('page-adduser', $_POST); + Render::addTemplate('header'); + $hasUsers = (Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false); + $show = Request::get('show', ($hasUsers ? 'list' : 'adduser'), 'string'); + if ($show === 'adduser') { + // Can add user if: - no user exists yet; - user has explicit permission to add users + if ($hasUsers) { + User::assertPermission('user.add'); + } + Render::addTemplate('page-adduser'); + } elseif ($show === 'edituser') { + User::assertPermission('user.edit'); + $userid = Request::get('userid', false, 'int'); + if ($userid === false) { + Message::addError('main.parameter-missing', 'userid'); + Util::redirect('?do=adduser&show=list'); + } + $user = Database::queryFirst('SELECT userid, login, fullname, phone, email + FROM user WHERE userid = :userid', compact('userid')); + if ($user === false) { + Message::addError('user-not-found', $userid); + } else { + // TODO: LDAP -> disallow pw change, maybe other fields too? + Render::addTemplate('page-edituser', $user); + } + } elseif ($show === 'list') { + User::assertPermission('list.view'); + $page = new Paginate('SELECT userid, login, fullname, phone, email FROM user ORDER BY login', 50); + $data = ['list' => $page->exec()->fetchAll(PDO::FETCH_ASSOC)]; + foreach ($data['list'] as &$u) { + // Don't allow deleting user 1 and self + $u['hide_delete'] = $u['userid'] == 1 || $u['userid'] == User::getId(); + } + unset($u); + Permission::addGlobalTags($data['perms'], null, ['user.add', 'user.edit', 'user.remove']); + $page->render('page-userlist', $data); } } diff --git a/modules-available/adduser/permissions/permissions.json b/modules-available/adduser/permissions/permissions.json new file mode 100644 index 00000000..e778b893 --- /dev/null +++ b/modules-available/adduser/permissions/permissions.json @@ -0,0 +1,14 @@ +{ + "user.add": { + "location-aware": false + }, + "user.edit": { + "location-aware": false + }, + "user.remove": { + "location-aware": false + }, + "list.view": { + "location-aware": false + } +} \ No newline at end of file diff --git a/modules-available/adduser/style.css b/modules-available/adduser/style.css new file mode 100644 index 00000000..e459352b --- /dev/null +++ b/modules-available/adduser/style.css @@ -0,0 +1,15 @@ +.form-adduser { + max-width: 600px; + padding: 10px; + margin: 0 auto; +} + +.form-adduser .form-control { + position: relative; + font-size: 16px; + height: auto; + padding: 10px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} diff --git a/modules-available/adduser/templates/header.html b/modules-available/adduser/templates/header.html new file mode 100644 index 00000000..34011e29 --- /dev/null +++ b/modules-available/adduser/templates/header.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/modules-available/adduser/templates/page-adduser.html b/modules-available/adduser/templates/page-adduser.html index deb911c0..58d705f8 100644 --- a/modules-available/adduser/templates/page-adduser.html +++ b/modules-available/adduser/templates/page-adduser.html @@ -1,28 +1,39 @@
-

{{lang_createUser}}

+

{{lang_createUser}}

-
{{lang_username}} *
-
+
+
+
-
{{lang_password}} *
-
+
+
+
-
{{lang_fullName}} *
-
+
+
+
-
{{lang_telephone}}
-
+
+
+
-
E-Mail
-
+
+
+
+
+
+
+
+ +
- +
diff --git a/modules-available/adduser/templates/page-edituser.html b/modules-available/adduser/templates/page-edituser.html new file mode 100644 index 00000000..b8e51b5c --- /dev/null +++ b/modules-available/adduser/templates/page-edituser.html @@ -0,0 +1,72 @@ +
+ + +

{{lang_editUser}}

+ +
+
+
+
+
+
+
+

+ {{lang_changeLoginHint}} +

+
+
+ +
+
+
+
+
+
+ {{#password_disabled}} +
+
+
+

+ {{lang_changeOwnPasswordHint}} +

+
+
+ {{/password_disabled}} +
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+ +
+
+ + + +
diff --git a/modules-available/adduser/templates/page-userlist.html b/modules-available/adduser/templates/page-userlist.html new file mode 100644 index 00000000..5b14f7e5 --- /dev/null +++ b/modules-available/adduser/templates/page-userlist.html @@ -0,0 +1,73 @@ +

{{lang_userlist}}

+ +{{{pagenav}}} + +
+ + + + + + + + + + + + + + {{#list}} + + + + + + + + {{/list}} + +
{{lang_login}}{{lang_name}}{{lang_phone}}{{lang_email}}
{{login}}{{fullname}}{{phone}}{{email}} + + {{^hide_delete}} + + {{/hide_delete}} +
+
+ + + {{lang_addUser}} + +
+
+ + + \ No newline at end of file diff --git a/modules-available/systemstatus/templates/_page.html b/modules-available/systemstatus/templates/_page.html index 59fffa3a..64830ea3 100644 --- a/modules-available/systemstatus/templates/_page.html +++ b/modules-available/systemstatus/templates/_page.html @@ -92,7 +92,7 @@ {{lang_maintenance}}
-
+ diff --git a/style/default.css b/style/default.css index a9d45681..10cbd5ac 100644 --- a/style/default.css +++ b/style/default.css @@ -11,20 +11,6 @@ body { background-image: url('bg.png'); } -.form-adduser { - max-width: 600px; - padding: 10px; - margin: 0 auto; -} -.form-adduser .form-control { - position: relative; - font-size: 16px; - height: auto; - padding: 10px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} .form-narrow { max-width: 320px; } -- cgit v1.2.3-55-g7522