File indexing completed on 2024-04-28 09:31:16

0001 /*  This file is part of the KDE project
0002     SPDX-FileCopyrightText: 2009 Jaroslav Reznik <jreznik@redhat.com>
0003     SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include <QDBusConnection>
0009 #include <QDebug>
0010 #include <QGuiApplication>
0011 #include <QQmlEngine>
0012 
0013 #include <KLocalizedString>
0014 #include <KWindowSystem>
0015 #include <KX11Extras>
0016 
0017 #include <PolkitQt1/ActionDescription>
0018 #include <PolkitQt1/Agent/Session>
0019 #include <PolkitQt1/Details>
0020 #include <PolkitQt1/Identity>
0021 #include <PolkitQt1/Subject>
0022 #include <QDebug>
0023 
0024 #include "IdentitiesModel.h"
0025 #include "QuickAuthDialog.h"
0026 #include "policykitlistener.h"
0027 #include "polkit1authagentadaptor.h"
0028 
0029 PolicyKitListener::PolicyKitListener(QObject *parent)
0030     : Listener(parent)
0031     , m_inProgress(false)
0032     , m_selectedUser(nullptr)
0033 {
0034     (void)new Polkit1AuthAgentAdaptor(this);
0035 
0036     if (!QDBusConnection::sessionBus().registerObject("/org/kde/Polkit1AuthAgent",
0037                                                       this,
0038                                                       QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportScriptableProperties
0039                                                           | QDBusConnection::ExportAdaptors)) {
0040         qWarning() << "Could not initiate DBus helper!";
0041     }
0042 
0043     qmlRegisterType<IdentitiesModel>("org.kde.polkitkde", 1, 0, "IdentitiesModel");
0044     qmlRegisterUncreatableType<PolkitQt1::ActionDescription>("org.kde.polkitkde", 1, 0, "ActionDescription", "nope!");
0045 
0046     qDebug() << "Listener online";
0047 }
0048 
0049 PolicyKitListener::~PolicyKitListener()
0050 {
0051 }
0052 
0053 void PolicyKitListener::setWIdForAction(const QString &action, qulonglong wID)
0054 {
0055     // For compatibility.
0056     setWindowHandleForAction(action, QString::number(wID));
0057 }
0058 
0059 void PolicyKitListener::setWindowHandleForAction(const QString &action, const QString &handle)
0060 {
0061     m_windowHandles[action] = handle;
0062 
0063     handleParentWindow(action, handle);
0064 }
0065 
0066 void PolicyKitListener::setActivationTokenForAction(const QString &action, const QString &token)
0067 {
0068     if (KWindowSystem::isPlatformWayland()) {
0069         // On X we just forceActivateWindow, no need to store the token.
0070         m_activationTokens[action] = token;
0071         handleWaylandActivation(action, token);
0072     }
0073 }
0074 
0075 void PolicyKitListener::initiateAuthentication(const QString &actionId,
0076                                                const QString &message,
0077                                                const QString &iconName,
0078                                                const PolkitQt1::Details &details,
0079                                                const QString &cookie,
0080                                                const PolkitQt1::Identity::List &identities,
0081                                                PolkitQt1::Agent::AsyncResult *result)
0082 {
0083     // The auth action might set any random old icon; all we really want here is a nice
0084     // generic "lock" icon, so we hardcode it and ignore the icon from the auth action.
0085     Q_UNUSED(iconName);
0086 
0087     qDebug() << "Initiating authentication";
0088 
0089     if (m_inProgress) {
0090         result->setError(i18n("Another client is already authenticating, please try again later."));
0091         result->setCompleted();
0092         qDebug() << "Another client is already authenticating, please try again later.";
0093         return;
0094     }
0095 
0096     if (identities.isEmpty()) {
0097         result->setError(i18nc("Error response when polkit calls us with an empty list of identities", "No user to authenticate as. Please check your system configuration."));
0098         result->setCompleted();
0099         qWarning() << "No user to authenticate as. Please check your system configuration.";
0100         return;
0101     }
0102 
0103     m_identities = identities;
0104     m_cookie = cookie;
0105     m_result = result;
0106     m_session.clear();
0107 
0108     m_inProgress = true;
0109 
0110     const QString parentHandle = m_windowHandles.value(actionId);
0111     const QString activationToken = m_activationTokens.value(actionId);
0112 
0113     m_dialog = new QuickAuthDialog(actionId, message, details, identities);
0114 
0115     if (!parentHandle.isEmpty()) {
0116         handleParentWindow(actionId, parentHandle);
0117     }
0118 
0119     connect(m_dialog.data(), SIGNAL(okClicked()), SLOT(dialogAccepted()));
0120     connect(m_dialog.data(), SIGNAL(rejected()), SLOT(dialogCanceled()));
0121 
0122     m_dialog->show();
0123 
0124     if (KWindowSystem::isPlatformWayland()) {
0125         if (!activationToken.isEmpty()) {
0126             handleWaylandActivation(actionId, activationToken);
0127         }
0128     } else if (KWindowSystem::isPlatformX11()) {
0129         KX11Extras::forceActiveWindow(m_dialog->windowHandle()->winId());
0130     }
0131 
0132     if (identities.length() == 1) {
0133         m_selectedUser = identities[0];
0134     } else {
0135         m_selectedUser = m_dialog.data()->adminUserSelected();
0136     }
0137 
0138     m_numTries = 0;
0139     tryAgain();
0140 }
0141 
0142 void PolicyKitListener::handleParentWindow(const QString &action, const QString &handle)
0143 {
0144     if (!m_dialog || m_dialog->actionId() != action) {
0145         return;
0146     }
0147 
0148     KWindowSystem::setMainWindow(m_dialog->windowHandle(), handle);
0149 }
0150 
0151 void PolicyKitListener::handleWaylandActivation(const QString &action, const QString &token)
0152 {
0153     if (!m_dialog || m_dialog->actionId() != action) {
0154         return;
0155     }
0156 
0157     qputenv("XDG_ACTIVATION_TOKEN", token.toUtf8());
0158     m_dialog->windowHandle()->requestActivate();
0159 }
0160 
0161 void PolicyKitListener::tryAgain()
0162 {
0163     qDebug() << "Trying again";
0164     m_wasCancelled = false;
0165 
0166     // We will create new session only when some user is selected
0167     if (m_selectedUser.isValid()) {
0168         m_session = new Session(m_selectedUser, m_cookie, m_result);
0169         // clang-format off
0170         connect(m_session.data(), SIGNAL(request(QString,bool)), this, SLOT(request(QString,bool)));
0171         connect(m_session.data(), SIGNAL(completed(bool)), this, SLOT(completed(bool)));
0172         connect(m_session.data(), SIGNAL(showError(QString)), this, SLOT(showError(QString)));
0173         connect(m_session.data(), SIGNAL(showInfo(QString)), this, SLOT(showInfo(QString)));
0174         // clang-format on
0175 
0176         m_session.data()->initiate();
0177     }
0178 }
0179 
0180 void PolicyKitListener::finishObtainPrivilege()
0181 {
0182     qDebug() << "Finishing obtaining privileges";
0183 
0184     // Number of tries increase only when some user is selected
0185     if (m_selectedUser.isValid()) {
0186         m_numTries++;
0187     }
0188 
0189     if (!m_gainedAuthorization && !m_wasCancelled && !m_dialog.isNull()) {
0190         m_dialog.data()->authenticationFailure();
0191 
0192         if (m_numTries < 3) {
0193             m_session.data()->deleteLater();
0194 
0195             tryAgain();
0196             return;
0197         }
0198     }
0199 
0200     if (!m_session.isNull()) {
0201         m_session.data()->result()->setCompleted();
0202     } else {
0203         m_result->setCompleted();
0204     }
0205     m_session.data()->deleteLater();
0206 
0207     if (!m_dialog.isNull()) {
0208         m_dialog.data()->hide();
0209         m_dialog.data()->deleteLater();
0210     }
0211 
0212     m_inProgress = false;
0213 
0214     qDebug() << "Finish obtain authorization:" << m_gainedAuthorization;
0215 }
0216 
0217 bool PolicyKitListener::initiateAuthenticationFinish()
0218 {
0219     qDebug() << "Finishing authentication";
0220     return true;
0221 }
0222 
0223 void PolicyKitListener::cancelAuthentication()
0224 {
0225     qDebug() << "Cancelling authentication";
0226 
0227     m_wasCancelled = true;
0228     finishObtainPrivilege();
0229 }
0230 
0231 void PolicyKitListener::request(const QString &request, bool echo)
0232 {
0233     Q_UNUSED(echo);
0234     qDebug() << "Request: " << request;
0235 }
0236 
0237 void PolicyKitListener::completed(bool gainedAuthorization)
0238 {
0239     qDebug() << "Completed: " << gainedAuthorization;
0240 
0241     m_gainedAuthorization = gainedAuthorization;
0242 
0243     finishObtainPrivilege();
0244 }
0245 
0246 void PolicyKitListener::showError(const QString &text)
0247 {
0248     qDebug() << "Error: " << text;
0249     if (!m_dialog.isNull()) {
0250         m_dialog.data()->showError(text);
0251     }
0252 }
0253 
0254 void PolicyKitListener::showInfo(const QString &text)
0255 {
0256     qDebug() << "Info: " << text;
0257     if (!m_dialog.isNull()) {
0258         m_dialog.data()->showInfo(text);
0259     }
0260 }
0261 
0262 void PolicyKitListener::dialogAccepted()
0263 {
0264     qDebug() << "Dialog accepted";
0265 
0266     if (!m_session.isNull() && !m_dialog.isNull()) {
0267         m_session.data()->setResponse(m_dialog.data()->password());
0268     }
0269 }
0270 
0271 void PolicyKitListener::dialogCanceled()
0272 {
0273     qDebug() << "Dialog cancelled";
0274 
0275     m_wasCancelled = true;
0276     if (!m_session.isNull()) {
0277         m_session.data()->cancel();
0278     }
0279 
0280     finishObtainPrivilege();
0281 }
0282 
0283 void PolicyKitListener::userSelected(const PolkitQt1::Identity &identity)
0284 {
0285     m_selectedUser = identity;
0286     // If some user is selected we must destroy existing session
0287     if (!m_session.isNull()) {
0288         m_session.data()->deleteLater();
0289     }
0290     tryAgain();
0291 }
0292 
0293 #include "moc_policykitlistener.cpp"