diff --git a/openconnect_sso/browser/webengine_process.py b/openconnect_sso/browser/webengine_process.py
index cb49acc..865f95c 100644
--- a/openconnect_sso/browser/webengine_process.py
+++ b/openconnect_sso/browser/webengine_process.py
@@ -8,10 +8,11 @@
import attr
import pkg_resources
import structlog
+import html as html_utils
from PyQt6.QtCore import QUrl, QTimer, pyqtSlot, Qt
from PyQt6.QtNetwork import QNetworkCookie, QNetworkProxy
-from PyQt6.QtWebEngineCore import QWebEngineScript, QWebEngineProfile, QWebEnginePage
+from PyQt6.QtWebEngineCore import QWebEngineScript, QWebEngineProfile, QWebEnginePage, QWebEngineClientCertificateSelection
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import QApplication, QWidget, QSizePolicy, QVBoxLayout
@@ -19,7 +20,6 @@
app = None
-profile = None
logger = structlog.get_logger("webengine")
@@ -69,7 +69,6 @@ async def get_state_async(self):
def run(self):
# To work around funky GC conflicts with C++ code by ensuring QApplication terminates last
global app
- global profile
signal.signal(signal.SIGTERM, on_sigterm)
signal.signal(signal.SIGINT, signal.SIG_DFL)
@@ -80,7 +79,6 @@ def run(self):
if self.display_mode == config.DisplayMode.HIDDEN:
argv += ["-platform", "minimal"]
app = QApplication(argv)
- profile = QWebEngineProfile("openconnect-sso")
if self.proxy:
parsed = urlparse(self.proxy)
@@ -102,7 +100,7 @@ def ignore():
pass
force_python_execution.timeout.connect(ignore)
- web = WebBrowser(cfg.auto_fill_rules, self._states.put, profile)
+ web = WebBrowser(cfg.auto_fill_rules, self._states.put)
startup_info = self._commands.get()
logger.info("Browser started", startup_info=startup_info)
@@ -124,7 +122,6 @@ async def wait(self):
def on_sigterm(signum, frame):
- global profile
logger.info("Terminate requested.")
# Force flush cookieStore to disk. Without this hack the cookieStore may
# not be synced at all if the browser lives only for a short amount of
@@ -133,7 +130,7 @@ def on_sigterm(signum, frame):
# See: https://github.com/qutebrowser/qutebrowser/commit/8d55d093f29008b268569cdec28b700a8c42d761
cookie = QNetworkCookie()
- profile.cookieStore().deleteCookie(cookie)
+ QWebEngineProfile.defaultProfile().cookieStore().deleteCookie(cookie)
# Give some time to actually save cookies
exit_timer = QTimer(app)
@@ -142,15 +139,14 @@ def on_sigterm(signum, frame):
class WebBrowser(QWebEngineView):
- def __init__(self, auto_fill_rules, on_update, profile):
+ def __init__(self, auto_fill_rules, on_update):
super().__init__()
self._on_update = on_update
self._auto_fill_rules = auto_fill_rules
- page = QWebEnginePage(profile, self)
- self.setPage(page)
cookie_store = self.page().profile().cookieStore()
cookie_store.cookieAdded.connect(self._on_cookie_added)
self.page().loadFinished.connect(self._on_load_finished)
+ self.page().selectClientCertificate.connect(self._on_select_client_certificate)
def createWindow(self, type):
if type == QWebEnginePage.WebDialog:
@@ -197,6 +193,23 @@ def _on_load_finished(self, success):
logger.debug("Page loaded", url=url)
self._on_update(Url(url))
+ def _on_select_client_certificate(self, selection):
+ logger.info("Select first client Certificate")
+ url = self.page().url().toString()
+ certificate = selection.certificates()[0]
+ text = ('Subject: {subj}
'
+ 'Issuer: {issuer}
'
+ 'Serial: {serial}'.format(
+ subj=html_utils.escape(certificate.subjectDisplayName()),
+ issuer=html_utils.escape(certificate.issuerDisplayName()),
+ serial=bytes(certificate.serialNumber()).decode('ascii')))
+ if len(selection.certificates()) > 1:
+ text += ('
Note: Multiple matching certificates '
+ 'were found, but certificate selection is not '
+ 'implemented yet!')
+ logger.info(text)
+ selection.select(certificate)
+ self.load(QUrl(url))
class WebPopupWindow(QWidget):