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):