Skip to content
Open
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
33 changes: 23 additions & 10 deletions openconnect_sso/browser/webengine_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@
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

from openconnect_sso import config


app = None
profile = None
logger = structlog.get_logger("webengine")


Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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:
Expand Down Expand Up @@ -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 = ('<b>Subject:</b> {subj}<br/>'
'<b>Issuer:</b> {issuer}<br/>'
'<b>Serial:</b> {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 += ('<br/><br/><b>Note:</b> 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):
Expand Down