Skip to content
Open
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 @@ -79,6 +79,7 @@ if (top !== self) top.location.replace(self.location.href);
@if(ViewBag.is_offline_mode_enabled == true){
<script src="/scripts/service-worker-manager.js"></script>
<script src="/scripts/offline/offline-status-manager.js" type="text/javascript"></script>
<script src="/scripts/offline/offline-session-validator.js" type="text/javascript"></script>
}

<script src="/scripts/shared/logout-handler.js"></script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,154 @@ function is_offline_mode() {
}

// Expose the offline session validator API to the global scope
// Helper function to get session data for validation
async function getSessionDataForValidation() {
// Try service worker first
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
try {
const sessionData = await requestSessionDataFromServiceWorker();
if (sessionData) {
return sessionData;
}
} catch (error) {
console.warn('Failed to get session data from service worker:', error);
}
}

// Fallback to global variable
if (window.mmria_offline_session_data) {
return window.mmria_offline_session_data;
}

// Last resort: localStorage
try {
const storedData = localStorage.getItem('mmria_offline_session');
if (storedData) {
return JSON.parse(storedData);
}
} catch (error) {
console.warn('localStorage not available for session data:', error);
}

return null;
}

/**
* Validates the current offline session
* @returns {boolean} - Whether the offline session is valid
*/
function validateOfflineSession() {
try {
const sessionData = localStorage.getItem('mmria_offline_session');
if (!sessionData) return false;

const session = JSON.parse(sessionData);
return session && session.user_id;
} catch (error) {
console.error('Error validating offline session:', error);
return false;
}
}

/**
* Clears offline session state but preserves session data for re-login
*/
function clearOfflineSessionData() {
localStorage.setItem('has_active_offline_session', 'false');

// Clear all case data from localStorage for security
try {
const keysToRemove = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith('case_')) {
keysToRemove.push(key);
}
}

// Remove all case-related keys
keysToRemove.forEach(key => {
localStorage.removeItem(key);
});

// Clear the case index as well
localStorage.removeItem('case_index');

console.log(`Cleared ${keysToRemove.length} case data items from localStorage on logout`);
} catch (error) {
console.error('Error clearing case data on logout:', error);
}

// Notify service worker of status change
if (window.ServiceWorkerManager) {
window.ServiceWorkerManager.notifyActiveOfflineSessionChange();
}
}

/**
* Logs offline events for audit purposes
* @param {string} action - The action being performed
* @param {string} message - Description of the action
*/
function logOfflineEvent(action, message) {
try {
const events = JSON.parse(localStorage.getItem('offline_audit_log') || '[]');
const sessionData = JSON.parse(localStorage.getItem('mmria_offline_session') || '{}');

events.push({
action,
message,
timestamp: new Date().toISOString(),
user: sessionData.user_id || 'unknown',
sessionId: localStorage.getItem('offline_session_id') || 'unknown'
});

// Keep only last 100 events to prevent localStorage overflow
if (events.length > 100) {
events.splice(0, events.length - 100);
}

localStorage.setItem('offline_audit_log', JSON.stringify(events));
} catch (error) {
console.error('Error logging offline event:', error);
}
}

/**
* Checks if user has a valid offline session and redirects to login if not
* Should be called on page load for protected routes
*/
function checkOfflineSessionAndRedirect() {
const isOfflineMode = localStorage.getItem('is_offline') === 'true';
const hasActiveSession = localStorage.getItem('has_active_offline_session') === 'true';

if (isOfflineMode && !hasActiveSession) {
console.log('Session validation failed: No active offline session, redirecting to offline login');

// Log the event for audit purposes
logOfflineEvent('session_invalid', 'User attempted to access protected route without valid session');

// Clear any potentially stale data
clearOfflineSessionData();

// Redirect to offline login
window.location.href = '/Account/OfflineLogin';
return false; // Session invalid
}

return true; // Session valid or not in offline mode
}

window.OfflineSessionValidator = {
validateKey: validate_offline_key,
getSessionData: get_offline_session_data,
validateKeyAgainstSession: validate_offline_key_against_session,
isOfflineMode: is_offline_mode
isOfflineMode: is_offline_mode,
getSessionDataForValidation: getSessionDataForValidation,
validateOfflineSession: validateOfflineSession,
clearOfflineSessionData: clearOfflineSessionData,
logOfflineEvent: logOfflineEvent,
checkOfflineSessionAndRedirect: checkOfflineSessionAndRedirect
};

console.log('Offline Session Validator module loaded');
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ async function encryptCasesOnOfflineLogout(enteredKey) {
return;
}

const sessionData = await getSessionDataForValidation();
const sessionData = await window.OfflineSessionValidator.getSessionDataForValidation();
if (!sessionData || !sessionData.keySalt) return;

// Send password to service worker to derive and set key
Expand All @@ -39,38 +39,6 @@ async function encryptCasesOnOfflineLogout(enteredKey) {
}
}

// Helper function to get session data for validation
async function getSessionDataForValidation() {
// Try service worker first
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
try {
const sessionData = await requestSessionDataFromServiceWorker();
if (sessionData) {
return sessionData;
}
} catch (error) {
console.warn('Failed to get session data from service worker:', error);
}
}

// Fallback to global variable
if (window.mmria_offline_session_data) {
return window.mmria_offline_session_data;
}

