feat: enhance web engine view with profile isolation and add cache clearing functionality
Some checks failed
Tests & Quality Checks / Test on Python 3.11 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.12 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.11-1 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.12-1 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.10 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.11-2 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.12-2 (push) Has been cancelled
Tests & Quality Checks / Build Artifacts (push) Has been cancelled
Tests & Quality Checks / Build Artifacts-1 (push) Has been cancelled

This commit is contained in:
claudi 2026-03-03 10:22:14 +01:00
parent ba0594c260
commit 705969cdba
5 changed files with 136 additions and 50 deletions

View file

@ -394,8 +394,10 @@ class MainWindow(QMainWindow):
else:
logger.warning(f"Window icon not found at {icon_path}")
# Create web engine view
self.web_view = RestrictedWebEngineView(config.allowed_urls)
# Create web engine view with URL for profile isolation
self.web_view = RestrictedWebEngineView(
allowed_urls=config.allowed_urls, webapp_url=config.webapp_url
)
# Enable the main window and web view to receive drag events
self.setAcceptDrops(True)
@ -1249,6 +1251,11 @@ class MainWindow(QMainWindow):
check_updates_action.setToolTip("Check for Updates")
check_updates_action.triggered.connect(self._on_manual_check_for_updates)
# Clear cache button on the right
clear_cache_action = toolbar.addAction("🗑️")
clear_cache_action.setToolTip("Clear Cache and Cookies")
clear_cache_action.triggered.connect(self._clear_cache_and_cookies)
# Log file button on the right
log_action = toolbar.addAction("📋")
log_action.setToolTip("Open Log File")
@ -1321,6 +1328,34 @@ class MainWindow(QMainWindow):
# Show the dialog
self.checking_dialog.show()
def _clear_cache_and_cookies(self) -> None:
"""Clear web view cache and cookies.
Useful for clearing authentication tokens or cached data from previous
sessions. Also disconnects and reconnects the page to ensure clean state.
"""
logger.info("Clearing cache and cookies")
try:
# Clear cache and cookies in the web view profile
self.web_view.clear_cache_and_cookies()
# Show confirmation message
QMessageBox.information(
self,
"Cache Cleared",
"Browser cache and cookies have been cleared successfully.\n\n"
"You may need to reload the page or restart the application for changes to take effect.",
)
logger.info("Cache and cookies cleared successfully")
except Exception as e:
logger.error(f"Failed to clear cache and cookies: {e}")
QMessageBox.warning(
self,
"Error",
f"Failed to clear cache and cookies: {str(e)}",
)
def _show_about_dialog(self) -> None:
"""Show About dialog with version and information."""
from PySide6.QtWidgets import QMessageBox

View file

@ -1,6 +1,7 @@
"""Restricted web view with URL whitelist enforcement for Kiosk-mode."""
import fnmatch
import hashlib
import logging
from pathlib import Path
from typing import List, Optional, Union
@ -13,9 +14,6 @@ from PySide6.QtWebEngineWidgets import QWebEngineView
logger = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class CustomWebEnginePage(QWebEnginePage):
"""Custom page that handles new window requests for downloads."""
@ -108,19 +106,26 @@ class RestrictedWebEngineView(QWebEngineView):
If allowed_urls is empty, no restrictions are applied.
If allowed_urls is not empty, only matching URLs are loaded in the view.
Non-matching URLs open in the system default browser.
Each webapp_url gets an isolated profile to prevent cache corruption
from old domains affecting new domains.
"""
def __init__(self, allowed_urls: Optional[List[str]] = None):
def __init__(self, allowed_urls: Optional[List[str]] = None, webapp_url: Optional[str] = None):
"""Initialize the restricted web view.
Args:
allowed_urls: List of allowed URL patterns (empty = no restriction)
Patterns support wildcards: *.example.com, localhost, etc.
webapp_url: The web application URL for profile isolation. If provided,
creates a unique profile per domain to avoid cache corruption.
"""
super().__init__()
self.allowed_urls = allowed_urls or []
self.webapp_url = webapp_url
# Create persistent profile for cookie and session storage
# Profile is unique per domain to prevent cache corruption
self.profile = self._create_persistent_profile()
# Use custom page for better download handling with persistent profile
@ -141,6 +146,9 @@ class RestrictedWebEngineView(QWebEngineView):
authentication sessions (e.g., Microsoft login) to persist
across application restarts.
Each unique webapp domain gets its own profile to prevent
cache corruption from old domains affecting new domains.
Returns:
Configured QWebEngineProfile with persistent storage
"""
@ -149,14 +157,32 @@ class RestrictedWebEngineView(QWebEngineView):
QStandardPaths.StandardLocation.AppDataLocation
)
# Create unique profile name based on webapp_url domain
# This ensures different domains get isolated profiles
if self.webapp_url:
# Extract domain/path for profile naming
if self.webapp_url.startswith("http://") or self.webapp_url.startswith("https://"):
# Remote URL - use domain
url_obj = QUrl(self.webapp_url)
domain = url_obj.host() or "remote"
else:
# Local file - use hash of path
domain = "local"
else:
domain = "default"
# Create a stable hash of the domain
# This creates a unique but consistent profile name per domain
domain_hash = hashlib.md5(domain.encode()).hexdigest()[:8]
profile_name = f"webdrop_bridge_{domain_hash}"
# Create profile directory path
profile_path = Path(app_data_dir) / "webdrop_bridge"
profile_path = Path(app_data_dir) / "webdrop_bridge" / profile_name
profile_path.mkdir(parents=True, exist_ok=True)
# Create persistent profile with custom storage location
# Using "webdrop_bridge" as the profile name
# Note: No parent specified so we control the lifecycle
profile = QWebEngineProfile("webdrop_bridge")
# Using unique profile name so different domains have isolated caches
profile = QWebEngineProfile(profile_name)
profile.setPersistentStoragePath(str(profile_path))
# Configure persistent cookies (critical for authentication)
@ -170,7 +196,8 @@ class RestrictedWebEngineView(QWebEngineView):
# Set cache size to 100 MB
profile.setHttpCacheMaximumSize(100 * 1024 * 1024)
logger.debug(f"Created persistent profile at: {profile_path}")
logger.debug(f"Created persistent profile '{profile_name}' at: {profile_path}")
logger.debug(f"Profile domain identifier: {domain}")
logger.debug("Cookies policy: ForcePersistentCookies")
logger.debug("HTTP cache: DiskHttpCache (100 MB)")
@ -242,3 +269,19 @@ class RestrictedWebEngineView(QWebEngineView):
return True
return False
def clear_cache_and_cookies(self) -> None:
"""Clear the profile cache and cookies.
Use this method when the webapp URL changes to prevent cache corruption
from old domains affecting the new domain's authentication.
"""
logger.debug(f"Clearing cache and cookies for profile: {self.profile.storageName()}")
# Clear all cookies
self.profile.cookieStore().deleteAllCookies()
# Clear cache
self.profile.clearHttpCache()
logger.debug("Cache and cookies cleared successfully")