diff --git a/core/authentication/resources/classes/authentication.php b/core/authentication/resources/classes/authentication.php index 9cdc02ebae..46ebc34e56 100644 --- a/core/authentication/resources/classes/authentication.php +++ b/core/authentication/resources/classes/authentication.php @@ -47,14 +47,18 @@ class authentication { */ public function __construct() { $this->database = database::new(); + $this->user_uuid = null; } /** * validate uses authentication plugins to check if a user is authorized to login - * @return array [plugin] => last plugin used to authenticate the user [authorized] => true or false + * @return array|false [plugin] => last plugin used to authenticate the user [authorized] => true or false */ public function validate() { + //set default return array as null + $result = null; + //get the domain_name and domain_uuid if (!isset($this->domain_name) || !isset($this->domain_uuid)) { $this->get_domain(); @@ -79,7 +83,7 @@ class authentication { } //check if contacts app exists - $contacts_exists = file_exists($_SERVER["DOCUMENT_ROOT"].PROJECT_PATH.'/core/contacts/') ? true : false; + $contacts_exists = file_exists(dirname(__DIR__, 4) . '/core/contacts/'); //use the authentication plugins foreach ($_SESSION['authentication']['methods'] as $name) { @@ -121,10 +125,10 @@ class authentication { $result['user_uuid'] = $array["user_uuid"]; $result['contact_uuid'] = $array["contact_uuid"]; if ($contacts_exists) { - $result["contact_organization"] = $array["contact_organization"]; - $result["contact_name_given"] = $array["contact_name_given"]; - $result["contact_name_family"] = $array["contact_name_family"]; - $result["contact_image"] = $array["contact_image"]; + $result["contact_organization"] = $array["contact_organization"] ?? ''; + $result["contact_name_given"] = $array["contact_name_given"] ?? ''; + $result["contact_name_family"] = $array["contact_name_family"] ?? ''; + $result["contact_image"] = $array["contact_image"] ?? ''; } $result['domain_uuid'] = $array["domain_uuid"]; $result['authorized'] = $array["authorized"]; @@ -156,7 +160,7 @@ class authentication { $_SESSION['authentication']['plugin'][$name]['username'] = $_SESSION['username']; $_SESSION['authentication']['plugin'][$name]['user_uuid'] = $_SESSION['user_uuid']; $_SESSION['authentication']['plugin'][$name]['user_email'] = $_SESSION['user_email']; - $_SESSION['authentication']['plugin'][$name]['authorized'] = 0; + $_SESSION['authentication']['plugin'][$name]['authorized'] = false; } } } @@ -178,222 +182,22 @@ class authentication { } } - //result array -// $result["plugin"] = "database"; -// $result["domain_name"] = $_SESSION['domain_name']; -// if (!isset($_SESSION['username'])) { -// $result["username"] = $_SESSION['username']; -// } -// if (!isset($_SESSION['user_uuid'])) { -// $result["user_uuid"] = $_SESSION['user_uuid']; -// } -// $result["domain_uuid"] = $_SESSION['domain_uuid']; -// if (!isset($_SESSION['contact_uuid'])) { -// $result["contact_uuid"] = $_SESSION['contact_uuid']; -// } -// $result["authorized"] = $authorized; - //user is authorized - get user settings, check user cidr if ($authorized) { + //get the cidr restrictions from global, domain, and user default settings + $user_settings = new settings(['database' => $this->database, 'domain_uuid' => $this->domain_uuid, 'user_uuid' => $this->user_uuid]); + $cidr_list = $user_settings->get('domain', 'cidr', []); + if (check_cidr($cidr_list, $_SERVER['REMOTE_ADDR'])) { + //user passed the cidr check + self::create_user_session($result, $user_settings); + } else { + //user failed the cidr check - no longer authorized + $authorized = false; + } + } - //get the user settings - $sql = "select * from v_user_settings "; - $sql .= "where domain_uuid = :domain_uuid "; - $sql .= "and user_uuid = :user_uuid "; - $sql .= "and user_setting_enabled = 'true' "; - $parameters['domain_uuid'] = $result["domain_uuid"]; - $parameters['user_uuid'] = $result["user_uuid"]; - $user_settings = $this->database->select($sql, $parameters, 'all'); - unset($sql, $parameters); - - //build the user cidr array - if (is_array($user_settings) && @sizeof($user_settings) != 0) { - foreach ($user_settings as $row) { - if ($row['user_setting_category'] == "domain" && $row['user_setting_subcategory'] == "cidr" && $row['user_setting_name'] == "array") { - $cidr_array[] = $row['user_setting_value']; - } - } - } - - //check to see if user address is in the cidr array - if (isset($cidr_array) && !defined('STDIN')) { - $found = false; - foreach($cidr_array as $cidr) { - if (check_cidr($cidr, $_SERVER['REMOTE_ADDR'])) { - $found = true; - break; - } - } - if (!$found) { - - //log the failed attempt - $plugin_classname = substr($class_name, 7); - user_logs::add($_SESSION['authentication']['plugin'][$plugin_classname]); - - //destroy session - session_unset(); - session_destroy(); - - //send http 403 - header('HTTP/1.0 403 Forbidden', true, 403); - - //exit the code - exit(); - } - } - - //set the session variables - $_SESSION["domain_uuid"] = $result["domain_uuid"]; - $_SESSION["domain_name"] = $result["domain_name"]; - $_SESSION["user_uuid"] = $result["user_uuid"]; - $_SESSION["context"] = $result['domain_name']; - - //build the session server array to validate the session - global $conf; - if (!isset($conf['session.validate'])) { $conf['session.validate'][] = 'HTTP_USER_AGENT'; } - foreach($conf['session.validate'] as $name) { - $server_array[$name] = $_SERVER[$name]; - } - - //save the user hash to be used in validate the session - $_SESSION["user_hash"] = hash('sha256', implode($server_array)); - - //user session array - $_SESSION["user"]["domain_uuid"] = $result["domain_uuid"]; - $_SESSION["user"]["domain_name"] = $result["domain_name"]; - $_SESSION["user"]["user_uuid"] = $result["user_uuid"]; - $_SESSION["user"]["username"] = $result["username"]; - $_SESSION["user"]["contact_uuid"] = $result["contact_uuid"]; - if ($contacts_exists) { - $_SESSION["user"]["contact_organization"] = $result["contact_organization"] ?? null; - $_SESSION["user"]["contact_name"] = trim(($result["contact_name_given"] ?? '').' '.($result["contact_name_family"] ?? '')); - $_SESSION["user"]["contact_name_given"] = $result["contact_name_given"] ?? null; - $_SESSION["user"]["contact_name_family"] = $result["contact_name_family"] ?? null; - $_SESSION["user"]["contact_image"] = !empty($result["contact_image"]) && is_uuid($result["contact_image"]) ? $result["contact_image"] : null; - } - - //empty the permissions - if (isset($_SESSION['permissions'])) { - unset($_SESSION['permissions']); - } - - //get the groups assigned to the user - $group = new groups($this->database, $result["domain_uuid"], $result["user_uuid"]); - $group->session(); - - //get the permissions assigned to the user through the assigned groups - $permission = new permissions($this->database, $result["domain_uuid"], $result["user_uuid"]); - $permission->session(); - - //get the domains - if (file_exists($_SERVER["PROJECT_ROOT"]."/app/domains/app_config.php") && !is_cli()){ - require_once "app/domains/resources/domains.php"; - } - - //get the user settings - if (is_array($user_settings) && @sizeof($user_settings) != 0) { - foreach ($user_settings as $row) { - $name = $row['user_setting_name']; - $category = $row['user_setting_category']; - $subcategory = $row['user_setting_subcategory']; - if (!empty($row['user_setting_value'])) { - if (empty($subcategory)) { - //$$category[$name] = $row['domain_setting_value']; - if ($name == "array") { - $_SESSION[$category][] = $row['user_setting_value']; - } - else { - $_SESSION[$category][$name] = $row['user_setting_value']; - } - } - else { - //$$category[$subcategory][$name] = $row['domain_setting_value']; - if ($name == "array") { - $_SESSION[$category][$subcategory][] = $row['user_setting_value']; - } - else { - $_SESSION[$category][$subcategory][$name] = $row['user_setting_value']; - } - } - } - } - } - unset($user_settings); - - //get the extensions that are assigned to this user - if (file_exists($_SERVER["PROJECT_ROOT"]."/app/extensions/app_config.php")) { - if (isset($_SESSION["user"]) && is_uuid($_SESSION["user_uuid"]) && is_uuid($_SESSION["domain_uuid"]) && !isset($_SESSION['user']['extension'])) { - //get the user extension list - $_SESSION['user']['extension'] = null; - $sql = "select "; - $sql .= "e.extension_uuid, "; - $sql .= "e.extension, "; - $sql .= "e.number_alias, "; - $sql .= "e.user_context, "; - $sql .= "e.outbound_caller_id_name, "; - $sql .= "e.outbound_caller_id_number, "; - $sql .= "e.description "; - $sql .= "from "; - $sql .= "v_extension_users as u, "; - $sql .= "v_extensions as e "; - $sql .= "where "; - $sql .= "e.domain_uuid = :domain_uuid "; - $sql .= "and e.extension_uuid = u.extension_uuid "; - $sql .= "and u.user_uuid = :user_uuid "; - $sql .= "and e.enabled = 'true' "; - $sql .= "order by "; - $sql .= "e.extension asc "; - $parameters['domain_uuid'] = $_SESSION['domain_uuid']; - $parameters['user_uuid'] = $_SESSION['user_uuid']; - $result = $this->database->select($sql, $parameters, 'all'); - if (is_array($result) && @sizeof($result) != 0) { - foreach($result as $x => $row) { - //set the destination - $destination = $row['extension']; - if (!empty($row['number_alias'])) { - $destination = $row['number_alias']; - } - - //build the user array - $_SESSION['user']['extension'][$x]['user'] = $row['extension']; - $_SESSION['user']['extension'][$x]['number_alias'] = $row['number_alias']; - $_SESSION['user']['extension'][$x]['destination'] = $destination; - $_SESSION['user']['extension'][$x]['extension_uuid'] = $row['extension_uuid']; - $_SESSION['user']['extension'][$x]['outbound_caller_id_name'] = $row['outbound_caller_id_name']; - $_SESSION['user']['extension'][$x]['outbound_caller_id_number'] = $row['outbound_caller_id_number']; - $_SESSION['user']['extension'][$x]['user_context'] = $row['user_context']; - $_SESSION['user']['extension'][$x]['description'] = $row['description']; - - //set the context - $_SESSION['user']['user_context'] = $row["user_context"]; - $_SESSION['user_context'] = $row["user_context"]; - } - } - unset($sql, $parameters, $result, $row); - } - } - - //set the time zone - if (!isset($_SESSION["time_zone"]["user"])) { $_SESSION["time_zone"]["user"] = null; } - if (strlen($_SESSION["time_zone"]["user"] ?? '') === 0) { - //set the domain time zone as the default time zone - date_default_timezone_set($_SESSION['domain']['time_zone']['name']); - } - else { - //set the user defined time zone - date_default_timezone_set($_SESSION["time_zone"]["user"]); - } - - //regenerate the session on login - //session_regenerate_id(true); - - //set a session variable to indicate authorized is set to true - $_SESSION['authorized'] = true; - - //add the username to the session - username session could be set so check_auth uses an authorized session variable instead - $_SESSION['username'] = $_SESSION['user']["username"]; - - } //authorized true + //set a session variable to indicate whether or not we are authorized + $_SESSION['authorized'] = $authorized; //log the attempt $plugin_classname = substr($class_name, 7); @@ -403,6 +207,211 @@ class authentication { return $result ?? false; } + /** + * Creates a valid user session in the superglobal $_SESSION. + *
The $result must be a validated user with the appropriate variables set.
+ * The associative array
+ * @global database $database
+ * @global string $conf
+ * @param array|bool $result Associative array containing: domain_uuid, domain_name, user_uuid, username. Contact keys can be empty, but should still be present. They include: contact_uuid, contact_name_given, contact_name_family, contact_image.
+ * @param array $user_settings User settings from the v_user_settings table or an empty array.
+ * @return void
+ */
+ public static function create_user_session($result = [], $user_settings = []): void {
+ global $database;
+
+ //
+ // Validate data
+ //
+ if (empty($result)) {
+ return;
+ }
+
+ // Required keys
+ $required_keys = [
+ 'domain_uuid' => true,
+ 'domain_name' => true,
+ 'user_uuid' => true,
+ 'username' => true,
+ ];
+
+ // Any missing required_fields are left in the $diff array.
+ // When all keys are present the $diff array will be empty.
+ $diff = array_diff_key($required_keys, $result);
+
+ // All required keys must be present in the $result associative array
+ if (!empty($diff)) {
+ return;
+ }
+
+ // Domain and User UUIDs must be valid UUIDs
+ if (!is_uuid($result['domain_uuid']) || !is_uuid($result['user_uuid'])) {
+ return;
+ }
+
+ // If Contact UUID has a value it must be a valid UUID
+ if (!empty($result['contact_uuid']) && !is_uuid($result['contact_uuid'])) {
+ return;
+ }
+
+ //
+ // All data validated continue to create session
+ //
+
+ // Set project root directory
+ $project_root = dirname(__DIR__, 4);
+
+ // Set the session variables
+ $_SESSION["domain_uuid"] = $result["domain_uuid"];
+ $_SESSION["domain_name"] = $result["domain_name"];
+ $_SESSION["user_uuid"] = $result["user_uuid"];
+ $_SESSION["context"] = $result['domain_name'];
+
+ // Build the session server array to validate the session
+ global $conf;
+ if (!isset($conf['session.validate'])) {
+ $conf['session.validate'][] = 'HTTP_USER_AGENT';
+ }
+ foreach ($conf['session.validate'] as $name) {
+ $server_array[$name] = $_SERVER[$name];
+ }
+
+ // Save the user hash to be used in check_auth
+ $_SESSION["user_hash"] = hash('sha256', implode($server_array));
+
+ // User session array
+ $_SESSION["user"]["domain_uuid"] = $result["domain_uuid"];
+ $_SESSION["user"]["domain_name"] = $result["domain_name"];
+ $_SESSION["user"]["user_uuid"] = $result["user_uuid"];
+ $_SESSION["user"]["username"] = $result["username"];
+ $_SESSION["user"]["contact_uuid"] = $result["contact_uuid"] ?? null; //contact_uuid is optional
+
+ // Check for contacts
+ if (file_exists($project_root . '/core/contacts/')) {
+ $_SESSION["user"]["contact_organization"] = $result["contact_organization"] ?? null;
+ $_SESSION["user"]["contact_name"] = trim(($result["contact_name_given"] ?? '') . ' ' . ($result["contact_name_family"] ?? ''));
+ $_SESSION["user"]["contact_name_given"] = $result["contact_name_given"] ?? null;
+ $_SESSION["user"]["contact_name_family"] = $result["contact_name_family"] ?? null;
+ $_SESSION["user"]["contact_image"] = !empty($result["contact_image"]) && is_uuid($result["contact_image"]) ? $result["contact_image"] : null;
+ }
+
+ //empty the permissions
+ if (isset($_SESSION['permissions'])) {
+ unset($_SESSION['permissions']);
+ }
+
+ //get the groups assigned to the user
+ $group = new groups($database, $result["domain_uuid"], $result["user_uuid"]);
+ $group->session();
+
+ //get the permissions assigned to the user through the assigned groups
+ $permission = new permissions($database, $result["domain_uuid"], $result["user_uuid"]);
+ $permission->session();
+
+ //get the domains
+ if (file_exists($project_root . '/app/domains/resources/domains.php') && !is_cli()) {
+ require_once $project_root . 'app/domains/resources/domains.php';
+ }
+
+ //store user settings in the session when available
+ if (is_array($user_settings)) {
+ foreach ($user_settings as $row) {
+ $name = $row['user_setting_name'];
+ $category = $row['user_setting_category'];
+ $subcategory = $row['user_setting_subcategory'];
+ if (!empty($row['user_setting_value'])) {
+ if (empty($subcategory)) {
+ //$$category[$name] = $row['domain_setting_value'];
+ if ($name == "array") {
+ $_SESSION[$category][] = $row['user_setting_value'];
+ } else {
+ $_SESSION[$category][$name] = $row['user_setting_value'];
+ }
+ } else {
+ //$$category[$subcategory][$name] = $row['domain_setting_value'];
+ if ($name == "array") {
+ $_SESSION[$category][$subcategory][] = $row['user_setting_value'];
+ } else {
+ $_SESSION[$category][$subcategory][$name] = $row['user_setting_value'];
+ }
+ }
+ }
+ }
+ }
+
+ //get the extensions that are assigned to this user
+ if (file_exists($project_root . '/app/extensions/app_config.php')) {
+ if (isset($_SESSION["user"]) && is_uuid($_SESSION["user_uuid"]) && is_uuid($_SESSION["domain_uuid"]) && !isset($_SESSION['user']['extension'])) {
+ $parameters = [];
+ //get the user extension list
+ $_SESSION['user']['extension'] = null;
+ $sql = "select ";
+ $sql .= "e.extension_uuid, ";
+ $sql .= "e.extension, ";
+ $sql .= "e.number_alias, ";
+ $sql .= "e.user_context, ";
+ $sql .= "e.outbound_caller_id_name, ";
+ $sql .= "e.outbound_caller_id_number, ";
+ $sql .= "e.description ";
+ $sql .= "from ";
+ $sql .= "v_extension_users as u, ";
+ $sql .= "v_extensions as e ";
+ $sql .= "where ";
+ $sql .= "e.domain_uuid = :domain_uuid ";
+ $sql .= "and e.extension_uuid = u.extension_uuid ";
+ $sql .= "and u.user_uuid = :user_uuid ";
+ $sql .= "and e.enabled = 'true' ";
+ $sql .= "order by ";
+ $sql .= "e.extension asc ";
+ $parameters['domain_uuid'] = $_SESSION['domain_uuid'];
+ $parameters['user_uuid'] = $_SESSION['user_uuid'];
+ $extensions = $database->select($sql, $parameters, 'all');
+ if (!empty($extensions)) {
+ foreach ($extensions as $x => $row) {
+ //set the destination
+ $destination = $row['extension'];
+ if (!empty($row['number_alias'])) {
+ $destination = $row['number_alias'];
+ }
+
+ //build the user array
+ $_SESSION['user']['extension'][$x]['user'] = $row['extension'];
+ $_SESSION['user']['extension'][$x]['number_alias'] = $row['number_alias'];
+ $_SESSION['user']['extension'][$x]['destination'] = $destination;
+ $_SESSION['user']['extension'][$x]['extension_uuid'] = $row['extension_uuid'];
+ $_SESSION['user']['extension'][$x]['outbound_caller_id_name'] = $row['outbound_caller_id_name'];
+ $_SESSION['user']['extension'][$x]['outbound_caller_id_number'] = $row['outbound_caller_id_number'];
+ $_SESSION['user']['extension'][$x]['user_context'] = $row['user_context'];
+ $_SESSION['user']['extension'][$x]['description'] = $row['description'];
+
+ //set the context
+ $_SESSION['user']['user_context'] = $row["user_context"];
+ $_SESSION['user_context'] = $row["user_context"];
+ }
+ }
+ }
+ }
+
+ //set the time zone
+ if (!isset($_SESSION["time_zone"]["user"])) {
+ $_SESSION["time_zone"]["user"] = null;
+ }
+ if (strlen($_SESSION["time_zone"]["user"] ?? '') === 0) {
+ //set the domain time zone as the default time zone
+ date_default_timezone_set($_SESSION['domain']['time_zone']['name']);
+ } else {
+ //set the user defined time zone
+ date_default_timezone_set($_SESSION["time_zone"]["user"]);
+ }
+
+ //regenerate the session on login
+ //session_regenerate_id(true);
+
+ //add the username to the session - username session could be set so check_auth uses an authorized session variable instead
+ $_SESSION['username'] = $_SESSION['user']["username"];
+ return;
+ }
+
/**
* get_domain used to get the domain name from the URL or username and then sets both domain_name and domain_uuid
*/
diff --git a/core/authentication/resources/classes/plugins/database.php b/core/authentication/resources/classes/plugins/database.php
index f44649eda0..8bbf487c9a 100644
--- a/core/authentication/resources/classes/plugins/database.php
+++ b/core/authentication/resources/classes/plugins/database.php
@@ -122,6 +122,26 @@ class plugin_database {
$view->assign("login_password_description", $text['label-password_description']);
$view->assign("button_cancel", $text['button-cancel']);
$view->assign("button_forgot_password", $text['button-forgot_password']);
+
+ //assign openid values to the template
+ if ($settings->get('open_id', 'enabled', false)) {
+ $classes = $settings->get('open_id', 'methods', []);
+ $banners = [];
+ foreach ($classes as $open_id_class) {
+ if (class_exists($open_id_class)) {
+ $banners[] = [
+ 'name' => $open_id_class,
+ 'image' => $open_id_class::get_banner_image(),
+ 'url' => '/app/open_id/open_id.php?action=' . $open_id_class,
+ ];
+ }
+ }
+ if (count($banners) > 0) {
+ $view->assign('banners', $banners);
+ }
+ }
+
+ //assign user to the template
if (!empty($_SESSION['username'])) {
$view->assign("username", $_SESSION['username']);
}
diff --git a/core/authentication/resources/views/login.htm b/core/authentication/resources/views/login.htm
index 4ea057ccfc..2b10129c5a 100644
--- a/core/authentication/resources/views/login.htm
+++ b/core/authentication/resources/views/login.htm
@@ -99,6 +99,18 @@
{if !empty($username)}
{$button_cancel}
{/if}
+ {if isset($banners)}
+
Performs a case-sensitive check indicating if needle is contained in haystack.
+ * @param string $haystack The string to search in. + * @param string $needle The substring to search for in the haystack. + * @return bool Returns true if needle is in haystack, false otherwise + * @link https://www.php.net/manual/en/function.str-contains.php Official PHP documentation + * @see str_ends_with(), str_starts_with(), strpos(), stripos(), strrpos(), strripos(), strstr(), strpbrk(), substr(), preg_match() + */ + function str_contains(string $haystack, string $needle): bool { + return strpos($haystack, $needle) !== false; + } + } + if (!function_exists('str_starts_with')) { /** * Checks if a string starts with a given substring @@ -116,13 +131,35 @@ if (!function_exists('check_cidr')) { + /** + * Checks if the $ip_address is within the range of the given $cidr + * @param string|array $cidr + * @param string $ip_address + * @return bool return true if the IP address is in CIDR or if it is empty + */ function check_cidr($cidr, $ip_address) { - if (isset($cidr) && !empty($cidr)) { - list ($subnet, $mask) = explode('/', $cidr); - return ( ip2long($ip_address) & ~((1 << (32 - $mask)) - 1) ) == ip2long($subnet); - } else { - return false; + + //no cidr restriction + if (empty($cidr)) { + return true; } + + //check to see if the user's remote address is in the cidr array + if (is_array($cidr) { + //cidr is an array + foreach ($cidr as $value) { + if (check_cidr($value, $ip_address)) { + return true; + } + } + } else { + //cidr is a string + list ($subnet, $mask) = explode('/', $cidr); + return (ip2long($ip_address) & ~((1 << (32 - $mask)) - 1)) == ip2long($subnet); + } + + //value not found in cidr + return false; } }