// Last resort: localStorage
try {
const storedData = localStorage.getItem('mmria_offline_session');
if (storedData) {
return JSON.parse(storedData);
}
} catch (error) {
console.warn('localStorage not available for session data:', error);
}

return null;
}

async function handleLogout(event) {
const isOffline = localStorage.getItem('is_offline') === 'true';

Expand All @@ -79,9 +47,9 @@ async function handleLogout(event) {
event.preventDefault();

// Validate offline session before logout
if (validateOfflineSession()) {
if (window.OfflineSessionValidator.validateOfflineSession()) {
// Log the logout event for audit purposes
logOfflineEvent('logout', 'User logged out in offline mode');
window.OfflineSessionValidator.logOfflineEvent('logout', 'User logged out in offline mode');

// Show a brief message before redirecting
//showLogoutMessage('Logging out of offline mode...');
Expand All @@ -91,7 +59,7 @@ async function handleLogout(event) {
//await encryptCasesOnOfflineLogout("sssDDDkkk@@@2d");

// Clear all offline data securely
await clearOfflineSessionData();
await window.OfflineSessionValidator.clearOfflineSessionData();

// Small delay to show message, then redirect
setTimeout(() => {
Expand All @@ -105,87 +73,6 @@ async function handleLogout(event) {
return true;
}

/**
* Validates the current offline session
* @returns {boolean} - Whether the offline session is valid
*/
function validateOfflineSession() {
try {
const sessionData = localStorage.getItem('mmria_offline_session');
if (!sessionData) return false;

const session = JSON.parse(sessionData);
return session && session.user_id;
} catch (error) {
console.error('Error validating offline session:', error);
return false;
}
}

/**
* Clears offline session state but preserves session data for re-login
*/
function clearOfflineSessionData() {
localStorage.setItem('has_active_offline_session', 'false');

// Clear all case data from localStorage for security
try {
const keysToRemove = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith('case_')) {
keysToRemove.push(key);
}
}

// Remove all case-related keys
keysToRemove.forEach(key => {
localStorage.removeItem(key);
});

// Clear the case index as well
localStorage.removeItem('case_index');

console.log(`Cleared ${keysToRemove.length} case data items from localStorage on logout`);
} catch (error) {
console.error('Error clearing case data on logout:', error);
}

// Notify service worker of status change
if (window.ServiceWorkerManager) {
window.ServiceWorkerManager.notifyActiveOfflineSessionChange();
}
}

/**
* Logs offline events for audit purposes
* @param {string} action - The action being performed
* @param {string} message - Description of the action
*/
function logOfflineEvent(action, message) {
try {
const events = JSON.parse(localStorage.getItem('offline_audit_log') || '[]');
const sessionData = JSON.parse(localStorage.getItem('mmria_offline_session') || '{}');

events.push({
action,
message,
timestamp: new Date().toISOString(),
user: sessionData.user_id || 'unknown',
sessionId: localStorage.getItem('offline_session_id') || 'unknown'
});

// Keep only last 100 events to prevent localStorage bloat
if (events.length > 100) {
events.splice(0, events.length - 100);
}

localStorage.setItem('offline_audit_log', JSON.stringify(events));
} catch (error) {
console.error('Error logging offline event:', error);
}
}

/**
* Shows a logout message to the user
* @param {string} message - Message to display
Expand Down Expand Up @@ -215,31 +102,6 @@ function showLogoutMessage(message) {
}, 3000);
}

/**
* Checks if user has a valid offline session and redirects to login if not
* Should be called on page load for protected routes
*/
function checkOfflineSessionAndRedirect() {
const isOfflineMode = localStorage.getItem('is_offline') === 'true';
const hasActiveSession = localStorage.getItem('has_active_offline_session') === 'true';

if (isOfflineMode && !hasActiveSession) {
console.log('Session validation failed: No active offline session, redirecting to offline login');

// Log the event for audit purposes
logOfflineEvent('session_invalid', 'User attempted to access protected route without valid session');

// Clear any potentially stale data
clearOfflineSessionData();

// Redirect to offline login
window.location.href = '/Account/OfflineLogin';
return false; // Session invalid
}

return true; // Session valid or not in offline mode
}

/**
* Initialize logout handlers and session validation when DOM is ready
* This provides an alternative to inline onsubmit handlers
Expand All @@ -261,12 +123,12 @@ document.addEventListener('DOMContentLoaded', function() {
const currentPath = window.location.pathname.toLowerCase();
if (currentPath.includes('/case') || currentPath.includes('/home')) {
console.log('Protected route detected, validating offline session...');
checkOfflineSessionAndRedirect();
window.OfflineSessionValidator.checkOfflineSessionAndRedirect();
}
});

// Make functions globally available
window.handleLogout = handleLogout;
window.clearOfflineSessionData = clearOfflineSessionData;
window.validateOfflineSession = validateOfflineSession;
window.checkOfflineSessionAndRedirect = checkOfflineSessionAndRedirect;
window.clearOfflineSessionData = window.OfflineSessionValidator.clearOfflineSessionData;
window.validateOfflineSession = window.OfflineSessionValidator.validateOfflineSession;
window.checkOfflineSessionAndRedirect = window.OfflineSessionValidator.checkOfflineSessionAndRedirect;