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)} +

+
+ or +
+
+ {foreach $banners as $banner} + + {$banner.image} +
+ {/foreach} + {/if} diff --git a/resources/functions.php b/resources/functions.php index 5de807806e..9ee7e8f9c3 100644 --- a/resources/functions.php +++ b/resources/functions.php @@ -27,6 +27,21 @@ Luis Daniel Lucio Quiroz */ + if (!function_exists('str_contains')) { + /** + * Determine if a string contains a given substring + *

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; } }