mirror of https://github.com/itflow-org/itflow
Unify Agent and Client login, if same user exists as a client and an agent then offer a selection of client portal or agent portal
This commit is contained in:
parent
ca5fb2e010
commit
58d6ab7342
418
login.php
418
login.php
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
// Unified login (Agent + Client) using one email & password
|
||||||
|
|
||||||
// Enforce a Content Security Policy for security against cross-site scripting
|
// Enforce a Content Security Policy for security against cross-site scripting
|
||||||
header("Content-Security-Policy: default-src 'self'");
|
header("Content-Security-Policy: default-src 'self'");
|
||||||
|
|
||||||
|
|
@ -11,6 +13,21 @@ if (!file_exists('config.php')) {
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once "config.php";
|
require_once "config.php";
|
||||||
|
require_once "functions.php";
|
||||||
|
require_once "plugins/totp/totp.php";
|
||||||
|
|
||||||
|
// Sessions & cookies
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
// HTTP-Only cookies
|
||||||
|
ini_set("session.cookie_httponly", true);
|
||||||
|
|
||||||
|
// Tell client to only send cookie(s) over HTTPS
|
||||||
|
if ($config_https_only || !isset($config_https_only)) {
|
||||||
|
ini_set("session.cookie_secure", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
// Check if setup mode is enabled or the variable is missing
|
// Check if setup mode is enabled or the variable is missing
|
||||||
if (!isset($config_enable_setup) || $config_enable_setup == 1) {
|
if (!isset($config_enable_setup) || $config_enable_setup == 1) {
|
||||||
|
|
@ -19,28 +36,30 @@ if (!isset($config_enable_setup) || $config_enable_setup == 1) {
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Timezone
|
|
||||||
require_once "includes/inc_set_timezone.php";
|
|
||||||
|
|
||||||
// Check if the application is configured for HTTPS-only access
|
// Check if the application is configured for HTTPS-only access
|
||||||
if ($config_https_only && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') && (!isset($_SERVER['HTTP_X_FORWARDED_PROTO']) || $_SERVER['HTTP_X_FORWARDED_PROTO'] !== 'https')) {
|
if ($config_https_only && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') && (!isset($_SERVER['HTTP_X_FORWARDED_PROTO']) || $_SERVER['HTTP_X_FORWARDED_PROTO'] !== 'https')) {
|
||||||
echo "Login is restricted as ITFlow defaults to HTTPS-only for enhanced security. To login using HTTP, modify the config.php file by setting config_https_only to false. However, this is strongly discouraged, especially when accessing from potentially unsafe networks like the internet.";
|
echo "Login is restricted as ITFlow defaults to HTTPS-only for enhanced security. To login using HTTP, modify the config.php file by setting config_https_only to false. However, this is strongly discouraged, especially when accessing from potentially unsafe networks like the internet.";
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once "functions.php";
|
// Set Timezone after session_start
|
||||||
|
require_once "includes/inc_set_timezone.php";
|
||||||
require_once "plugins/totp/totp.php";
|
|
||||||
|
|
||||||
|
|
||||||
// IP & User Agent for logging
|
// IP & User Agent for logging
|
||||||
$session_ip = sanitizeInput(getIP());
|
$session_ip = sanitizeInput(getIP());
|
||||||
$session_user_agent = sanitizeInput($_SERVER['HTTP_USER_AGENT']);
|
$session_user_agent = sanitizeInput($_SERVER['HTTP_USER_AGENT'] ?? '');
|
||||||
|
|
||||||
// Block brute force password attacks - check recent failed login attempts for this IP
|
// Block brute force password attacks - check recent failed login attempts for this IP
|
||||||
// Block access if more than 15 failed login attempts have happened in the last 10 minutes
|
// Block access if more than 15 failed login attempts have happened in the last 10 minutes
|
||||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(log_id) AS failed_login_count FROM logs WHERE log_ip = '$session_ip' AND log_type = 'Login' AND log_action = 'Failed' AND log_created_at > (NOW() - INTERVAL 10 MINUTE)"));
|
$row = mysqli_fetch_assoc(mysqli_query(
|
||||||
|
$mysqli,
|
||||||
|
"SELECT COUNT(log_id) AS failed_login_count
|
||||||
|
FROM logs
|
||||||
|
WHERE log_ip = '$session_ip'
|
||||||
|
AND log_type = 'Login'
|
||||||
|
AND log_action = 'Failed'
|
||||||
|
AND log_created_at > (NOW() - INTERVAL 10 MINUTE)"
|
||||||
|
));
|
||||||
$failed_login_count = intval($row['failed_login_count']);
|
$failed_login_count = intval($row['failed_login_count']);
|
||||||
|
|
||||||
if ($failed_login_count >= 15) {
|
if ($failed_login_count >= 15) {
|
||||||
|
|
@ -53,7 +72,12 @@ if ($failed_login_count >= 15) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query Settings for company
|
// Query Settings for company
|
||||||
$sql_settings = mysqli_query($mysqli, "SELECT * FROM settings LEFT JOIN companies ON settings.company_id = companies.company_id WHERE settings.company_id = 1");
|
$sql_settings = mysqli_query($mysqli, "
|
||||||
|
SELECT settings.*, companies.company_name, companies.company_logo
|
||||||
|
FROM settings
|
||||||
|
LEFT JOIN companies ON settings.company_id = companies.company_id
|
||||||
|
WHERE settings.company_id = 1
|
||||||
|
");
|
||||||
$row = mysqli_fetch_array($sql_settings);
|
$row = mysqli_fetch_array($sql_settings);
|
||||||
|
|
||||||
// Company info
|
// Company info
|
||||||
|
|
@ -73,64 +97,148 @@ $config_mail_from_name = sanitizeInput($row['config_mail_from_name']);
|
||||||
|
|
||||||
// Client Portal Enabled
|
// Client Portal Enabled
|
||||||
$config_client_portal_enable = intval($row['config_client_portal_enable']);
|
$config_client_portal_enable = intval($row['config_client_portal_enable']);
|
||||||
|
|
||||||
// Login key (if setup)
|
|
||||||
$config_login_key_required = $row['config_login_key_required'];
|
|
||||||
$config_login_key_secret = $row['config_login_key_secret'];
|
|
||||||
|
|
||||||
$config_login_remember_me_expire = intval($row['config_login_remember_me_expire']);
|
$config_login_remember_me_expire = intval($row['config_login_remember_me_expire']);
|
||||||
|
|
||||||
// Login key verification
|
// Azure / Entra for client
|
||||||
// If no/incorrect 'key' is supplied, send to client portal instead
|
$azure_client_id = $row['config_azure_client_id'] ?? null;
|
||||||
if ($config_login_key_required) {
|
|
||||||
if (!isset($_GET['key']) || $_GET['key'] !== $config_login_key_secret) {
|
$response = null;
|
||||||
redirect("client");
|
$token_field = null;
|
||||||
|
$show_role_choice = false;
|
||||||
|
$email = '';
|
||||||
|
$password = '';
|
||||||
|
|
||||||
|
// Handle POST login request (normal login or role choice)
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && (isset($_POST['login']) || isset($_POST['role_choice']))) {
|
||||||
|
|
||||||
|
$email = sanitizeInput($_POST['email'] ?? '');
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
$role_choice = $_POST['role_choice'] ?? null; // 'agent' or 'client'
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
if (empty($email) || empty($password) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
header("HTTP/1.1 401 Unauthorized");
|
||||||
|
$response = "
|
||||||
|
<div class='alert alert-danger'>
|
||||||
|
Incorrect username or password.
|
||||||
|
<button class='close' data-dismiss='alert'>×</button>
|
||||||
|
</div>";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unified lookup:
|
||||||
|
* - user_type = 1 → Agent
|
||||||
|
* - user_type = 2 → Client (must not be archived, client not archived)
|
||||||
|
* We fetch all possible matches for this email, then verify password per row.
|
||||||
|
* If both an agent and a client match with the same password:
|
||||||
|
* - First, show choice buttons (Agent / Client).
|
||||||
|
* - When user clicks a choice, we honor role_choice.
|
||||||
|
*/
|
||||||
|
$sql = mysqli_query($mysqli, "
|
||||||
|
SELECT users.*,
|
||||||
|
user_settings.*,
|
||||||
|
contacts.*,
|
||||||
|
clients.*
|
||||||
|
FROM users
|
||||||
|
LEFT JOIN user_settings ON users.user_id = user_settings.user_id
|
||||||
|
LEFT JOIN contacts ON users.user_id = contacts.contact_user_id
|
||||||
|
LEFT JOIN clients ON contacts.contact_client_id = clients.client_id
|
||||||
|
WHERE user_email = '$email'
|
||||||
|
AND user_archived_at IS NULL
|
||||||
|
AND user_status = 1
|
||||||
|
AND (
|
||||||
|
user_type = 1
|
||||||
|
OR (user_type = 2 AND client_archived_at IS NULL)
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
$agentRow = null;
|
||||||
|
$clientRow = null;
|
||||||
|
|
||||||
|
while ($r = mysqli_fetch_assoc($sql)) {
|
||||||
|
if (!password_verify($password, $r['user_password'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (intval($r['user_type']) === 1 && $agentRow === null) {
|
||||||
|
$agentRow = $r;
|
||||||
|
}
|
||||||
|
if (intval($r['user_type']) === 2 && $clientRow === null) {
|
||||||
|
$clientRow = $r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP-Only cookies
|
$selectedRow = null;
|
||||||
ini_set("session.cookie_httponly", true);
|
$selectedType = null; // 1 = agent, 2 = client
|
||||||
|
|
||||||
// Tell client to only send cookie(s) over HTTPS
|
if ($agentRow === null && $clientRow === null) {
|
||||||
if ($config_https_only || !isset($config_https_only)) {
|
|
||||||
ini_set("session.cookie_secure", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle POST login request
|
// No matching user/password combo
|
||||||
if (isset($_POST['login'])) {
|
header("HTTP/1.1 401 Unauthorized");
|
||||||
|
logAction("Login", "Failed", "Failed login attempt using $email");
|
||||||
|
|
||||||
// Sessions should start after the user has POSTed data
|
$response = "
|
||||||
session_start();
|
<div class='alert alert-danger'>
|
||||||
|
Incorrect username or password.
|
||||||
|
<button class='close' data-dismiss='alert'>×</button>
|
||||||
|
</div>";
|
||||||
|
|
||||||
// Passed login brute force check
|
} elseif ($agentRow !== null && $clientRow !== null) {
|
||||||
$email = sanitizeInput($_POST['email']);
|
|
||||||
$password = $_POST['password'];
|
|
||||||
|
|
||||||
$current_code = 0; // Default value
|
// Both agent and client accounts share same email + password
|
||||||
|
if ($role_choice === 'agent') {
|
||||||
|
$selectedRow = $agentRow;
|
||||||
|
$selectedType = 1;
|
||||||
|
} elseif ($role_choice === 'client') {
|
||||||
|
$selectedRow = $clientRow;
|
||||||
|
$selectedType = 2;
|
||||||
|
} else {
|
||||||
|
// First time we realise this is a dual-role account: ask user to pick
|
||||||
|
$show_role_choice = true;
|
||||||
|
$response = "
|
||||||
|
<div class='alert alert-info'>
|
||||||
|
This login can be used as either an Agent account or a Client Portal account.
|
||||||
|
Please choose how you want to continue.
|
||||||
|
<button class='close' data-dismiss='alert'>×</button>
|
||||||
|
</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Only one valid row (agent OR client)
|
||||||
|
if ($agentRow !== null) {
|
||||||
|
$selectedRow = $agentRow;
|
||||||
|
$selectedType = 1;
|
||||||
|
} else {
|
||||||
|
$selectedRow = $clientRow;
|
||||||
|
$selectedType = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a specific user selected, proceed with actual login
|
||||||
|
if ($selectedRow !== null && $selectedType !== null) {
|
||||||
|
|
||||||
|
$user_id = intval($selectedRow['user_id']);
|
||||||
|
$user_email = sanitizeInput($selectedRow['user_email']);
|
||||||
|
$session_user_id = $user_id; // to pass the user_id to logAction function
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// AGENT LOGIN FLOW
|
||||||
|
// =========================
|
||||||
|
if ($selectedType === 1) {
|
||||||
|
|
||||||
|
$user_name = sanitizeInput($selectedRow['user_name']);
|
||||||
|
$token = sanitizeInput($selectedRow['user_token']);
|
||||||
|
$force_mfa = intval($selectedRow['user_config_force_mfa']);
|
||||||
|
$user_role_id = intval($selectedRow['user_role_id']);
|
||||||
|
$user_encryption_ciphertext = $selectedRow['user_specific_encryption_ciphertext'];
|
||||||
|
$user_extension_key = $selectedRow['user_extension_key'];
|
||||||
|
|
||||||
|
$current_code = 0;
|
||||||
if (isset($_POST['current_code'])) {
|
if (isset($_POST['current_code'])) {
|
||||||
$current_code = intval($_POST['current_code']);
|
$current_code = intval($_POST['current_code']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM users LEFT JOIN user_settings on users.user_id = user_settings.user_id WHERE user_email = '$email' AND user_archived_at IS NULL AND user_status = 1 AND user_type = 1"));
|
$mfa_is_complete = false;
|
||||||
|
$extended_log = '';
|
||||||
// Check password
|
|
||||||
if ($row && password_verify($password, $row['user_password'])) {
|
|
||||||
|
|
||||||
// User password correct (partial login)
|
|
||||||
|
|
||||||
// Set temporary user variables
|
|
||||||
$user_name = sanitizeInput($row['user_name']);
|
|
||||||
$user_id = intval($row['user_id']);
|
|
||||||
$session_user_id = $user_id; // to pass the user_id to logAction function
|
|
||||||
$user_email = sanitizeInput($row['user_email']);
|
|
||||||
$token = sanitizeInput($row['user_token']);
|
|
||||||
$force_mfa = intval($row['user_config_force_mfa']);
|
|
||||||
$user_role_id = intval($row['user_role_id']);
|
|
||||||
$user_encryption_ciphertext = $row['user_specific_encryption_ciphertext'];
|
|
||||||
$user_extension_key = $row['user_extension_key'];
|
|
||||||
|
|
||||||
$mfa_is_complete = false; // Default to requiring MFA
|
|
||||||
$extended_log = ''; // Default value
|
|
||||||
|
|
||||||
if (empty($token)) {
|
if (empty($token)) {
|
||||||
// MFA is not configured
|
// MFA is not configured
|
||||||
|
|
@ -139,10 +247,14 @@ if (isset($_POST['login'])) {
|
||||||
|
|
||||||
// Validate MFA via a remember-me cookie
|
// Validate MFA via a remember-me cookie
|
||||||
if (isset($_COOKIE['rememberme'])) {
|
if (isset($_COOKIE['rememberme'])) {
|
||||||
// Get remember tokens less than $config_login_remember_me_days_expire days old
|
$remember_tokens = mysqli_query($mysqli, "
|
||||||
$remember_tokens = mysqli_query($mysqli, "SELECT remember_token_token FROM remember_tokens WHERE remember_token_user_id = $user_id AND remember_token_created_at > (NOW() - INTERVAL $config_login_remember_me_expire DAY)");
|
SELECT remember_token_token
|
||||||
while ($row = mysqli_fetch_assoc($remember_tokens)) {
|
FROM remember_tokens
|
||||||
if (hash_equals($row['remember_token_token'], $_COOKIE['rememberme'])) {
|
WHERE remember_token_user_id = $user_id
|
||||||
|
AND remember_token_created_at > (NOW() - INTERVAL $config_login_remember_me_expire DAY)
|
||||||
|
");
|
||||||
|
while ($remember_row = mysqli_fetch_assoc($remember_tokens)) {
|
||||||
|
if (hash_equals($remember_row['remember_token_token'], $_COOKIE['rememberme'])) {
|
||||||
$mfa_is_complete = true;
|
$mfa_is_complete = true;
|
||||||
$extended_log = 'with 2FA remember-me cookie';
|
$extended_log = 'with 2FA remember-me cookie';
|
||||||
break;
|
break;
|
||||||
|
|
@ -157,25 +269,48 @@ if (isset($_POST['login'])) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($mfa_is_complete) {
|
if ($mfa_is_complete) {
|
||||||
// MFA Completed successfully
|
// FULL AGENT LOGIN SUCCESS
|
||||||
|
|
||||||
// FULL LOGIN SUCCESS
|
|
||||||
|
|
||||||
// Create a remember me token, if requested
|
// Create a remember me token, if requested
|
||||||
if (isset($_POST['remember_me'])) {
|
if (isset($_POST['remember_me'])) {
|
||||||
// TODO: Record the UA and IP a token is generated from so that can be shown later on
|
|
||||||
$newRememberToken = bin2hex(random_bytes(64));
|
$newRememberToken = bin2hex(random_bytes(64));
|
||||||
setcookie('rememberme', $newRememberToken, time() + 86400*$config_login_remember_me_expire, "/", null, true, true);
|
setcookie(
|
||||||
mysqli_query($mysqli, "INSERT INTO remember_tokens SET remember_token_user_id = $user_id, remember_token_token = '$newRememberToken'");
|
'rememberme',
|
||||||
|
$newRememberToken,
|
||||||
|
time() + 86400 * $config_login_remember_me_expire,
|
||||||
|
"/",
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
mysqli_query($mysqli, "
|
||||||
|
INSERT INTO remember_tokens
|
||||||
|
SET remember_token_user_id = $user_id,
|
||||||
|
remember_token_token = '$newRememberToken'
|
||||||
|
");
|
||||||
|
|
||||||
$extended_log .= ", generated a new remember-me token";
|
$extended_log .= ", generated a new remember-me token";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check this login isn't suspicious
|
// Check this login isn't suspicious
|
||||||
$sql_ip_prev_logins = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(log_id) AS ip_previous_logins FROM logs WHERE log_type = 'Login' AND log_action = 'Success' AND log_ip = '$session_ip' AND log_user_id = $user_id"));
|
$sql_ip_prev_logins = mysqli_fetch_assoc(mysqli_query($mysqli, "
|
||||||
|
SELECT COUNT(log_id) AS ip_previous_logins
|
||||||
|
FROM logs
|
||||||
|
WHERE log_type = 'Login'
|
||||||
|
AND log_action = 'Success'
|
||||||
|
AND log_ip = '$session_ip'
|
||||||
|
AND log_user_id = $user_id
|
||||||
|
"));
|
||||||
$ip_previous_logins = sanitizeInput($sql_ip_prev_logins['ip_previous_logins']);
|
$ip_previous_logins = sanitizeInput($sql_ip_prev_logins['ip_previous_logins']);
|
||||||
|
|
||||||
$sql_ua_prev_logins = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(log_id) AS ua_previous_logins FROM logs WHERE log_type = 'Login' AND log_action = 'Success' AND log_user_agent = '$session_user_agent' AND log_user_id = $user_id"));
|
$sql_ua_prev_logins = mysqli_fetch_assoc(mysqli_query($mysqli, "
|
||||||
|
SELECT COUNT(log_id) AS ua_previous_logins
|
||||||
|
FROM logs
|
||||||
|
WHERE log_type = 'Login'
|
||||||
|
AND log_action = 'Success'
|
||||||
|
AND log_user_agent = '$session_user_agent'
|
||||||
|
AND log_user_id = $user_id
|
||||||
|
"));
|
||||||
$ua_prev_logins = sanitizeInput($sql_ua_prev_logins['ua_previous_logins']);
|
$ua_prev_logins = sanitizeInput($sql_ua_prev_logins['ua_previous_logins']);
|
||||||
|
|
||||||
// Notify if both the user agent and IP are different
|
// Notify if both the user agent and IP are different
|
||||||
|
|
@ -209,32 +344,20 @@ if (isset($_POST['login'])) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup encryption session key
|
// Setup encryption session key
|
||||||
if (isset($user_encryption_ciphertext)) {
|
if (!empty($user_encryption_ciphertext)) {
|
||||||
$site_encryption_master_key = decryptUserSpecificKey($user_encryption_ciphertext, $password);
|
$site_encryption_master_key = decryptUserSpecificKey($user_encryption_ciphertext, $password);
|
||||||
generateUserSessionKey($site_encryption_master_key);
|
generateUserSessionKey($site_encryption_master_key);
|
||||||
|
|
||||||
// Setup extension - currently unused
|
|
||||||
//if (is_null($user_extension_key)) {
|
|
||||||
// Extension cookie
|
|
||||||
// Note: Browsers don't accept cookies with SameSite None if they are not HTTPS.
|
|
||||||
//setcookie("user_extension_key", "$user_extension_key", ['path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'None']);
|
|
||||||
|
|
||||||
// Set PHP session in DB, so we can access the session encryption data (above)
|
|
||||||
//$user_php_session = session_id();
|
|
||||||
//mysqli_query($mysqli, "UPDATE users SET user_php_session = '$user_php_session' WHERE user_id = $user_id");
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to last visited or config home
|
// Redirect to last visited or config home
|
||||||
|
|
||||||
if (isset($_GET['last_visited']) && (str_starts_with(base64_decode($_GET['last_visited']), '/agent') || str_starts_with(base64_decode($_GET['last_visited']), '/admin'))) {
|
if (isset($_GET['last_visited']) && (str_starts_with(base64_decode($_GET['last_visited']), '/agent') || str_starts_with(base64_decode($_GET['last_visited']), '/admin'))) {
|
||||||
|
|
||||||
redirect($_SERVER["REQUEST_SCHEME"] . "://" . $config_base_url . base64_decode($_GET['last_visited']) );
|
redirect($_SERVER["REQUEST_SCHEME"] . "://" . $config_base_url . base64_decode($_GET['last_visited']));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
redirect("agent/$config_start_page");
|
redirect("agent/$config_start_page");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// MFA is configured and needs to be confirmed, or was unsuccessful
|
// MFA is configured and needs to be confirmed, or was unsuccessful
|
||||||
|
|
@ -242,7 +365,9 @@ if (isset($_POST['login'])) {
|
||||||
// HTML code for the token input field
|
// HTML code for the token input field
|
||||||
$token_field = "
|
$token_field = "
|
||||||
<div class='input-group mb-3'>
|
<div class='input-group mb-3'>
|
||||||
<input type='text' inputmode='numeric' pattern='[0-9]*' maxlength='6' class='form-control' placeholder='Enter your 2FA code' name='current_code' required autofocus>
|
<input type='text' inputmode='numeric' pattern='[0-9]*' maxlength='6'
|
||||||
|
class='form-control' placeholder='Enter your 2FA code'
|
||||||
|
name='current_code' required autofocus>
|
||||||
<div class='input-group-append'>
|
<div class='input-group-append'>
|
||||||
<div class='input-group-text'>
|
<div class='input-group-text'>
|
||||||
<span class='fas fa-key'></span>
|
<span class='fas fa-key'></span>
|
||||||
|
|
@ -250,11 +375,9 @@ if (isset($_POST['login'])) {
|
||||||
</div>
|
</div>
|
||||||
</div>";
|
</div>";
|
||||||
|
|
||||||
// Log/notify if MFA was unsuccessful
|
|
||||||
if ($current_code !== 0) {
|
if ($current_code !== 0) {
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
logAction("Login", "MFA Failed", "$user_name failed MFA", 0, $user_id);
|
logAction("Login", "MFA Failed", "$user_email failed MFA", 0, $user_id);
|
||||||
|
|
||||||
// Email the tech to advise their credentials may be compromised
|
// Email the tech to advise their credentials may be compromised
|
||||||
if (!empty($config_smtp_host)) {
|
if (!empty($config_smtp_host)) {
|
||||||
|
|
@ -270,10 +393,9 @@ if (isset($_POST['login'])) {
|
||||||
'body' => $body
|
'body' => $body
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
$mail = addToMailQueue($data);
|
addToMailQueue($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTML feedback for incorrect 2FA code
|
|
||||||
$response = "
|
$response = "
|
||||||
<div class='alert alert-warning'>
|
<div class='alert alert-warning'>
|
||||||
Please Enter 2FA Code!
|
Please Enter 2FA Code!
|
||||||
|
|
@ -282,24 +404,59 @@ if (isset($_POST['login'])) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// CLIENT LOGIN FLOW
|
||||||
|
// =========================
|
||||||
|
} elseif ($selectedType === 2) {
|
||||||
|
|
||||||
|
if ($config_client_portal_enable != 1) {
|
||||||
|
// Client portal disabled
|
||||||
|
header("HTTP/1.1 401 Unauthorized");
|
||||||
|
logAction("Client Login", "Failed", "Client portal disabled; login attempt using $email");
|
||||||
|
$response = "
|
||||||
|
<div class='alert alert-danger'>
|
||||||
|
Incorrect username or password.
|
||||||
|
<button class='close' data-dismiss='alert'>×</button>
|
||||||
|
</div>";
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Password incorrect or user doesn't exist - show generic error
|
$client_id = intval($selectedRow['contact_client_id']);
|
||||||
|
$contact_id = intval($selectedRow['contact_id']);
|
||||||
|
$user_auth_method = sanitizeInput($selectedRow['user_auth_method']);
|
||||||
|
|
||||||
|
if ($client_id && $contact_id && $user_auth_method === 'local') {
|
||||||
|
|
||||||
|
$_SESSION['client_logged_in'] = true;
|
||||||
|
$_SESSION['client_id'] = $client_id;
|
||||||
|
$_SESSION['user_id'] = $user_id;
|
||||||
|
$_SESSION['user_type'] = 2;
|
||||||
|
$_SESSION['contact_id'] = $contact_id;
|
||||||
|
$_SESSION['login_method'] = "local";
|
||||||
|
|
||||||
|
logAction("Client Login", "Success", "Client contact $user_email successfully logged in locally", $client_id, $user_id);
|
||||||
|
|
||||||
|
header("Location: client/index.php");
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Not allowed or invalid
|
||||||
|
logAction("Client Login", "Failed", "Failed client portal login attempt using $email (invalid auth method or missing contact/client)", $client_id ?? 0, $user_id);
|
||||||
|
|
||||||
header("HTTP/1.1 401 Unauthorized");
|
header("HTTP/1.1 401 Unauthorized");
|
||||||
|
|
||||||
logAction("Login", "Failed", "Failed login attempt using $email");
|
|
||||||
|
|
||||||
$response = "
|
$response = "
|
||||||
<div class='alert alert-danger'>
|
<div class='alert alert-danger'>
|
||||||
Incorrect username or password.
|
Incorrect username or password.
|
||||||
<button class='close' data-dismiss='alert'>×</button>
|
<button class='close' data-dismiss='alert'>×</button>
|
||||||
</div>";
|
</div>";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
|
@ -313,10 +470,7 @@ if (isset($_POST['login'])) {
|
||||||
<!-- Font Awesome -->
|
<!-- Font Awesome -->
|
||||||
<link rel="stylesheet" href="plugins/fontawesome-free/css/all.min.css">
|
<link rel="stylesheet" href="plugins/fontawesome-free/css/all.min.css">
|
||||||
|
|
||||||
<!--
|
<!-- Favicon -->
|
||||||
Favicon
|
|
||||||
If Fav Icon exists else use the default one
|
|
||||||
-->
|
|
||||||
<?php if(file_exists('uploads/favicon.ico')) { ?>
|
<?php if(file_exists('uploads/favicon.ico')) { ?>
|
||||||
<link rel="icon" type="image/x-icon" href="/uploads/favicon.ico">
|
<link rel="icon" type="image/x-icon" href="/uploads/favicon.ico">
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|
@ -336,7 +490,6 @@ if (isset($_POST['login'])) {
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- /.login-logo -->
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body login-card-body">
|
<div class="card-body login-card-body">
|
||||||
|
|
||||||
|
|
@ -350,16 +503,21 @@ if (isset($_POST['login'])) {
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
|
|
||||||
<div class="input-group mb-3" <?php if (isset($token_field)) { echo "hidden"; } ?>>
|
<div class="input-group mb-3" <?php if (isset($token_field) && $token_field) { echo "hidden"; } ?>>
|
||||||
<input type="text" class="form-control" placeholder="Agent Email" name="email" value="<?php if (isset($token_field)) { echo $email; }?>" required <?php if (!isset($token_field)) { echo "autofocus"; } ?> >
|
<input type="text" class="form-control" placeholder="Email" name="email"
|
||||||
|
value="<?php echo htmlspecialchars($email ?? '', ENT_QUOTES); ?>"
|
||||||
|
required <?php if (!isset($token_field) || !$token_field) { echo "autofocus"; } ?> >
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<div class="input-group-text">
|
<div class="input-group-text">
|
||||||
<span class="fas fa-envelope"></span>
|
<span class="fas fa-envelope"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group mb-3" <?php if (isset($token_field)) { echo "hidden"; } ?>>
|
|
||||||
<input type="password" class="form-control" placeholder="Agent Password" name="password" value="<?php if (isset($token_field)) { echo $password; } ?>" required>
|
<div class="input-group mb-3" <?php if (isset($token_field) && $token_field) { echo "hidden"; } ?>>
|
||||||
|
<input type="password" class="form-control" placeholder="Password" name="password"
|
||||||
|
value="<?php echo isset($token_field) && $token_field ? htmlspecialchars($password ?? '', ENT_QUOTES) : ''; ?>"
|
||||||
|
required>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<div class="input-group-text">
|
<div class="input-group-text">
|
||||||
<span class="fas fa-lock"></span>
|
<span class="fas fa-lock"></span>
|
||||||
|
|
@ -368,38 +526,58 @@ if (isset($_POST['login'])) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
if (isset($token_field)) {
|
// If agent needs MFA, show 2FA field + remember me
|
||||||
|
if (isset($token_field) && $token_field) {
|
||||||
|
|
||||||
echo $token_field;
|
echo $token_field;
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="form-group mb-3">
|
<div class="form-group mb-3">
|
||||||
<div class="custom-control custom-checkbox">
|
<div class="custom-control custom-checkbox">
|
||||||
<input type="checkbox" class="custom-control-input" id="remember_me" name="remember_me">
|
<input type="checkbox" class="custom-control-input" id="remember_me" name="remember_me">
|
||||||
<label class="custom-control-label" for="remember_me">Remember Me</label>
|
<label class="custom-control-label" for="remember_me">Remember Me</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary btn-block mb-3" name="login">Sign In</button>
|
|
||||||
|
|
||||||
<?php if($config_client_portal_enable == 1){ ?>
|
|
||||||
<hr>
|
|
||||||
<h5 class="text-center">Looking for the <a href="client">Client Portal?<a/></h5>
|
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|
||||||
|
<?php if ($show_role_choice): ?>
|
||||||
|
<!-- When both agent & client accounts exist with same email/password -->
|
||||||
|
<div class="mb-2 text-center">
|
||||||
|
<button type="submit" class="btn btn-primary btn-block mb-2" name="role_choice" value="agent">
|
||||||
|
Log in as Agent
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn btn-success btn-block" name="role_choice" value="client">
|
||||||
|
Log in as Client
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<button type="submit" class="btn btn-primary btn-block mb-3" name="login">Sign In</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<?php if($config_client_portal_enable == 1){ ?>
|
||||||
|
<hr>
|
||||||
|
<?php if (!empty($config_smtp_host)) { ?>
|
||||||
|
<h5 class="text-center"><a href="client/login_reset.php">Forgot password?</a></h5>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if (!empty($azure_client_id)) { ?>
|
||||||
|
<div class="col text-center mt-2">
|
||||||
|
<a href="client/login_microsoft.php">
|
||||||
|
<button type="button" class="btn btn-secondary">Login with Microsoft Entra</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php } ?>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- /.login-card-body -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- /.login-box -->
|
|
||||||
|
<?php
|
||||||
|
if (isset($config_whitelabel_enabled) && !$config_whitelabel_enabled) {
|
||||||
|
echo '<small class="text-muted">Powered by ITFlow</small>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
<!-- jQuery -->
|
<!-- jQuery -->
|
||||||
<script src="plugins/jquery/jquery.min.js"></script>
|
<script src="plugins/jquery/jquery.min.js"></script>
|
||||||
|
|
@ -410,8 +588,6 @@ if (isset($_POST['login'])) {
|
||||||
<!-- AdminLTE App -->
|
<!-- AdminLTE App -->
|
||||||
<script src="plugins/adminlte/js/adminlte.min.js"></script>
|
<script src="plugins/adminlte/js/adminlte.min.js"></script>
|
||||||
|
|
||||||
<!-- <script src="plugins/Show-Hide-Passwords-Bootstrap-4/bootstrap-show-password.min.js"></script> -->
|
|
||||||
|
|
||||||
<!-- Prevents resubmit on refresh or back -->
|
<!-- Prevents resubmit on refresh or back -->
|
||||||
<script src="js/login_prevent_resubmit.js"></script>
|
<script src="js/login_prevent_resubmit.js"></script>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue