diff --git a/core/users/user_profile.php b/core/users/user_profile.php new file mode 100644 index 0000000000..4bddc6f22d --- /dev/null +++ b/core/users/user_profile.php @@ -0,0 +1,745 @@ + + Portions created by the Initial Developer are Copyright (C) 2008-2025 + the Initial Developer. All Rights Reserved. + + Contributor(s): + Mark J Crane +*/ + +//includes files + require_once dirname(__DIR__, 2) . "/resources/require.php"; + require_once "resources/check_auth.php"; + +//add multi-lingual support + $language = new text; + $text = $language->get(); + +//get the user uuid + $user_uuid = $_SESSION['user_uuid']; + +//retrieve password requirements + if (permission_exists('user_password')) { + $required['length'] = $settings->get('users', 'password_length', 12); + $required['number'] = $settings->get('users', 'password_number', false); + $required['lowercase'] = $settings->get('users', 'password_lowercase', false); + $required['uppercase'] = $settings->get('users', 'password_uppercase', false); + $required['special'] = $settings->get('users', 'password_special', false); + } + +//process the http post + if (!empty($_POST)) { + + //get the HTTP values and set as variables + $password = $_POST["password"]; + $password_confirm = $_POST["password_confirm"]; + $user_email = $_POST["user_email"]; + $user_status = $_POST["user_status"] ?? ''; + $user_language = $_POST["user_language"]; + $user_time_zone = $_POST["user_time_zone"]; + //if (permission_exists('api_key')) { + // $api_key = $_POST["api_key"]; + //} + if (!empty($_SESSION['authentication']['methods']) && in_array('totp', $_SESSION['authentication']['methods'])) { + $user_totp_secret = strtoupper($_POST["user_totp_secret"]); + } + + //validate the token + $token = new token; + if (!$token->validate($_SERVER['PHP_SELF'])) { + message::add($text['message-invalid_token'],'negative'); + header('Location: users.php'); + exit; + } + + //validate the user status + switch ($user_status) { + case "Available" : + break; + case "Available (On Demand)" : + break; + case "On Break" : + break; + case "Do Not Disturb" : + break; + case "Logged Out" : + break; + default : + $user_status = ''; + } + + //check required values + //require the passwords to match + if (!empty($password) && $password != $password_confirm) { + message::add($text['message-password_mismatch'], 'negative', 7500); + } + + //require passwords not allowed to be empty + if (permission_exists('user_password') && permission_exists('user_add') && $action == 'add') { + if (empty($password)) { + message::add($text['message-password_blank'], 'negative', 7500); + } + if (empty($group_uuid_name)) { + $invalid[] = $text['label-group']; + } + } + + //require a value a valid email address format + if (!valid_email($user_email)) { + $invalid[] = $text['label-email']; + } + + //require passwords with the defined required attributes: length, number, lower case, upper case, and special characters + if (permission_exists('user_password') && !empty($password)) { + if (!empty($required['length']) && is_numeric($required['length']) && $required['length'] != 0) { + if (strlen($password) < $required['length']) { + $invalid[] = $text['label-characters']; + } + } + if ($required['number']) { + if (!preg_match('/(?=.*[\d])/', $password)) { + $invalid[] = $text['label-numbers']; + } + } + if ($required['lowercase']) { + if (!preg_match('/(?=.*[a-z])/', $password)) { + $invalid[] = $text['label-lowercase_letters']; + } + } + if ($required['uppercase']) { + if (!preg_match('/(?=.*[A-Z])/', $password)) { + $invalid[] = $text['label-uppercase_letters']; + } + } + if ($required['special']) { + if (!preg_match('/(?=.*[\W])/', $password)) { + $invalid[] = $text['label-special_characters']; + } + } + } + + //return if error + if (message::count() != 0 || !empty($invalid)) { + if ($invalid) { message::add($text['message-required'].implode(', ', $invalid), 'negative', 7500); } + persistent_form_values('store', $_POST); + header("Location: user_profile.php"); + exit; + } + else { + persistent_form_values('clear'); + } + + //save the data + $i = $n = $x = $c = 0; //set initial array indexes + + //check to see if user language is set + $sql = "select user_setting_uuid, user_setting_value from v_user_settings "; + $sql .= "where user_setting_category = 'domain' "; + $sql .= "and user_setting_subcategory = 'language' "; + $sql .= "and user_uuid = :user_uuid "; + $parameters['user_uuid'] = $user_uuid; + $row = $database->select($sql, $parameters, 'row'); + if (!empty($user_language) && (empty($row) || (!empty($row['user_setting_uuid']) && !is_uuid($row['user_setting_uuid'])))) { + //add user setting to array for insert + $array['user_settings'][$i]['user_setting_uuid'] = uuid(); + $array['user_settings'][$i]['user_uuid'] = $user_uuid; + $array['user_settings'][$i]['domain_uuid'] = $domain_uuid; + $array['user_settings'][$i]['user_setting_category'] = 'domain'; + $array['user_settings'][$i]['user_setting_subcategory'] = 'language'; + $array['user_settings'][$i]['user_setting_name'] = 'code'; + $array['user_settings'][$i]['user_setting_value'] = $user_language; + $array['user_settings'][$i]['user_setting_enabled'] = 'true'; + $i++; + } + else { + if (empty($row['user_setting_value']) || empty($user_language)) { + $array_delete['user_settings'][0]['user_setting_category'] = 'domain'; + $array_delete['user_settings'][0]['user_setting_subcategory'] = 'language'; + $array_delete['user_settings'][0]['user_uuid'] = $user_uuid; + + $p = permissions::new(); + $p->add('user_setting_delete', 'temp'); + + $database->delete($array_delete); + unset($array_delete); + + $p->delete('user_setting_delete', 'temp'); + } + if (!empty($user_language)) { + //add user setting to array for update + $array['user_settings'][$i]['user_setting_uuid'] = $row['user_setting_uuid']; + $array['user_settings'][$i]['user_uuid'] = $user_uuid; + $array['user_settings'][$i]['domain_uuid'] = $domain_uuid; + $array['user_settings'][$i]['user_setting_category'] = 'domain'; + $array['user_settings'][$i]['user_setting_subcategory'] = 'language'; + $array['user_settings'][$i]['user_setting_name'] = 'code'; + $array['user_settings'][$i]['user_setting_value'] = $user_language; + $array['user_settings'][$i]['user_setting_enabled'] = 'true'; + $i++; + } + } + unset($sql, $parameters, $row); + + //check to see if user time zone is set + $sql = "select user_setting_uuid, user_setting_value from v_user_settings "; + $sql .= "where user_setting_category = 'domain' "; + $sql .= "and user_setting_subcategory = 'time_zone' "; + $sql .= "and user_uuid = :user_uuid "; + $parameters['user_uuid'] = $user_uuid; + $row = $database->select($sql, $parameters, 'row'); + if (!empty($user_time_zone) && (empty($row) || (!empty($row['user_setting_uuid']) && !is_uuid($row['user_setting_uuid'])))) { + //add user setting to array for insert + $array['user_settings'][$i]['user_setting_uuid'] = uuid(); + $array['user_settings'][$i]['user_uuid'] = $user_uuid; + $array['user_settings'][$i]['domain_uuid'] = $domain_uuid; + $array['user_settings'][$i]['user_setting_category'] = 'domain'; + $array['user_settings'][$i]['user_setting_subcategory'] = 'time_zone'; + $array['user_settings'][$i]['user_setting_name'] = 'name'; + $array['user_settings'][$i]['user_setting_value'] = $user_time_zone; + $array['user_settings'][$i]['user_setting_enabled'] = 'true'; + $i++; + } + else { + if (empty($row['user_setting_value']) || empty($user_time_zone)) { + $array_delete['user_settings'][0]['user_setting_category'] = 'domain'; + $array_delete['user_settings'][0]['user_setting_subcategory'] = 'time_zone'; + $array_delete['user_settings'][0]['user_uuid'] = $user_uuid; + + $p = permissions::new(); + $p->add('user_setting_delete', 'temp'); + + $database->delete($array_delete); + unset($array_delete); + + $p->delete('user_setting_delete', 'temp'); + } + if (!empty($user_time_zone)) { + //add user setting to array for update + $array['user_settings'][$i]['user_setting_uuid'] = $row['user_setting_uuid']; + $array['user_settings'][$i]['user_uuid'] = $user_uuid; + $array['user_settings'][$i]['domain_uuid'] = $domain_uuid; + $array['user_settings'][$i]['user_setting_category'] = 'domain'; + $array['user_settings'][$i]['user_setting_subcategory'] = 'time_zone'; + $array['user_settings'][$i]['user_setting_name'] = 'name'; + $array['user_settings'][$i]['user_setting_value'] = $user_time_zone; + $array['user_settings'][$i]['user_setting_enabled'] = 'true'; + $i++; + } + } + unset($sql, $parameters, $row); + + //set the password hash cost + $options = array('cost' => 10); + + //add user setting to array for update + $array['users'][$x]['user_uuid'] = $user_uuid; + + if (permission_exists('user_password') && !empty($password) && $password == $password_confirm) { + //remove the session id files + $sql = "select session_id from v_user_logs "; + $sql .= "where user_uuid = :user_uuid "; + $sql .= "and timestamp > NOW() - INTERVAL '4 hours' "; + $parameters['user_uuid'] = $user_uuid; + $user_logs = $database->select($sql, $parameters, 'all'); + foreach ($user_logs as $row) { + if (preg_match('/^[a-zA-Z0-9,-]+$/', $row['session_id']) && file_exists(session_save_path() . "/sess_" . $row['session_id'])) { + unlink(session_save_path() . "/sess_" . $row['session_id']); + } + } + + //create a one way hash for the user password + $array['users'][$x]['password'] = password_hash($password, PASSWORD_DEFAULT, $options); + $array['users'][$x]['salt'] = null; + } + $array['users'][$x]['user_email'] = $user_email; + $array['users'][$x]['user_status'] = $user_status; + if (permission_exists('user_add') || permission_exists('user_edit')) { + if (!empty($_SESSION['authentication']['methods']) && in_array('totp', $_SESSION['authentication']['methods'])) { + $array['users'][$x]['user_totp_secret'] = $user_totp_secret; + } + if ($action == 'add') { + $array['users'][$x]['add_user'] = $_SESSION["user"]["username"]; + $array['users'][$x]['add_date'] = date("Y-m-d H:i:s.uO"); + } + } + $x++; + + //add the user_edit permission + $p = permissions::new(); + $p->add("user_setting_add", "temp"); + $p->add("user_setting_edit", "temp"); + $p->add("user_edit", "temp"); + $p->add('user_group_add', 'temp'); + + //save the data + $database->save($array); + //$message = $database->message; + + //remove the temporary permission + $p->delete("user_setting_add", "temp"); + $p->delete("user_setting_edit", "temp"); + $p->delete("user_edit", "temp"); + $p->delete('user_group_add', 'temp'); + + //if call center installed + if ($action == 'edit' && permission_exists('user_edit') && file_exists($_SERVER["PROJECT_ROOT"]."/app/call_centers/app_config.php")) { + //get the call center agent uuid + $sql = "select call_center_agent_uuid from v_call_center_agents "; + $sql .= "where domain_uuid = :domain_uuid "; + $sql .= "and user_uuid = :user_uuid "; + $parameters['domain_uuid'] = $_SESSION['domain_uuid']; + $parameters['user_uuid'] = $user_uuid; + $call_center_agent_uuid = $database->select($sql, $parameters, 'column'); + unset($sql, $parameters); + + //update the user_status + if (isset($call_center_agent_uuid) && is_uuid($call_center_agent_uuid) && !empty($user_status)) { + $esl = event_socket::create(); + $switch_cmd = "callcenter_config agent set status ".$call_center_agent_uuid." '".$user_status."'"; + $switch_result = event_socket::api($switch_cmd); + } + + //update the user state + if (isset($call_center_agent_uuid) && is_uuid($call_center_agent_uuid)) { + $esl = event_socket::create(); + $cmd = "callcenter_config agent set state ".$call_center_agent_uuid." Waiting"; + $response = event_socket::api($cmd); + } + } + + //response message + if ($action == 'edit') { + message::add($text['message-update'],'positive'); + } + else { + message::add($text['message-add'],'positive'); + } + } + +//populate form + if (persistent_form_values('exists')) { + //populate the form with values from session variable + persistent_form_values('load'); + //clear, set $unsaved flag + persistent_form_values('clear'); + } + else { + //populate the form with values from db + $sql = "select domain_uuid, user_uuid, username, user_email, api_key, user_totp_secret, "; + $sql .= "user_type, contact_uuid, user_enabled, user_status "; + $sql .= "from v_users "; + $sql .= "where user_uuid = :user_uuid "; + if (!permission_exists('user_all')) { + $sql .= "and domain_uuid = :domain_uuid "; + $parameters['domain_uuid'] = $domain_uuid; + } + $parameters['user_uuid'] = $user_uuid; + $row = $database->select($sql, $parameters, 'row'); + if (is_array($row) && sizeof($row) > 0) { + $domain_uuid = $row["domain_uuid"]; + $user_uuid = $row["user_uuid"]; + $username = $row["username"]; + $user_email = $row["user_email"]; + $api_key = $row["api_key"]; + $user_totp_secret = $row["user_totp_secret"]; + $user_type = $row["user_type"]; + $user_enabled = $row["user_enabled"]; + if (permission_exists('contact_view')) { + $contact_uuid = $row["contact_uuid"]; + } + $user_status = $row["user_status"]; + } + else { + message::add($text['message-invalid_user'], 'negative', 7500); + header("Location: user_edit.php?id=".$_SESSION['user_uuid']); + exit; + } + unset($sql, $parameters, $row); + + //get all language codes from database + $sql = "select * from v_languages order by language asc "; + $languages = $database->select($sql, null, 'all'); + + //get user settings + $sql = "select * from v_user_settings "; + $sql .= "where user_uuid = :user_uuid "; + $sql .= "and user_setting_enabled = true "; + $parameters['user_uuid'] = $user_uuid; + $result = $database->select($sql, $parameters, 'all'); + if (is_array($result)) { + foreach($result as $row) { + $name = $row['user_setting_name']; + $category = $row['user_setting_category']; + $subcategory = $row['user_setting_subcategory']; + if (empty($subcategory)) { + //$$category[$name] = $row['domain_setting_value']; + $user_settings[$category][$name] = $row['user_setting_value']; + } + else { + $user_settings[$category][$subcategory][$name] = $row['user_setting_value']; + } + } + } + unset($sql, $parameters, $result, $row); + } + +//set the defaults + if (empty($user_totp_secret)) { $user_totp_secret = ""; } + +//create token + $object = new token; + $token = $object->create($_SERVER['PHP_SELF']); + +//include the header + require_once "resources/header.php"; + $document['title'] = $text['title-user_edit']; + +//show the content + if (permission_exists('user_password')) { + echo "\n"; + } + + echo "
\n"; + + echo "
\n"; + echo "
".$text['title-user_profile']."
\n"; + echo "
\n"; + if (!empty($unsaved)) { + echo "
".$text['message-unsaved_changes']."
"; + } + + $button_margin = 'margin-left: 15px;'; + + if (permission_exists('user_add') || permission_exists('user_edit')) { + echo button::create(['type'=>'button','label'=>$text['button-save'],'icon'=>$settings->get('theme', 'button_icon_save'),'id'=>'btn_save','style'=>'margin-left: 15px;','onclick'=>'submit_form();']); + } + echo "
\n"; + echo "
\n"; + echo "
\n"; + + echo $text['description-user_profile']."\n"; + echo "

