diff options
| author | Simon Rettberg | 2025-09-24 15:04:29 +0200 |
|---|---|---|
| committer | Simon Rettberg | 2025-09-24 15:04:29 +0200 |
| commit | 2630737572ab46e3a3f3e8c2a0fb0cdde7b30a7b (patch) | |
| tree | 5da613169b85cffd9cee0ddad1e01b00b3bf87c2 /src/webview.cpp | |
| parent | Fix some deprecations and compiler nags (diff) | |
| download | slxgreeter-2630737572ab46e3a3f3e8c2a0fb0cdde7b30a7b.tar.gz slxgreeter-2630737572ab46e3a3f3e8c2a0fb0cdde7b30a7b.tar.xz slxgreeter-2630737572ab46e3a3f3e8c2a0fb0cdde7b30a7b.zip | |
Shibboleth: Add support for checking required entitlements
TODO: Add same feature to QRCode login
Diffstat (limited to 'src/webview.cpp')
| -rw-r--r-- | src/webview.cpp | 121 |
1 files changed, 86 insertions, 35 deletions
diff --git a/src/webview.cpp b/src/webview.cpp index 1e4a3a4..c4995bd 100644 --- a/src/webview.cpp +++ b/src/webview.cpp @@ -2,7 +2,6 @@ #include "global.h" -#include <QAction> #include <QMessageBox> #include <QTimer> #include <QUrlQuery> @@ -22,6 +21,9 @@ #include <QWebEngineUrlRequestInterceptor> #include <QWebEngineScriptCollection> +#include "global.h" +#include "settings.h" + // Add: custom page to catch console messages from JS class ActivityPage : public QWebEnginePage { @@ -273,38 +275,51 @@ static QRegularExpression urlListToRegExp(const QStringList &list) void WebView::installJsInjectionScript() { - // If we should filter the list of allowed IdPs, inject the list into - // the page as JavaScript - QString str = Global::getCombinedIdpWhitelist().replace( - QRegularExpression("[^\\w. /:-]", QRegularExpression::UseUnicodePropertiesOption), - QString()); - QWebEngineScript script; - script.setName(QStringLiteral("slxIdpFilterInjector")); - script.setInjectionPoint(QWebEngineScript::DocumentCreation); - script.setWorldId(QWebEngineScript::MainWorld); - script.setRunsOnSubFrames(true); - script.setSourceCode(QStringLiteral("var slxIdpFilter ='") + str + QStringLiteral("';")); - page()->scripts().insert(script); - - // Inject activity listeners to signal user interaction back to C++ - QWebEngineScript activityScript; - activityScript.setName(QStringLiteral("slxUserActivity")); - activityScript.setInjectionPoint(QWebEngineScript::DocumentCreation); - activityScript.setWorldId(QWebEngineScript::MainWorld); - activityScript.setRunsOnSubFrames(true); - activityScript.setSourceCode(QStringLiteral( - "(function(){\n" - " var last=0; function ping(){\n" - " var now=Date.now(); if(now-last<2000) return; last=now;\n" - " try{console.debug('LOG_USER_ACTIVITY');}catch(e){}\n" - " }\n" - " var evts=['mousedown','mouseup','click','keydown','keyup','wheel','touchstart','touchend','scroll'];\n" - " evts.forEach(function(ev){\n" - " window.addEventListener(ev, ping, {passive:true, capture:true});\n" - " });\n" - "})();\n" - )); - page()->scripts().insert(activityScript); + QString idpSpaceList; + { + // If we should filter the list of allowed IdPs, inject the list into + // the page as JavaScript + static QRegularExpression REGEX("[^\\w./:@-]", QRegularExpression::UseUnicodePropertiesOption); + const auto& map = Global::getCombinedIdpWhitelist(); + QSet<QString> idpList; + for (auto it = map.constBegin(); it != map.constEnd(); ++it) { + for (QString s : it.value().toStringList()) { + idpList.insert(s.replace(REGEX, QString())); + } + } + idpSpaceList = idpList.values().join(' '); + } + { + QWebEngineScript script; + script.setName(QStringLiteral("slxIdpFilterInjector")); + script.setInjectionPoint(QWebEngineScript::DocumentCreation); + script.setWorldId(QWebEngineScript::MainWorld); + script.setRunsOnSubFrames(true); + script.setSourceCode(QStringLiteral("var slxIdpFilter = '") + + idpSpaceList + QStringLiteral("';")); + page()->scripts().insert(script); + } + { + // Inject activity listeners to signal user interaction back to C++ + QWebEngineScript activityScript; + activityScript.setName(QStringLiteral("slxUserActivity")); + activityScript.setInjectionPoint(QWebEngineScript::DocumentCreation); + activityScript.setWorldId(QWebEngineScript::MainWorld); + activityScript.setRunsOnSubFrames(true); + activityScript.setSourceCode(QStringLiteral( + "(function(){\n" + " var last=0; function ping(){\n" + " var now=Date.now(); if(now-last<2000) return; last=now;\n" + " try{console.debug('LOG_USER_ACTIVITY');}catch(e){}\n" + " }\n" + " var evts=['mousedown','mouseup','click','keydown','keyup','wheel','touchstart','touchend','scroll'];\n" + " evts.forEach(function(ev){\n" + " window.addEventListener(ev, ping, {passive:true, capture:true});\n" + " });\n" + "})();\n" + )); + page()->scripts().insert(activityScript); + } } void WebView::contextMenuEvent(QContextMenuEvent* ev) @@ -315,7 +330,7 @@ void WebView::contextMenuEvent(QContextMenuEvent* ev) // Find and remove/hide the "View source" action QAction* viewSource = page()->action(QWebEnginePage::ViewSource); if (viewSource) { - viewSource->setVisible(false); // or: menu->removeAction(viewSource); + viewSource->setVisible(false); viewSource->setEnabled(false); } @@ -337,7 +352,9 @@ void WebView::evaluateAuthDom() " pass: t('#bwlp-password')," " err: t('#bwlp-error')," " hash: t('#bwlp-hash')," - " adminToken: t('#bwlp-cow-token')" + " adminToken: t('#bwlp-cow-token')," + " idp: t('#bwlp-idp')," + " entitlement: t('#bwlp-entitlement')" " };" "})();" ); @@ -349,6 +366,8 @@ void WebView::evaluateAuthDom() const QString err = map.value(QStringLiteral("err")).toString(); const QString hash = map.value(QStringLiteral("hash")).toString(); const QString adminToken = map.value(QStringLiteral("adminToken")).toString(); + const QString idp = map.value(QStringLiteral("idp")).toString(); + const QStringList userEntitlements = map.value(QStringLiteral("entitlement")).toString().split(';'); if (!user.isEmpty() && !pass.isEmpty() && !hash.isEmpty()) { if (hash != QCryptographicHash::hash(_token.toLatin1(), QCryptographicHash::Md5).toHex()) { @@ -356,6 +375,38 @@ void WebView::evaluateAuthDom() emit triggerReset(QStringLiteral("Invalid Hash")); return; } + const auto &idpMap = Global::getCombinedIdpWhitelist(); + if (!idpMap.isEmpty()) { + bool ok = false; + for (auto it = idpMap.constBegin(); it != idpMap.constEnd(); ++it) { + if (it.key().isEmpty()) { + // An "anything goes" list, no restrictions + if (it.value().toStringList().contains(idp)) { + ok = true; + break; + } + } else if (it.value().toStringList().contains(idp)) { + // IdP in list, so if the entitlements match up, allow + ok = true; + // Break up key into individual entitlements, make sure the user has them all + QStringList requiredEntitlements = it.key().split(';'); + for (const QString &n : requiredEntitlements) { + if (!userEntitlements.contains(n)) { + ok = false; + break; + } + } + if (ok) + break; + } + } + // Disallow if entitlement requirements are not met + if (!ok) { + QMessageBox::warning(this, QStringLiteral("Invalid entitlements"), Settings::shibEntitlementMissingText()); + emit triggerReset(QStringLiteral("Invalid entitlements")); + return; + } + } if (Global::isValidShibCreds(user, pass)) { if (!adminToken.isEmpty()) { Global::writeCowToken(user, adminToken); |
