$canManageUsers = perms_check($userPerms, MSZ_PERM_USER_MANAGE_USERS), 'can_manage_roles' => $canManageRoles = perms_check($userPerms, MSZ_PERM_USER_MANAGE_ROLES), 'can_manage_perms' => $canManagePerms = perms_check($userPerms, MSZ_PERM_USER_MANAGE_PERMS), 'can_manage_warns' => $canManageWarnings = perms_check($userPerms, MSZ_PERM_USER_MANAGE_WARNINGS), ]); switch ($_GET['v'] ?? null) { default: case 'listing': if (!$canManageUsers && !$canManagePerms) { echo render_error(403); break; } $manageUsersCount = db_query(' SELECT COUNT(`user_id`) FROM `msz_users` ')->fetchColumn(); $usersPagination = pagination_create($manageUsersCount, 30); $usersOffset = pagination_offset($usersPagination, pagination_param()); if (!pagination_is_valid_offset($usersOffset)) { echo render_error(404); break; } $getManageUsers = db_prepare(' SELECT u.`user_id`, u.`username`, u.`user_country`, r.`role_id`, u.`user_created`, u.`user_active`, u.`user_deleted`, INET6_NTOA(u.`register_ip`) AS `register_ip`, INET6_NTOA(u.`last_ip`) AS `last_ip`, COALESCE(u.`user_title`, r.`role_title`, r.`role_name`) AS `user_title`, COALESCE(u.`user_colour`, r.`role_colour`) AS `user_colour` FROM `msz_users` AS u LEFT JOIN `msz_roles` AS r ON u.`display_role` = r.`role_id` ORDER BY `user_id` LIMIT :offset, :take '); $getManageUsers->bindValue('offset', $usersOffset); $getManageUsers->bindValue('take', $usersPagination['range']); $manageUsers = db_fetch_all($getManageUsers); tpl_vars([ 'manage_users' => $manageUsers, 'manage_users_pagination' => $usersPagination, ]); echo tpl_render('manage.users.users'); break; case 'view': if (!$canManageUsers && !$canManagePerms) { echo render_error(403); break; } $userId = (int)($_GET['u'] ?? 0); if ($userId < 1) { echo render_error(404); break; } $getHasRoles = db_prepare(' SELECT `role_id`, `role_name`, `role_hierarchy` FROM `msz_roles` WHERE `role_id` IN ( SELECT `role_id` FROM `msz_user_roles` WHERE `user_id` = :user_id ) '); $getHasRoles->bindValue('user_id', $userId); $hasRoles = db_fetch_all($getHasRoles); $getAvailableRoles = db_prepare(' SELECT `role_id`, `role_name`, `role_hierarchy` FROM `msz_roles` WHERE `role_id` NOT IN ( SELECT `role_id` FROM `msz_user_roles` WHERE `user_id` = :user_id ) '); $getAvailableRoles->bindValue('user_id', $userId); $availableRoles = db_fetch_all($getAvailableRoles); if ($canManagePerms) { tpl_var('permissions', $permissions = manage_perms_list(perms_get_user_raw($userId))); } $notices = []; if ($isPostRequest) { if (!csrf_verify('users_edit', $_POST['csrf'] ?? '')) { $notices[] = "Couldn't verify the request."; } elseif (!user_check_authority(user_session_current('user_id'), $userId)) { $notices[] = 'You are not allowed to administer this user.'; } else { $setUserInfo = []; if (!empty($_POST['user']) && is_array($_POST['user'])) { $setUserInfo['username'] = (string)($_POST['user']['username'] ?? ''); $setUserInfo['email'] = (string)($_POST['user']['email'] ?? ''); $setUserInfo['user_country'] = (string)($_POST['user']['country'] ?? ''); $setUserInfo['user_title'] = (string)($_POST['user']['title'] ?? ''); $usernameValidation = user_validate_username($setUserInfo['username']); $emailValidation = user_validate_email($setUserInfo['email']); $countryValidation = strlen($setUserInfo['user_country']) === 2 && ctype_alpha($setUserInfo['user_country']) && ctype_upper($setUserInfo['user_country']); if (!empty($usernameValidation)) { $notices[] = MSZ_USER_USERNAME_VALIDATION_STRINGS[$usernameValidation]; } if (!empty($emailValidation)) { $notices[] = $emailValidation === 'in-use' ? 'This e-mail address has already been used!' : 'This e-mail address is invalid!'; } else { $setUserInfo['email'] = mb_strtolower($setUserInfo['email']); } if (!$countryValidation) { $notices[] = 'Country code was invalid.'; } if (strlen($setUserInfo['user_title']) < 1) { $setUserInfo['user_title'] = null; } elseif (strlen($setUserInfo['user_title']) > 64) { $notices[] = 'User title was invalid.'; } } if (!empty($_POST['colour']) && is_array($_POST['colour'])) { $userColour = null; if (!empty($_POST['colour']['enable'])) { $userColour = colour_create(); if (!colour_from_hex($userColour, (string)($_POST['colour']['hex'] ?? ''))) { $notices[] = 'An invalid colour was supplied.'; } } $setUserInfo['user_colour'] = $userColour; } if (!empty($_POST['password']) && is_array($_POST['password'])) { $passwordNewValue = (string)($_POST['password']['new'] ?? ''); $passwordConfirmValue = (string)($_POST['password']['confirm'] ?? ''); if (!empty($passwordNewValue)) { if ($passwordNewValue !== $passwordConfirmValue) { $notices[] = 'Confirm password does not match.'; } elseif (!empty(user_validate_password($passwordNewValue))) { $notices[] = 'New password is too weak.'; } else { $setUserInfo['password'] = user_password_hash($passwordNewValue); } } } if (empty($notices) && !empty($setUserInfo)) { $userUpdate = db_prepare(sprintf( ' UPDATE `msz_users` SET %s WHERE `user_id` = :set_user_id ', pdo_prepare_array_update($setUserInfo, true) )); $userUpdate->bindValue('set_user_id', $userId); foreach ($setUserInfo as $key => $value) { $userUpdate->bindValue($key, $value); } if (!$userUpdate->execute()) { $notices[] = 'Something went wrong while updating the user.'; } } if (!empty($permissions) && !empty($_POST['perms']) && is_array($_POST['perms'])) { $perms = manage_perms_apply($permissions, $_POST['perms']); if ($perms !== null) { if (!perms_set_user_raw($userId, $perms)) { $notices[] = 'Failed to update permissions.'; } } else { if (!perms_delete_user($userId)) { $notices[] = 'Failed to remove permissions.'; } } } if (isset($_POST['add_role']) && user_role_check_authority(user_session_current('user_id'), (int)$_POST['add_role']['role'])) { user_role_add($userId, $_POST['add_role']['role']); } if (isset($_POST['manage_roles'])) { switch ($_POST['manage_roles']['mode'] ?? '') { case 'display': user_role_set_display($userId, $_POST['manage_roles']['role']); break; case 'remove': if ((int)$_POST['manage_roles']['role'] !== MSZ_ROLE_MAIN && user_role_check_authority(user_session_current('user_id'), (int)$_POST['manage_roles']['role'])) { user_role_remove($userId, $_POST['manage_roles']['role']); } break; } } } } $getUser = db_prepare(' SELECT u.*, INET6_NTOA(u.`register_ip`) as `register_ip_decoded`, INET6_NTOA(u.`last_ip`) as `last_ip_decoded`, COALESCE(u.`user_colour`, r.`role_colour`) as `colour` FROM `msz_users` as u LEFT JOIN `msz_roles` as r ON u.`display_role` = r.`role_id` WHERE `user_id` = :user_id ORDER BY `user_id` '); $getUser->bindValue('user_id', $userId); $manageUser = db_fetch($getUser); if (empty($manageUser)) { echo render_error(404); break; } tpl_vars([ 'available_roles' => $availableRoles, 'has_roles' => $hasRoles, 'manage_user' => $manageUser, 'profile_fields' => user_profile_fields_get(), 'manage_notices' => $notices, ]); echo tpl_render('manage.users.user'); break; case 'roles': if (!$canManageRoles && !$canManagePerms) { echo render_error(403); break; } $manageRolesCount = db_query(' SELECT COUNT(`role_id`) FROM `msz_roles` ')->fetchColumn(); $rolesPagination = pagination_create($manageRolesCount, 10); $rolesOffset = pagination_offset($rolesPagination, pagination_param()); if (!pagination_is_valid_offset($rolesOffset)) { echo render_error(404); break; } $getManageRoles = db_prepare(' SELECT `role_id`, `role_colour`, `role_name`, `role_title`, ( SELECT COUNT(`user_id`) FROM `msz_user_roles` as ur WHERE ur.`role_id` = r.`role_id` ) as `users` FROM `msz_roles` as r LIMIT :offset, :take '); $getManageRoles->bindValue('offset', $rolesOffset); $getManageRoles->bindValue('take', $rolesPagination['range']); $manageRoles = db_fetch_all($getManageRoles); echo tpl_render('manage.users.roles', [ 'manage_roles' => $manageRoles, 'manage_roles_pagination' => $rolesPagination, ]); break; case 'role': if (!$canManageRoles && !$canManagePerms) { echo render_error(403); break; } $roleId = $_GET['r'] ?? null; if ($canManagePerms) { tpl_var('permissions', $permissions = manage_perms_list(perms_get_role_raw($roleId ?? 0))); } if ($isPostRequest) { if (!csrf_verify('users_role', $_POST['csrf'] ?? '')) { echo 'csrf err'; break; } $roleHierarchy = (int)($_POST['role']['hierarchy'] ?? -1); if ($roleId === null ? (user_get_hierarchy(user_session_current('user_id')) <= $roleHierarchy) : !user_role_check_authority(user_session_current('user_id'), $roleId)) { echo 'Your hierarchy is too low to do this.'; break; } if (!isset($_POST['role'])) { echo 'no'; break; } $roleName = $_POST['role']['name'] ?? ''; $roleNameLength = strlen($roleName); if ($roleNameLength < 1 || $roleNameLength > 255) { echo 'invalid name length'; break; } $roleSecret = !empty($_POST['role']['secret']); if ($roleHierarchy < 1 || $roleHierarchy > 100) { echo 'Invalid hierarchy value.'; break; } $roleColour = colour_create(); if (!empty($_POST['role']['colour']['inherit'])) { colour_set_inherit($roleColour); } else { foreach (['red', 'green', 'blue'] as $key) { $value = (int)($_POST['role']['colour'][$key] ?? -1); $func = 'colour_set_' . ucfirst($key); if ($value < 0 || $value > 0xFF) { echo 'invalid colour value'; break 2; } $func($roleColour, $value); } } $roleDescription = $_POST['role']['description'] ?? null; $roleTitle = $_POST['role']['title'] ?? null; if ($roleDescription !== null) { $rdLength = strlen($roleDescription); if ($rdLength < 1) { $roleDescription = null; } elseif ($rdLength > 1000) { echo 'description is too long'; break; } } if ($roleTitle !== null) { $rtLength = strlen($roleTitle); if ($rtLength < 1) { $roleTitle = null; } elseif ($rtLength > 64) { echo 'title is too long'; break; } } if ($roleId < 1) { $updateRole = db_prepare(' INSERT INTO `msz_roles` ( `role_name`, `role_hierarchy`, `role_hidden`, `role_colour`, `role_description`, `role_title` ) VALUES ( :role_name, :role_hierarchy, :role_hidden, :role_colour, :role_description, :role_title ) '); } else { $updateRole = db_prepare(' UPDATE `msz_roles` SET `role_name` = :role_name, `role_hierarchy` = :role_hierarchy, `role_hidden` = :role_hidden, `role_colour` = :role_colour, `role_description` = :role_description, `role_title` = :role_title WHERE `role_id` = :role_id '); $updateRole->bindValue('role_id', $roleId); } $updateRole->bindValue('role_name', $roleName); $updateRole->bindValue('role_hierarchy', $roleHierarchy); $updateRole->bindValue('role_hidden', $roleSecret ? 1 : 0); $updateRole->bindValue('role_colour', $roleColour); $updateRole->bindValue('role_description', $roleDescription); $updateRole->bindValue('role_title', $roleTitle); $updateRole->execute(); if ($roleId < 1) { $roleId = (int)db_last_insert_id(); } if (!empty($permissions) && !empty($_POST['perms']) && is_array($_POST['perms'])) { $perms = manage_perms_apply($permissions, $_POST['perms']); if ($perms !== null) { $permKeys = array_keys($perms); $setPermissions = db_prepare(' REPLACE INTO `msz_permissions` (`role_id`, `user_id`, `' . implode('`, `', $permKeys) . '`) VALUES (:role_id, NULL, :' . implode(', :', $permKeys) . ') '); $setPermissions->bindValue('role_id', $roleId); foreach ($perms as $key => $value) { $setPermissions->bindValue($key, $value); } $setPermissions->execute(); } else { $deletePermissions = db_prepare(' DELETE FROM `msz_permissions` WHERE `role_id` = :role_id AND `user_id` IS NULL '); $deletePermissions->bindValue('role_id', $roleId); $deletePermissions->execute(); } } header("Location: ?v=role&r={$roleId}"); break; } if ($roleId !== null) { if ($roleId < 1) { echo 'no'; break; } $getEditRole = db_prepare(' SELECT * FROM `msz_roles` WHERE `role_id` = :role_id '); $getEditRole->bindValue('role_id', $roleId); $editRole = db_fetch($getEditRole); if (empty($editRole)) { echo 'invalid role'; break; } tpl_vars(['edit_role' => $editRole]); } echo tpl_render('manage.users.role'); break; case 'warnings': if (!$canManageWarnings) { echo render_error(403); break; } $notices = []; if (!empty($_POST['warning']) && is_array($_POST['warning'])) { $warningType = (int)($_POST['warning']['type'] ?? 0); if (user_warning_type_is_valid($warningType)) { $warningDuration = 0; if (user_warning_has_duration($warningType)) { $duration = (int)($_POST['warning']['duration'] ?? 0); if ($duration > 0) { $warningDuration = time() + $duration; } elseif ($duration < 0) { $customDuration = $_POST['warning']['duration_custom'] ?? ''; if (!empty($customDuration)) { switch ($duration) { case -1: // YYYY-MM-DD $splitDate = array_apply(explode('-', $customDuration, 3), function ($a) { return (int)$a; }); if (checkdate($splitDate[1], $splitDate[2], $splitDate[0])) { $warningDuration = mktime(0, 0, 0, $splitDate[1], $splitDate[2], $splitDate[0]); } break; case -2: // Raw seconds $warningDuration = time() + (int)$customDuration; break; case -3: // strtotime $warningDuration = strtotime($customDuration); break; } } } if ($warningDuration <= time()) { $notices[] = 'The duration supplied was invalid.'; } } $warningsUser = (int)($_POST['warning']['user'] ?? 0); if (!user_check_authority(user_session_current('user_id'), $warningsUser)) { $notices[] = 'You do not have authority over this user.'; } if (empty($notices) && $warningsUser > 0) { $warningId = user_warning_add( $warningsUser, user_get_last_ip($warningsUser), user_session_current('user_id'), ip_remote_address(), $warningType, $_POST['warning']['note'], $_POST['warning']['private'], $warningDuration ); } if (!empty($warningId) && $warningId < 0) { switch ($warningId) { case MSZ_E_WARNING_ADD_DB: $notices[] = 'Failed to record the warning in the database.'; break; case MSZ_E_WARNING_ADD_TYPE: $notices[] = 'The warning type provided was invalid.'; break; case MSZ_E_WARNING_ADD_USER: $notices[] = 'The User ID provided was invalid.'; break; case MSZ_E_WARNING_ADD_DURATION: $notices[] = 'The duration specified was invalid.'; break; } } } } elseif (!empty($_POST['lookup']) && is_string($_POST['lookup'])) { $userId = user_id_from_username($_POST['lookup']); header("Location: ?v=warnings&u={$userId}"); return; } elseif (!empty($_GET['m'])) { $warningId = (int)($_GET['w'] ?? 0); $modeName = $_GET['m'] ?? ''; $csrfRealm = "warning-{$modeName}[{$warningId}]"; if (csrf_verify($csrfRealm, $_GET['c'] ?? '')) { switch ($modeName) { case 'delete': user_warning_remove($warningId); break; } header('Location: ' . ($_SERVER['HTTP_REFERER'] ?? '?m=warnings' . (empty($_GET['u']) ? '' : '&u=' . (int)($_GET['u'])))); return; } } if (empty($warningsUser)) { $warningsUser = max(0, (int)($_GET['u'] ?? 0)); } $warningsPagination = pagination_create(user_warning_global_count($warningsUser), 50); $warningsOffset = $warningsPagination['count'] > 0 ? pagination_offset($warningsPagination, pagination_param()) : 0; if (!pagination_is_valid_offset($warningsOffset)) { echo render_error(404); break; } $warningsList = user_warning_global_fetch($warningsOffset, $warningsPagination['range'], $warningsUser); // calling array_flip since the input_select macro wants value => display, but this looks cuter $warningDurations = array_flip([ 'Pick a duration...' => 0, '5 Minutes' => 60 * 5, '15 Minutes' => 60 * 15, '30 Minutes' => 60 * 30, '45 Minutes' => 60 * 45, '1 Hour' => 60 * 60, '2 Hours' => 60 * 60 * 2, '3 Hours' => 60 * 60 * 3, '6 Hours' => 60 * 60 * 6, '12 Hours' => 60 * 60 * 12, '1 Day' => 60 * 60 * 24, '2 Days' => 60 * 60 * 24 * 2, '1 Week' => 60 * 60 * 24 * 7, '2 Weeks' => 60 * 60 * 24 * 7 * 2, '1 Month' => 60 * 60 * 24 * 365 / 12, '3 Months' => 60 * 60 * 24 * 365 / 12 * 3, '6 Months' => 60 * 60 * 24 * 365 / 12 * 6, '9 Months' => 60 * 60 * 24 * 365 / 12 * 9, '1 Year' => 60 * 60 * 24 * 365, 'Until (YYYY-MM-DD) ->' => -1, 'Until (Seconds) ->' => -2, 'Until (strtotime) ->' => -3, ]); echo tpl_render('manage.users.warnings', [ 'warnings' => [ 'notices' => $notices, 'pagination' => $warningsPagination, 'list' => $warningsList, 'user_id' => $warningsUser, 'username' => user_username_from_id($warningsUser), 'types' => user_warning_get_types(), 'durations' => $warningDurations, ], ]); break; }