Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ public function beforeExecuteRoute(Dispatcher $dispatcher)
$this->view->session_username = !empty($_SESSION['Username']) ? $_SESSION['Username'] : '(unknown)';
$this->view->system_hostname = $cnf->object()->system->hostname;
$this->view->system_domain = $cnf->object()->system->domain;
$this->view->session_timeout = $this->session_timeout;

if (isset($this->view->menuBreadcrumbs[0]['name'])) {
$output = [];
Expand Down
18 changes: 14 additions & 4 deletions src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ class ControllerRoot extends Controller
*/
protected $langcode = 'en_US';

/**
* @var int session timeout in seconds
*/
public $session_timeout = 14400;

/**
* set system language according to configuration
*/
Expand Down Expand Up @@ -130,9 +135,9 @@ public function doAuth()
{
$cnf = Config::getInstance()->object();
if (!empty($cnf->system->webgui->session_timeout)) {
$session_timeout = $cnf->system->webgui->session_timeout * 60;
$this->session_timeout = $cnf->system->webgui->session_timeout * 60;
} else {
$session_timeout = 14400;
$this->session_timeout = 14400;
}
$redirect_uri = "/?url=" . $_SERVER['REQUEST_URI'];
if ($this->session->has("Username") == false) {
Expand All @@ -147,7 +152,7 @@ public function doAuth()
return false;
} elseif (
$this->session->has("last_access")
&& $this->session->get("last_access") < (time() - $session_timeout)
&& $this->session->get("last_access") < (time() - $this->session_timeout)
) {
// session expired / cleanup session data
$this->getLogger('audit')->notice(sprintf(
Expand All @@ -164,7 +169,12 @@ public function doAuth()

$this->setLang();

$this->session->set("last_access", time());
$is_ajax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
$is_post = $_SERVER['REQUEST_METHOD'] === 'POST';

if (!$is_ajax || $is_post) {
$this->session->set("last_access", time());
}

// Authorization using legacy acl structure
$acl = new ACL();
Expand Down
3 changes: 3 additions & 0 deletions src/opnsense/mvc/app/views/layouts/default.volt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

<script>
// setup default scripting after page loading.
window.sessionTimeout = {{ session_timeout|default('14400') }};
$( document ).ready(function() {
// hook into jquery ajax requests to ensure csrf handling.
$.ajaxSetup({
Expand Down Expand Up @@ -97,6 +98,7 @@
addMultiSelectClearUI();
initGlobalOpenShortcuts();

initSessionTimeout();
updateSystemStatus();

// Register collapsible table headers
Expand Down Expand Up @@ -333,6 +335,7 @@
searchColumns: "{{ lang._('Search columns') }}",
expand: "{{ lang._('Click to expand/collapse cell') }}"
});
localStorage.setItem('opnsense_auth_sync', Date.now().toString());
</script>

</body>
Expand Down
74 changes: 74 additions & 0 deletions src/opnsense/www/js/opnsense.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,3 +370,77 @@ function download_content(payload, filename, file_type) {
}
});
}

const ACTIVITY_KEY = 'opn_last_activity';
let sessionThrottleTimer = null;

let lastPingTime = Date.now();

/**
* Resets the shared local storage timestamp.
*/
function resetSessionTimeout() {
if (!sessionThrottleTimer) {
sessionThrottleTimer = setTimeout(function() {
let now = Date.now();
localStorage.setItem(ACTIVITY_KEY, now.toString());
sessionThrottleTimer = null;

if ((now - lastPingTime) > 300000) {
lastPingTime = now;
$.post('/api/core/menu/search');
}
}, 1000);
}
}

/**
* Initializes the auto-logout tracking mechanism.
*/
function initSessionTimeout() {
if (typeof window.sessionTimeout !== 'number' || window.sessionTimeout <= 0) {
return;
}

const sessionTimeoutMs = window.sessionTimeout * 1000;

if (!localStorage.getItem(ACTIVITY_KEY)) {
localStorage.setItem(ACTIVITY_KEY, Date.now().toString());
}

$(document).on('mousemove keydown click scroll touchstart', function(e) {
if (e.originalEvent === undefined || e.originalEvent.isTrusted === false) {
return;
}
resetSessionTimeout();
});

// Explicit logout synchronization
$(document).on('click', 'a[href*="logout"]', function() {
localStorage.setItem('opnsense_logout', Date.now().toString());
localStorage.removeItem(ACTIVITY_KEY);
});

window.addEventListener('storage', function(e) {
if (e.key === 'opnsense_logout' || (e.key === ACTIVITY_KEY && !e.newValue)) {
window.location.reload();
}
});

setInterval(function() {
let activeKeyStr = localStorage.getItem(ACTIVITY_KEY);

if (!activeKeyStr) {
window.location.reload();
return;
}

let lastActive = parseInt(activeKeyStr, 10);
let timeIdleMs = Date.now() - lastActive;

if (timeIdleMs > (sessionTimeoutMs + 10000)) {
localStorage.removeItem(ACTIVITY_KEY);
window.location.reload();
}
}, 5000);
}
14 changes: 13 additions & 1 deletion src/www/authgui.inc
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,19 @@ function display_login_form($Login_Error)
<?php if (get_themed_filename('/js/theme.js', true)) : ?>
<script src="<?= cache_safe(get_themed_filename('/js/theme.js')) ?>"></script>
<?php endif ?>

<script>
window.addEventListener('storage', function(e) {
if (e.key === 'opnsense_auth_sync' && e.newValue) {
let urlParams = new URLSearchParams(window.location.search);
let redirectUrl = urlParams.get('url');
if (redirectUrl && redirectUrl.startsWith('/')) {
window.location.href = redirectUrl;
} else {
window.location.reload();
}
}
});
</script>
</head>
<body class="page-login">

Expand Down
7 changes: 7 additions & 0 deletions src/www/head.inc
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ $pagetitle .= html_safe(sprintf(' | %s.%s', $config['system']['hostname'], $conf

<script>
//<![CDATA[
window.sessionTimeout = <?= !empty($config['system']['webgui']['session_timeout']) ? $config['system']['webgui']['session_timeout'] * 60 : 14400 ?>;

$( document ).ready(function() {
$('[data-toggle="tooltip"]').tooltip();
$("input").not("[autocomplete]").attr("autocomplete","off");
Expand Down Expand Up @@ -246,10 +248,15 @@ $pagetitle .= html_safe(sprintf(' | %s.%s', $config['system']['hostname'], $conf
});
// convert input form tables for better mobile experience
hook_stacked_form_tables(".opnsense_standard_table_form");

initSessionTimeout();
});
//]]>
</script>
<?php if (get_themed_filename('/js/theme.js', true)): ?>
<script src="<?= cache_safe(get_themed_filename('/js/theme.js')) ?>"></script>
<?php endif ?>
<script>
localStorage.setItem('opnsense_auth_sync', Date.now().toString());
</script>
</head>