mirror of
https://github.com/fusionpbx/fusionpbx.git
synced 2026-03-31 21:49:55 +00:00
Add remember me feature (#7750)
This commit is contained in:
@@ -135,6 +135,33 @@ $text['label-password_description']['zh-cn'] = "輸入您的密碼。";
|
||||
$text['label-password_description']['ja-jp'] = "パスワードを入力してください。";
|
||||
$text['label-password_description']['ko-kr'] = "비밀번호를 입력하세요.";
|
||||
|
||||
$text['label-remember_me']['en-us'] = "Remember Me";
|
||||
$text['label-remember_me']['en-gb'] = "Remember Me";
|
||||
$text['label-remember_me']['ar-eg'] = "تذكرني";
|
||||
$text['label-remember_me']['de-at'] = "Angemeldet bleiben";
|
||||
$text['label-remember_me']['de-ch'] = "Angemeldet bleiben";
|
||||
$text['label-remember_me']['de-de'] = "Angemeldet bleiben";
|
||||
$text['label-remember_me']['el-gr'] = "Θύστε μου";
|
||||
$text['label-remember_me']['es-cl'] = "Recuérdame";
|
||||
$text['label-remember_me']['es-mx'] = "Recuérdame";
|
||||
$text['label-remember_me']['fr-ca'] = "Se souvenir de moi";
|
||||
$text['label-remember_me']['fr-fr'] = "Se souvenir de moi";
|
||||
$text['label-remember_me']['he-il'] = "זכור אותי";
|
||||
$text['label-remember_me']['it-it'] = "Ricordami";
|
||||
$text['label-remember_me']['ka-ge'] = "დაიხმარე შემთხვევით";
|
||||
$text['label-remember_me']['nl-nl'] = "Onthoud me";
|
||||
$text['label-remember_me']['pl-pl'] = "Pamiętaj mnie";
|
||||
$text['label-remember_me']['pt-br'] = "Lembre-me";
|
||||
$text['label-remember_me']['pt-pt'] = "Lembre-me";
|
||||
$text['label-remember_me']['ro-ro'] = "Ține-mă minte";
|
||||
$text['label-remember_me']['ru-ru'] = "Запомнить меня";
|
||||
$text['label-remember_me']['sv-se'] = "Kom ihåg mig";
|
||||
$text['label-remember_me']['uk-ua'] = "Пам'ятати мене";
|
||||
$text['label-remember_me']['tr-tr'] = "Beni hatırla";
|
||||
$text['label-remember_me']['zh-cn'] = "记住我";
|
||||
$text['label-remember_me']['ja-jp'] = "私を覚えて";
|
||||
$text['label-remember_me']['ko-kr'] = "나를 기억해줘";
|
||||
|
||||
$text['description-totp']['en-us'] = "Scan the code with an authentication application or password manager. Then use it to generate the token for the login.";
|
||||
$text['description-totp']['en-gb'] = "Scan the code with an authentication application or password manager. Then use it to generate the token for the login.";
|
||||
$text['description-totp']['ar-eg'] = "امسح الرمز ضوئيًا باستخدام تطبيق المصادقة أو مدير كلمات المرور. ثم استخدمه لإنشاء الرمز المميز لتسجيل الدخول.";
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
Mark J Crane <markjcrane@fusionpbx.com>
|
||||
Portions created by the Initial Developer are Copyright (C) 2008-2024
|
||||
Portions created by the Initial Developer are Copyright (C) 2008-2026
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s):
|
||||
@@ -101,8 +101,122 @@ class authentication {
|
||||
//check if contacts app exists
|
||||
$contacts_exists = file_exists(dirname(__DIR__, 4) . '/core/contacts/');
|
||||
|
||||
//check for remember me cookie
|
||||
if (isset($_COOKIE['remember'])) {
|
||||
//set variables
|
||||
$plugin_name = 'remember';
|
||||
$remote_address = $_SERVER['REMOTE_ADDR'] ?? '';
|
||||
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
list($cookie_selector, $cookie_validator) = explode(":", $_COOKIE['remember']);
|
||||
|
||||
//get user logs
|
||||
$sql = "select user_uuid, remember_validator from v_user_logs ";
|
||||
$sql .= "where remember_selector = :remember_selector \n";
|
||||
$sql .= "and remote_address = :remote_address ";
|
||||
$sql .= "and user_agent = :user_agent ";
|
||||
$sql .= "and timestamp > NOW() - INTERVAL '7 days' ";
|
||||
$sql .= "and result = 'success' ";
|
||||
$sql .= "limit 1 ";
|
||||
$parameters['remember_selector'] = $cookie_selector;
|
||||
$parameters['remote_address'] = $remote_address;
|
||||
$parameters['user_agent'] = $user_agent;
|
||||
$user_logs = $this->database->select($sql, $parameters, 'row');
|
||||
unset($sql, $parameters);
|
||||
|
||||
//validate the token
|
||||
if (!empty($user_logs) && password_verify($cookie_validator, $user_logs['remember_validator'])) {
|
||||
//get the user details
|
||||
$sql = "select \n";
|
||||
$sql .= "u.domain_uuid, \n";
|
||||
$sql .= "d.domain_name, \n";
|
||||
$sql .= "u.user_uuid, \n";
|
||||
$sql .= "u.username, \n";
|
||||
$sql .= "u.contact_uuid \n";
|
||||
$sql .= "from v_users as u, v_domains as d \n";
|
||||
$sql .= "where user_uuid = :user_uuid \n";
|
||||
$sql .= "and u.domain_uuid = d.domain_uuid \n";
|
||||
$sql .= "and u.user_enabled = 'true' \n";
|
||||
$parameters['user_uuid'] = $user_logs['user_uuid'];
|
||||
$row = $this->database->select($sql, $parameters, 'row');
|
||||
unset($sql, $parameters);
|
||||
|
||||
//get the contact details
|
||||
if ($contacts_exists && !empty($row["contact_uuid"])) {
|
||||
$sql = "select * from v_contacts \n";
|
||||
$sql .= "where contact_uuid = :contact_uuid \n";
|
||||
$sql .= "and domain_uuid = :domain_uuid \n";
|
||||
$parameters['contact_uuid'] = $row["contact_uuid"];
|
||||
$parameters['domain_uuid'] = $row["domain_uuid"];
|
||||
$contact = $this->database->select($sql, $parameters, 'row');
|
||||
unset($sql, $parameters);
|
||||
}
|
||||
|
||||
//build a result array
|
||||
$result['plugin'] = $plugin_name;
|
||||
$result['domain_name'] = $row["domain_name"];
|
||||
$result['username'] = $row['username'];
|
||||
$result['user_uuid'] = $row['user_uuid'];
|
||||
$result['contact_uuid'] = $row["contact_uuid"];
|
||||
if ($contacts_exists) {
|
||||
$result["contact_organization"] = $contact["contact_organization"] ?? '';
|
||||
$result["contact_name_given"] = $contact["contact_name_given"] ?? '';
|
||||
$result["contact_name_family"] = $contact["contact_name_family"] ?? '';
|
||||
$result["contact_image"] = $contact["contact_image"] ?? '';
|
||||
}
|
||||
$result['domain_uuid'] = $row['domain_uuid'];
|
||||
$result['authorized'] = true;
|
||||
|
||||
//set the domain_uuid
|
||||
$this->domain_uuid = $row["domain_uuid"];
|
||||
|
||||
//set the user_uuid
|
||||
$this->user_uuid = $row["user_uuid"];
|
||||
|
||||
//save the result to the authentication plugin
|
||||
$_SESSION['authentication']['methods'] = [];
|
||||
$_SESSION['authentication']['methods'][] = $plugin_name;
|
||||
$_SESSION['authentication']['plugin'] = [];
|
||||
$_SESSION['authentication']['plugin'][$plugin_name] = $result;
|
||||
|
||||
//create the session
|
||||
self::create_user_session($result, $this->settings);
|
||||
|
||||
//generate new token
|
||||
$selector = uuid();
|
||||
$validator = generate_password(32);
|
||||
$hashed_validator = password_hash($validator, PASSWORD_DEFAULT);
|
||||
$token = $selector.':'.$validator;
|
||||
|
||||
//update the user logs
|
||||
$sql = "update v_user_logs ";
|
||||
$sql .= "set remember_selector = :remember_selector, ";
|
||||
$sql .= "remember_validator = :remember_validator ";
|
||||
$sql .= "where remember_selector = :cookie_selector ";
|
||||
$parameters['remember_selector'] = $selector;
|
||||
$parameters['remember_validator'] = $hashed_validator;
|
||||
$parameters['cookie_selector'] = $cookie_selector;
|
||||
$this->database->execute($sql, $parameters);
|
||||
unset($sql, $parameters);
|
||||
|
||||
//set the cookie
|
||||
setcookie('remember', $token, [
|
||||
'expires' => strtotime('+7 days'),
|
||||
'path' => '/',
|
||||
'secure' => true,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//use the authentication plugins
|
||||
foreach ($_SESSION['authentication']['methods'] as $name) {
|
||||
//skip the loop if already authorized
|
||||
if (isset($result['authorized']) && $result['authorized']) {
|
||||
break;
|
||||
}
|
||||
|
||||
//already processed the plugin move to the next plugin
|
||||
if (!empty($_SESSION['authentication']['plugin'][$name]['authorized']) && $_SESSION['authentication']['plugin'][$name]['authorized']) {
|
||||
continue;
|
||||
@@ -178,7 +292,7 @@ class authentication {
|
||||
}
|
||||
|
||||
//debug information
|
||||
//view_array($_SESSION['authentication'], false);
|
||||
// view_array($_SESSION['authentication'], false);
|
||||
|
||||
//set authorized to false if any authentication method failed
|
||||
$authorized = false;
|
||||
@@ -212,6 +326,60 @@ class authentication {
|
||||
}
|
||||
}
|
||||
|
||||
//create remember me token
|
||||
if ($authorized && isset($_SESSION['username']) && isset($_SESSION['remember'])) {
|
||||
//set session variables
|
||||
$input_username = $_SESSION['username'];
|
||||
$remember = $_SESSION['remember'];
|
||||
|
||||
//match the username
|
||||
$sql = "select user_uuid from v_users ";
|
||||
$sql .= "where username = :username";
|
||||
$parameters['username'] = $input_username;
|
||||
$user = $this->database->select($sql, $parameters, 'row');
|
||||
unset($sql, $parameters);
|
||||
|
||||
if ($remember && $user) {
|
||||
//generate the token
|
||||
$selector = uuid();
|
||||
$validator = generate_password(32);
|
||||
$hashed_validator = password_hash($validator, PASSWORD_DEFAULT);
|
||||
$token = $selector.':'.$validator;
|
||||
$remote_address = $_SERVER['REMOTE_ADDR'] ?? '';
|
||||
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
|
||||
//save token to the user logs
|
||||
$sql = "update v_user_logs ";
|
||||
$sql .= "set remember_selector = :remember_selector, ";
|
||||
$sql .= "remember_validator = :remember_validator ";
|
||||
$sql .= "where user_log_uuid = ( ";
|
||||
$sql .= " select user_log_uuid FROM v_user_logs ";
|
||||
$sql .= " where result = 'success' ";
|
||||
$sql .= " and remote_address = :remote_address ";
|
||||
$sql .= " and user_agent = :user_agent ";
|
||||
$sql .= " and user_uuid = :user_uuid ";
|
||||
$sql .= " and timestamp > NOW() - INTERVAL '7 days' ";
|
||||
$sql .= " order by timestamp desc limit 1 ";
|
||||
$sql .= ") ";
|
||||
$parameters['remember_selector'] = $selector;
|
||||
$parameters['remember_validator'] = $hashed_validator;
|
||||
$parameters['remote_address'] = $remote_address;
|
||||
$parameters['user_agent'] = $user_agent;
|
||||
$parameters['user_uuid'] = $user['user_uuid'];
|
||||
$this->database->execute($sql, $parameters);
|
||||
unset($sql, $parameters);
|
||||
|
||||
//set the cookie
|
||||
setcookie('remember', $token, [
|
||||
'expires' => strtotime('+7 days'),
|
||||
'path' => '/',
|
||||
'secure' => true,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
//set a session variable to indicate whether or not we are authorized
|
||||
$_SESSION['authorized'] = $authorized;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
Mark J Crane <markjcrane@fusionpbx.com>
|
||||
Portions created by the Initial Developer are Copyright (C) 2008-2024
|
||||
Portions created by the Initial Developer are Copyright (C) 2008-2026
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s):
|
||||
@@ -68,6 +68,7 @@ class plugin_database {
|
||||
$theme_background_video = (isset($background_videos[0])) ? $background_videos[0] : '';
|
||||
$login_domain_name_visible = $settings->get('login', 'domain_name_visible', false);
|
||||
$login_domain_name = $settings->get('login', 'domain_name');
|
||||
$login_remember_me = $settings->get('login', 'remember_me');
|
||||
$login_destination = $settings->get('login', 'destination');
|
||||
$users_unique = $settings->get('users', 'unique', '');
|
||||
|
||||
@@ -115,6 +116,7 @@ class plugin_database {
|
||||
$view->assign("label_username", $text['label-username']);
|
||||
$view->assign("label_password", $text['label-password']);
|
||||
$view->assign("label_domain", $text['label-domain']);
|
||||
$view->assign("label_remember_me", $text['label-remember_me']);
|
||||
$view->assign("button_login", $text['button-login']);
|
||||
|
||||
//assign default values to the template
|
||||
@@ -122,6 +124,7 @@ class plugin_database {
|
||||
$view->assign("login_destination_url", $login_destination);
|
||||
$view->assign("login_domain_name_visible", $login_domain_name_visible);
|
||||
$view->assign("login_domain_names", $login_domain_name);
|
||||
$view->assign("login_remember_me", $login_remember_me);
|
||||
$view->assign("login_password_reset_enabled", $login_password_reset_enabled);
|
||||
$view->assign("favicon", $theme_favicon);
|
||||
$view->assign("login_logo_width", $theme_login_logo_width);
|
||||
@@ -186,6 +189,9 @@ class plugin_database {
|
||||
if (isset($_REQUEST["password"])) {
|
||||
$this->password = $_REQUEST["password"];
|
||||
}
|
||||
if (isset($_POST["remember"])) {
|
||||
$_SESSION['remember'] = $_POST["remember"];
|
||||
}
|
||||
if (isset($_REQUEST["key"])) {
|
||||
$this->key = $_REQUEST["key"];
|
||||
}
|
||||
|
||||
@@ -91,6 +91,14 @@
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{if !empty($login_remember_me)}
|
||||
<div style="display: flex;">
|
||||
<span style='padding-left: 15px;'>
|
||||
<input type='checkbox' id='remember' name='remember'>
|
||||
<label for='remember' class='login_text' style='padding-bottom: 1px;'>{$label_remember_me}</label>
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
<div>
|
||||
<input type='submit' id='btn_login' class='btn' style='width: 100px; margin-top: 15px;' value='{$button_login}' /><br />
|
||||
{if !empty($login_password_reset_enabled) && $login_password_reset_enabled}
|
||||
|
||||
@@ -249,6 +249,14 @@
|
||||
$apps[$x]['default_settings'][$y]['default_setting_enabled'] = "false";
|
||||
$apps[$x]['default_settings'][$y]['default_setting_description'] = "Set the domain to use in the Password Reset link sent via email.";
|
||||
$y++;
|
||||
$apps[$x]['default_settings'][$y]['default_setting_uuid'] = "d8b18b38-8510-489a-9bba-33534550ddef";
|
||||
$apps[$x]['default_settings'][$y]['default_setting_category'] = "login";
|
||||
$apps[$x]['default_settings'][$y]['default_setting_subcategory'] = "remember_me";
|
||||
$apps[$x]['default_settings'][$y]['default_setting_name'] = "boolean";
|
||||
$apps[$x]['default_settings'][$y]['default_setting_value'] = "true";
|
||||
$apps[$x]['default_settings'][$y]['default_setting_enabled'] = "true";
|
||||
$apps[$x]['default_settings'][$y]['default_setting_description'] = "Display a Remember Me checkbox on the login box.";
|
||||
$y++;
|
||||
$apps[$x]['default_settings'][$y]['default_setting_uuid'] = "962ac32c-74ce-4cce-b1d9-89f4d921493d";
|
||||
$apps[$x]['default_settings'][$y]['default_setting_category'] = "login";
|
||||
$apps[$x]['default_settings'][$y]['default_setting_subcategory'] = "domain_name_visible";
|
||||
|
||||
@@ -93,6 +93,16 @@
|
||||
$apps[$x]['db'][$y]['fields'][$z]['search_by'] = 'true';
|
||||
$apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = '';
|
||||
$z++;
|
||||
$apps[$x]['db'][$y]['fields'][$z]['name'] = 'remember_selector';
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['pgsql'] = "uuid";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['sqlite'] = "text";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['mysql'] = "char(36)";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = "Remember me selector";
|
||||
$z++;
|
||||
$apps[$x]['db'][$y]['fields'][$z]['name'] = 'remember_validator';
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type'] = 'text';
|
||||
$apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = "Remember me validator";
|
||||
$z++;
|
||||
$apps[$x]['db'][$y]['fields'][$z]['name'] = "insert_date";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['pgsql'] = 'timestamptz';
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['sqlite'] = 'date';
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
Mark J Crane <markjcrane@fusionpbx.com>
|
||||
Portions created by the Initial Developer are Copyright (C) 2019-2024
|
||||
Portions created by the Initial Developer are Copyright (C) 2019-2026
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s):
|
||||
@@ -113,6 +113,7 @@ class user_logs {
|
||||
$array['user_logs'][0]["remote_address"] = $_SERVER['REMOTE_ADDR'];
|
||||
$array['user_logs'][0]["user_agent"] = $_SERVER['HTTP_USER_AGENT'];
|
||||
$array['user_logs'][0]["session_id"] = session_id();
|
||||
$array['user_logs'][0]["remember_token"] = $result['remember_token'];
|
||||
if ($result["authorized"]) {
|
||||
$array['user_logs'][0]["result"] = 'success';
|
||||
} else {
|
||||
|
||||
12
logout.php
12
logout.php
@@ -17,7 +17,7 @@
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
Mark J Crane <markjcrane@fusionpbx.com>
|
||||
Portions created by the Initial Developer are Copyright (C) 2008-2019
|
||||
Portions created by the Initial Developer are Copyright (C) 2008-2026
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s):
|
||||
@@ -30,6 +30,16 @@
|
||||
//use custom logout destination if set otherwise redirect to the index page
|
||||
$logout_destination = $settings->get('login', 'logout_destination', PROJECT_PATH.'/');
|
||||
|
||||
//remove remember me token
|
||||
setcookie('remember', '', time() - 3600, '/');
|
||||
$sql = "update v_user_logs ";
|
||||
$sql .= "set remember_selector = null, ";
|
||||
$sql .= "remember_validator = null ";
|
||||
$sql .= "where user_uuid = :user_uuid ";
|
||||
$parameters['user_uuid'] = $_SESSION['user_uuid'];
|
||||
$database->execute($sql, $parameters);
|
||||
unset($sql, $parameters);
|
||||
|
||||
//destroy session
|
||||
session_unset();
|
||||
session_destroy();
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
Mark J Crane <markjcrane@fusionpbx.com>
|
||||
Portions created by the Initial Developer are Copyright (C) 2008-2025
|
||||
Portions created by the Initial Developer are Copyright (C) 2008-2026
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s):
|
||||
@@ -145,7 +145,7 @@
|
||||
settings::clear_cache();
|
||||
|
||||
//if logged in, redirect to login destination
|
||||
if (!isset($_REQUEST["key"])) {
|
||||
if (!isset($_REQUEST["key"]) && !isset($_COOKIE['remember'])) {
|
||||
|
||||
//connect to the settings object
|
||||
$settings = new settings(['database' => $database, 'domain_uuid' => $domain_uuid, 'user_uuid' => $user_uuid]);
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
//set the current domain_uuid
|
||||
$domain_uuid = $_SESSION['domain_uuid'] ?? '';
|
||||
|
||||
//initialize the settigns object
|
||||
//initialize the settings object
|
||||
$settings = new settings(['database' => $database, 'domain_uuid' => $domain_uuid]);
|
||||
|
||||
//get action, if any - define, request, reset
|
||||
@@ -259,6 +259,16 @@
|
||||
$database->execute($sql, $parameters);
|
||||
unset($sql, $parameters);
|
||||
|
||||
//remove remember me token
|
||||
setcookie('remember', '', time() - 3600, '/');
|
||||
$sql = "update v_user_logs ";
|
||||
$sql .= "set remember_selector = null, ";
|
||||
$sql .= "remember_validator = null ";
|
||||
$sql .= "where username = :username ";
|
||||
$parameters['username'] = $_SESSION['valid_username'];
|
||||
$database->execute($sql, $parameters);
|
||||
unset($sql, $parameters);
|
||||
|
||||
//build the user log array
|
||||
$log_array['type'] = 'Password Changed';
|
||||
$log_array['domain_uuid'] = $_SESSION['valid_domain'];
|
||||
|
||||
Reference in New Issue
Block a user