\n"; + + echo "
\n"; + echo ""; + + echo " "; + echo " "; + echo " "; + echo " "; + + if (permission_exists('user_password')) { + echo " "; + echo " "; + echo " "; + echo " "; + echo " "; + echo " "; + echo " "; + echo " "; + } + + echo " "; + echo " "; + echo " "; + echo " "; + + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + + if (permission_exists("user_status")) { + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + } + + //if (permission_exists('api_key')) { + // echo " "; + // echo " "; + // echo " "; + // echo " "; + // + + //user time based one time password secret + if (!empty($_SESSION['authentication']['methods']) && in_array('totp', $_SESSION['authentication']['methods'])) { + if (!empty($user_totp_secret) && !empty($username)) { + $otpauth = "otpauth://totp/".$username."?secret=".$user_totp_secret."&issuer=".$_SESSION['domain_name']; + + require_once 'resources/qr_code/QRErrorCorrectLevel.php'; + require_once 'resources/qr_code/QRCode.php'; + require_once 'resources/qr_code/QRCodeImage.php'; + + try { + $code = new QRCode (- 1, QRErrorCorrectLevel::H); + $code->addData($otpauth); + $code->make(); + $img = new QRCodeImage ($code, $width=210, $height=210, $quality=50); + $img->draw(); + $image = $img->getImage(); + $img->finish(); + } + catch (Exception $error) { + echo $error; + } + } + echo "\n"; + echo "\n"; + echo "\n"; + echo "\n"; + } + + echo "
".$text['label-username'].""; + echo " ".escape($username)."\n"; + echo "
".$text['label-password'].""; + echo " "; //help defeat browser auto-fill + echo " "; + echo "

