From 271019b16bfb21e906be11723f7a8cd6da71762e Mon Sep 17 00:00:00 2001 From: wrongecho Date: Sat, 14 Sep 2024 18:29:44 +0100 Subject: [PATCH 1/3] Permissions overhaul - Define permissions in the database 2nd attempt at this one! Similar to #1008 but separately defining the roles, modules and associated permissions in the database. Also has admin being a defined role automatically having full access. Parent issue: #530 --- admin_role_add_modal.php | 58 ++++++++++++++++ admin_role_edit_modal.php | 119 ++++++++++++++++++++++++++++++++ admin_roles.php | 140 ++++++++++++++++++++++++++++++++++++++ check_login.php | 3 + database_updates.php | 50 +++++++++++++- database_version.php | 2 +- db.sql | 24 +++++++ functions.php | 46 +++++++++++++ post/admin.php | 53 ++++++++++++++- setup.php | 27 ++++++-- 10 files changed, 513 insertions(+), 9 deletions(-) create mode 100644 admin_role_add_modal.php create mode 100644 admin_role_edit_modal.php create mode 100644 admin_roles.php diff --git a/admin_role_add_modal.php b/admin_role_add_modal.php new file mode 100644 index 000000000..e860128f6 --- /dev/null +++ b/admin_role_add_modal.php @@ -0,0 +1,58 @@ + diff --git a/admin_role_edit_modal.php b/admin_role_edit_modal.php new file mode 100644 index 000000000..98356760b --- /dev/null +++ b/admin_role_edit_modal.php @@ -0,0 +1,119 @@ + diff --git a/admin_roles.php b/admin_roles.php new file mode 100644 index 000000000..d0b5005df --- /dev/null +++ b/admin_roles.php @@ -0,0 +1,140 @@ + +
Roles are not yet active/enforced - do not use.
+ +
+
+

Roles

+
+
+ +
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+
+ +
+
+
+
+
+
+ + "> + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionAdminUser countAction
+ +
+
+
+ + + +
+
+ +
+
+ +module permissions + mysqli_query($mysqli, "CREATE TABLE `user_role_permissions` ( + `user_role_id` INT(11) NOT NULL, + `module_id` INT(11) NOT NULL, + `user_role_permission_level` INT(11) NOT NULL + )"); + + // Add default permissions for accountant role + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 1, user_role_permission_level = 1"); // Read clients + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 2, user_role_permission_level = 1"); // Read support + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 4, user_role_permission_level = 1"); // Read sales + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 5, user_role_permission_level = 2"); // Modify financial + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 6, user_role_permission_level = 1"); // Read reports + + // Add default permissions for tech role + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 1, user_role_permission_level = 2"); // Modify clients + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 2, user_role_permission_level = 2"); // Modify support + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 3, user_role_permission_level = 2"); // Modify credentials + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 4, user_role_permission_level = 2"); // Modify sales + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.5.0'"); + } + + // if (CURRENT_DATABASE_VERSION == '1.5.0') { + // // Insert queries here required to update to DB version 1.5.1 // // Then, update the database to the next sequential version - // mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.5.0'"); + // mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.5.1'"); // } } else { diff --git a/database_version.php b/database_version.php index 917931520..f81f9fe0d 100644 --- a/database_version.php +++ b/database_version.php @@ -5,4 +5,4 @@ * It is used in conjunction with database_updates.php */ -DEFINE("LATEST_DATABASE_VERSION", "1.4.9"); +DEFINE("LATEST_DATABASE_VERSION", "1.5.0"); diff --git a/db.sql b/db.sql index cd8d9b2b2..9d9d4b14d 100644 --- a/db.sql +++ b/db.sql @@ -889,6 +889,18 @@ CREATE TABLE `logs` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `modules` +-- + +DROP TABLE IF EXISTS `modules`; +CREATE TABLE IF NOT EXISTS `modules` ( + `module_id` int(11) NOT NULL AUTO_INCREMENT, + `module_name` varchar(200) NOT NULL, + `module_description` varchar(200) DEFAULT NULL, + PRIMARY KEY (`module_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; + -- -- Table structure for table `networks` -- @@ -1959,6 +1971,7 @@ CREATE TABLE `user_roles` ( `user_role_id` int(11) NOT NULL AUTO_INCREMENT, `user_role_name` varchar(200) NOT NULL, `user_role_description` varchar(200) DEFAULT NULL, + `user_role_is_admin` int(11) NOT NULL DEFAULT 0, `user_role_created_at` datetime NOT NULL DEFAULT current_timestamp(), `user_role_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(), `user_role_archived_at` datetime DEFAULT NULL, @@ -1966,6 +1979,17 @@ CREATE TABLE `user_roles` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `user_role_permissions` +-- + +DROP TABLE IF EXISTS `user_role_permissions`; +CREATE TABLE IF NOT EXISTS `user_role_permissions` ( + `user_role_id` int(11) NOT NULL, + `module_id` int(11) NOT NULL, + `user_role_permission_level` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; + -- -- Table structure for table `user_settings` -- diff --git a/functions.php b/functions.php index 1202e192a..108b80183 100644 --- a/functions.php +++ b/functions.php @@ -1341,3 +1341,49 @@ function validateWhitelabelKey($key) return false; } + +// When provided a module name (e.g. module_support), returns the associated permission level (false=none, 1=read, 2=write, 3=full) +function lookupUserPermission($module) { + global $mysqli, $session_is_admin, $session_user_role; + + if (isset($session_is_admin) && $session_is_admin === true) { + return 3; + } + + $module = sanitizeInput($module); + + $sql = mysqli_query( + $mysqli, + "SELECT + urp.user_role_permission_level + FROM + modules AS m + JOIN + user_role_permissions AS urp + ON + m.module_id = urp.module_id + WHERE + m.module_name = '$module' AND urp.user_role_id = $session_user_role" + ); + + $row = mysqli_fetch_array($sql); + + if (isset($row['user_role_permission_level'])) { + return intval($row['user_role_permission_level']); + } + + // Default return for no module permission + return false; +} + +// Ensures a user has access to a module (e.g. module_support) with at least the required permission level provided (defaults to read) +function enforceUserPermission($module, $check_access_level = 1) { + $permitted_access_level = lookupUserPermission($module); + + if (!$permitted_access_level || $permitted_access_level < $check_access_level) { + $_SESSION['alert_type'] = "danger"; + $_SESSION['alert_message'] = WORDING_ROLECHECK_FAILED; + header("Location: " . $_SERVER["HTTP_REFERER"]); + exit(WORDING_ROLECHECK_FAILED); + } +} diff --git a/post/admin.php b/post/admin.php index afd759ffa..18e449c27 100644 --- a/post/admin.php +++ b/post/admin.php @@ -342,4 +342,55 @@ header("Location: " . $_SERVER["HTTP_REFERER"]); -} \ No newline at end of file +} + +if (isset($_POST['add_role'])) { + validateCSRFToken($_POST['csrf_token']); + validateAdminRole(); + + $name = sanitizeInput($_POST['role_name']); + $description = sanitizeInput($_POST['role_description']); + $admin = intval($_POST['role_is_admin']); + + mysqli_query($mysqli, "INSERT INTO user_roles SET user_role_name = '$name', user_role_description = '$description', user_role_is_admin = $admin"); + + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Role', log_action = 'Create', log_description = '$session_name created the $name role', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + + $_SESSION['alert_message'] = "Role $name created"; + + header("Location: " . $_SERVER["HTTP_REFERER"]); + +} + +if (isset($_POST['edit_role'])) { + validateCSRFToken($_POST['csrf_token']); + validateAdminRole(); + + // Update role metadata + $role_id = sanitizeInput($_POST['role_id']); + $name = sanitizeInput($_POST['role_name']); + $description = sanitizeInput($_POST['role_description']); + $admin = intval($_POST['role_is_admin']); + mysqli_query($mysqli, "UPDATE user_roles SET user_role_name = '$name', user_role_description = '$description', user_role_is_admin = $admin WHERE user_role_id = $role_id"); + + // Update role access levels + mysqli_query($mysqli, "DELETE FROM user_role_permissions WHERE user_role_id = $role_id"); + foreach ($_POST as $key => $value) { + if (str_contains($key, '##module_')){ + $module_id = intval(explode('##', $key)[0]); + $access_level = intval($value); + + if ($access_level > 0) { + echo $key . ' with id ' . $module_id . " : ". $access_level . "\n"; + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = $role_id, module_id = $module_id, user_role_permission_level = $access_level"); + } + } + + } + + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Role', log_action = 'Modify', log_description = '$session_name updated the $name role', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + + $_SESSION['alert_message'] = "Role $name updated"; + + header("Location: " . $_SERVER["HTTP_REFERER"]); +} diff --git a/setup.php b/setup.php index 94cbdd0cb..71fb6e0f5 100644 --- a/setup.php +++ b/setup.php @@ -300,13 +300,32 @@ mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'New', ticket_status_color = '#dc3545'"); // Default ID for new tickets is 1 mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'Open', ticket_status_color = '#007bff'"); // 2 mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'On Hold', ticket_status_color = '#28a745'"); // 3 - mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'Resolved', ticket_status_color = '#343a40'"); // 4 - was auto-close, now resolved + mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'Resolved', ticket_status_color = '#343a40'"); // 4 (was auto-close) mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'Closed', ticket_status_color = '#343a40'"); // 5 + // Add default modules + mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_client', module_description = 'General client & contact management'"); + mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_support', module_description = 'Access to ticketing, assets and documentation'"); + mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_credential', module_description = 'Access to client credentials - usernames, passwords and 2FA codes'"); + mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_sales', module_description = 'Access to quotes, invoices and products'"); + mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_financial', module_description = 'Access to payments, accounts, expenses and budgets'"); + mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_reporting', module_description = 'Access to all reports'"); + // Add default roles - mysqli_query($mysqli, "INSERT INTO `user_roles` SET user_role_id = 1, user_role_name = 'Accountant', user_role_description = 'Built-in - Limited access to financial-focused modules'"); - mysqli_query($mysqli, "INSERT INTO `user_roles` SET user_role_id = 2, user_role_name = 'Technician', user_role_description = 'Built-in - Limited access to technical-focused modules'"); - mysqli_query($mysqli, "INSERT INTO `user_roles` SET user_role_id = 3, user_role_name = 'Administrator', user_role_description = 'Built-in - Full administrative access to all modules (including user management)'"); + mysqli_query($mysqli, "INSERT INTO user_roles SET user_role_id = 1, user_role_name = 'Accountant', user_role_description = 'Built-in - Limited access to financial-focused modules'"); + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 1, user_role_permission_level = 1"); // Read clients + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 2, user_role_permission_level = 1"); // Read support + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 4, user_role_permission_level = 1"); // Read sales + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 5, user_role_permission_level = 2"); // Modify financial + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 6, user_role_permission_level = 1"); // Read reports + + mysqli_query($mysqli, "INSERT INTO user_roles SET user_role_id = 2, user_role_name = 'Technician', user_role_description = 'Built-in - Limited access to technical-focused modules'"); + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 1, user_role_permission_level = 2"); // Modify clients + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 2, user_role_permission_level = 2"); // Modify support + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 3, user_role_permission_level = 2"); // Modify credentials + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 4, user_role_permission_level = 2"); // Modify sales + + mysqli_query($mysqli, "INSERT INTO user_roles SET user_role_id = 3, user_role_name = 'Administrator', user_role_description = 'Built-in - Full administrative access to all modules (including user management)', user_role_is_admin = 1"); $_SESSION['alert_message'] = "Company $name created!"; From c7340ca0d43e7fc22a14ee012db9d8fb607595dc Mon Sep 17 00:00:00 2001 From: wrongecho Date: Sat, 14 Sep 2024 18:55:00 +0100 Subject: [PATCH 2/3] Permissions overhaul - Define permissions in the database 2nd attempt at this one! Similar to #1008 but separately defining the roles, modules and associated permissions in the database. Also has admin being a defined role automatically having full access. Parent issue: #530 --- admin_roles.php | 5 ----- functions.php | 8 ++++---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/admin_roles.php b/admin_roles.php index d0b5005df..929130318 100644 --- a/admin_roles.php +++ b/admin_roles.php @@ -45,11 +45,6 @@ -
-
- -
-