\n"; + if ((!empty($required['length']) && is_numeric($required['length']) && $required['length'] != 0) || $required['number'] || $required['lowercase'] || $required['uppercase'] || $required['special']) { + echo $text['label-required'].': '; + if (is_numeric($required['length']) && $required['length'] != 0) { + echo $required['length']." ".$text['label-characters']; + if ($required['number'] || $required['lowercase'] || $required['uppercase'] || $required['special']) { + echo " ("; + } + } + if ($required['number']) { + $required_temp[] = $text['label-number']; + } + if ($required['lowercase']) { + $required_temp[] = $text['label-lowercase']; + } + if ($required['uppercase']) { + $required_temp[] = $text['label-uppercase']; + } + if ($required['special']) { + $required_temp[] = $text['label-special']; + } + if (!empty($required_temp)) { + echo implode(', ',$required_temp); + if (is_numeric($required['length']) && $required['length'] != 0) { + echo ")"; + } + } + unset($required_temp); + } + echo "
".$text['label-confirm_password'].""; + echo "
\n"; + echo " ".$text['message-green_border_passwords_match']."\n"; + echo "
".$text['label-email']."
\n"; + echo " ".$text['label-user_language']."\n"; + echo " \n"; + echo " \n"; + echo "
\n"; + echo " ".$text['description-user_language']."
\n"; + echo "
\n"; + echo " ".$text['label-time_zone']."\n"; + echo " \n"; + echo " \n"; + echo "
\n"; + echo " ".$text['description-time_zone']."
\n"; + echo "
\n"; + echo " ".$text['label-status']."\n"; + echo " \n"; + echo " \n"; + echo "
\n"; + echo " ".$text['description-status']."
\n"; + echo "
".$text['label-api_key']."\n"; + // echo " "; + // if (empty($api_key)) { + // //generate api key + // echo button::create(['type'=>'button', + // 'label'=>$text['button-generate'], + // 'icon'=>'key', + // 'style'=>'margin-top: 1px; margin-bottom: 1px;', + // 'onclick'=>"document.getElementById('api_key').value = '".generate_password(32,3)."'; + // document.getElementById('frm').submit();"]); + // } + // else { + // //view the api key + // echo button::create(['type'=>'button', + // 'label'=>$text['button-view'], + // 'id'=>'button-api_key_view', + // 'icon'=>'key', + // 'style'=>'margin-top: 1px; margin-bottom: 1px;', + // 'onclick'=>"document.getElementById ('button-api_key_view').style.display = 'none'; + // document.getElementById('api_key').style.display = 'inline'; + // document.getElementById('button-api_key_hide').style.display = 'inline'; + // document.getElementById('button-api_key_view').style.display = 'none';"]); + // echo button::create(['type'=>'button', + // 'label'=>$text['button-hide'], + // 'id'=>'button-api_key_hide', + // 'icon'=>'key', + // 'style'=>'display: none;', + // 'onclick'=>"document.getElementById('api_key').style.display = 'none'; + // document.getElementById('button-api_key_hide').style.display = 'none'; + // document.getElementById('button-api_key_view').style.display = 'inline';"]); + // } + // if (!empty($text['description-api_key'])) { + // echo "
".$text['description-api_key']."
\n"; + // } + // echo "
\n"; + echo " ".$text['label-user_totp_secret']."\n"; + echo "\n"; + echo " "; + if (empty($user_totp_secret)) { + $base32 = new base2n(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', FALSE, TRUE, TRUE); + $user_totp_secret = $base32->encode(generate_password(20,3)); + echo button::create(['type'=>'button', + 'label'=>$text['button-setup'], + 'icon'=>'key', + 'onclick'=>"document.getElementById('user_totp_secret').value = '".$user_totp_secret."'; + document.getElementById('frm').submit();"]); + } + else { + echo " \n"; + echo button::create(['type'=>'button', + 'label'=>$text['button-view'], + 'id'=>'button-totp_view', + 'icon'=>'key', + 'onclick'=>"document.getElementById('totp_qr').style.display = 'inline'; + document.getElementById('button-totp_hide').style.display = 'inline'; + document.getElementById('button-totp_disable').style.display = 'inline'; + document.getElementById('button-totp_view').style.display = 'none';"]); + + echo button::create(['type'=>'button', + 'label'=>$text['button-hide'], + 'id'=>'button-totp_hide', + 'icon'=>'key', + 'style'=>'display: none;', + 'onclick'=>"document.getElementById('totp_qr').style.display = 'none'; + document.getElementById('button-totp_hide').style.display = 'none'; + document.getElementById('button-totp_disable').style.display = 'none'; + document.getElementById('button-totp_view').style.display = 'inline';"]); + + echo button::create(['type'=>'button', + 'label'=>$text['button-disable'], + 'id'=>'button-totp_disable', + 'icon'=>'trash', + 'style'=>'display: none;', + 'onclick'=>"document.getElementById('user_totp_secret').value = ''; + document.getElementById('frm').submit();"]); + } + if (empty($user_totp_secret)) { + echo "
".$text['description-user_totp_secret']."
\n"; + } + else { + echo "
".$text['description-user_totp_view']."
\n"; + } + echo "
"; + echo "
\n"; + echo "

"; + + echo "\n"; + + echo "
"; + +//hide password fields before submit + echo "\n"; + +//include the footer + require_once "resources/footer.php"; + +?>