diff --git a/functions.php b/functions.php index 108b80183..0711b222e 100644 --- a/functions.php +++ b/functions.php @@ -1356,13 +1356,13 @@ function lookupUserPermission($module) { $mysqli, "SELECT urp.user_role_permission_level - FROM + FROM modules AS m - JOIN + JOIN user_role_permissions AS urp - ON + ON m.module_id = urp.module_id - WHERE + WHERE m.module_name = '$module' AND urp.user_role_id = $session_user_role" ); From ee9a2789e91135f6d9a845fc9c35be6b3e7a4f1e Mon Sep 17 00:00:00 2001 From: wrongecho Date: Sat, 14 Sep 2024 19:14:35 +0100 Subject: [PATCH 3/3] Permissions overhaul - Define permissions in the database 2nd attempt at this one! Similar to #1008 but separately defining the roles, modules and associated permissions in the database. Also has admin being a defined role automatically having full access. Parent issue: #530 --- admin_roles.php | 4 ++-- functions.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/admin_roles.php b/admin_roles.php index 929130318..6a0e3f851 100644 --- a/admin_roles.php +++ b/admin_roles.php @@ -97,8 +97,8 @@ - - + + diff --git a/functions.php b/functions.php index 0711b222e..e26aaf348 100644 --- a/functions.php +++ b/functions.php @@ -1354,7 +1354,7 @@ function lookupUserPermission($module) { $sql = mysqli_query( $mysqli, - "SELECT + "SELECT urp.user_role_permission_level FROM modules AS m