KAuth

Polkit1Backend.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
3 SPDX-FileCopyrightText: 2009 Radek Novacek <rnovacek@redhat.com>
4 SPDX-FileCopyrightText: 2009-2010 Dario Freddi <drf@kde.org>
5 SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
6 SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
7
8 SPDX-License-Identifier: LGPL-2.1-or-later
9*/
10
11#include "Polkit1Backend.h"
12#include "kauthdebug.h"
13
14#include <KWaylandExtras>
15#include <KWindowSystem>
16
17#include <QCoreApplication>
18#include <QTimer>
19#include <qplugin.h>
20
21#include <QGuiApplication>
22#include <QWindow>
23
24#include <QDBusConnection>
25#include <QDBusConnectionInterface>
26#include <QDBusPendingCallWatcher>
27#include <QDBusPendingReply>
28
29#include <PolkitQt1/Subject>
30#include <polkitqt1-version.h>
31
32constexpr QLatin1String c_kdeAgentService{"org.kde.polkit-kde-authentication-agent-1"};
33constexpr QLatin1String c_kdeAgentPath{"/org/kde/Polkit1AuthAgent"};
34constexpr QLatin1String c_kdeAgentInterface{"org.kde.Polkit1AuthAgent"};
35
36namespace KAuth
37{
38
39Polkit1Backend::Polkit1Backend()
40 : AuthBackend()
41{
42 setCapabilities(AuthorizeFromHelperCapability | PreAuthActionCapability);
43
44 // Setup useful signals
45 connect(PolkitQt1::Authority::instance(), &PolkitQt1::Authority::configChanged, this, &KAuth::Polkit1Backend::checkForResultChanged);
46 connect(PolkitQt1::Authority::instance(), &PolkitQt1::Authority::consoleKitDBChanged, this, &KAuth::Polkit1Backend::checkForResultChanged);
47}
48
49Polkit1Backend::~Polkit1Backend()
50{
51}
52
53void Polkit1Backend::preAuthAction(const QString &action, QWindow *parentWindow)
54{
55 // If a parent was not specified, skip this
56 if (!parentWindow) {
57 qCDebug(KAUTH) << "Parent widget does not exist, skipping";
58 return;
59 }
60
61 // Check if we actually are entitled to use GUI capabilities
62 if (!qGuiApp) {
63 qCDebug(KAUTH) << "Not streaming parent as we are on a TTY application";
64 return;
65 }
66
67 // Are we running our KDE auth agent?
68 if (QDBusConnection::sessionBus().interface()->isServiceRegistered(QLatin1String("org.kde.polkit-kde-authentication-agent-1"))) {
70 KWaylandExtras::exportWindow(parentWindow);
71 connect(
72 KWaylandExtras::self(),
74 this,
75 [this, action, parentWindow](QWindow *window, const QString &handle) {
76 if (window == parentWindow) {
77 sendWindowHandle(action, handle);
78 }
79 },
81
82 // Generate and send an XDG Activation token.
83 sendActivationToken(action, parentWindow);
84 } else {
85 // Retrieve the dialog root window Id
86 const qulonglong wId = parentWindow->winId();
87
88 sendWindowHandle(action, QString::number(wId));
89
90 // Call the old method for compatibility.
91 QDBusMessage methodCall = QDBusMessage::createMethodCall(c_kdeAgentService, c_kdeAgentPath, c_kdeAgentInterface, QLatin1String("setWIdForAction"));
92 methodCall << action;
93 methodCall << wId;
94
95 // Legacy call has to be blocking, old agent doesn't handle it coming in delayed.
96 const auto reply = QDBusConnection::sessionBus().call(methodCall);
97 if (reply.type() != QDBusMessage::ReplyMessage) {
98 qWarning() << "Failed to set window id" << wId << "for" << action << reply.errorMessage();
99 }
100 }
101 } else {
102 qCDebug(KAUTH) << "KDE polkit agent appears too old or not registered on the bus";
103 }
104}
105
106void Polkit1Backend::sendWindowHandle(const QString &action, const QString &handle)
107{
108 // Send it over the bus to our agent
109 QDBusMessage methodCall = QDBusMessage::createMethodCall(c_kdeAgentService, c_kdeAgentPath, c_kdeAgentInterface, QLatin1String("setWindowHandleForAction"));
110 methodCall << action;
111 methodCall << handle;
112
113 const auto reply = QDBusConnection::sessionBus().asyncCall(methodCall);
114 auto *watcher = new QDBusPendingCallWatcher(reply, this);
115 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, handle, action] {
116 watcher->deleteLater();
117
118 QDBusPendingReply<> reply = *watcher;
119 if (reply.isError()) {
120 qCWarning(KAUTH) << "Failed to set window handle" << handle << "for" << action << reply.error().message();
121 }
122 });
123}
124
125void Polkit1Backend::sendActivationToken(const QString &action, QWindow *window)
126{
127 const auto requestedSerial = KWaylandExtras::lastInputSerial(window);
128 connect(
129 KWaylandExtras::self(),
131 this,
132 [this, requestedSerial, action](quint32 serial, const QString &token) {
133 if (serial != requestedSerial || token.isEmpty()) {
134 return;
135 }
136 QDBusMessage methodCall =
137 QDBusMessage::createMethodCall(c_kdeAgentService, c_kdeAgentPath, c_kdeAgentInterface, QLatin1String("setActivationTokenForAction"));
138 methodCall << action;
139 methodCall << token;
140
141 const auto reply = QDBusConnection::sessionBus().asyncCall(methodCall);
142 auto *watcher = new QDBusPendingCallWatcher(reply, this);
143 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, token, action] {
144 watcher->deleteLater();
145
146 QDBusPendingReply<> reply = *watcher;
147 if (reply.isError()) {
148 qCWarning(KAUTH) << "Failed to set activation token" << token << "for" << action << reply.error().message();
149 }
150 });
151 },
153 KWaylandExtras::requestXdgActivationToken(window, requestedSerial, {});
154}
155
156Action::AuthStatus Polkit1Backend::authorizeAction(const QString &action)
157{
158 Q_UNUSED(action)
159 // Always return Yes here, we'll authorize inside isCallerAuthorized
160 return Action::AuthorizedStatus;
161}
162
163void Polkit1Backend::setupAction(const QString &action)
164{
165 m_cachedResults[action] = actionStatus(action);
166}
167
168Action::AuthStatus Polkit1Backend::actionStatus(const QString &action)
169{
171 auto authority = PolkitQt1::Authority::instance();
172 PolkitQt1::Authority::Result r = authority->checkAuthorizationSync(action, subject, PolkitQt1::Authority::None);
173
174 if (authority->hasError()) {
175 qCDebug(KAUTH) << "Encountered error while checking action status, error code:" << authority->lastError() << authority->errorDetails();
176 authority->clearError();
177 return Action::InvalidStatus;
178 }
179
180 switch (r) {
182 return Action::AuthorizedStatus;
185 return Action::DeniedStatus;
186 default:
187 return Action::AuthRequiredStatus;
188 }
189}
190
191QByteArray Polkit1Backend::callerID() const
192{
194}
195
196bool Polkit1Backend::isCallerAuthorized(const QString &action, const QByteArray &callerID, const QVariantMap &details)
197{
200 QMap<QString, QString> polkit1Details;
201 for (auto it = details.cbegin(); it != details.cend(); ++it) {
202 polkit1Details.insert(it.key(), it.value().toString());
203 }
204
206 QEventLoop e;
208 result = _result;
209 e.quit();
210 });
211
212#if POLKITQT1_IS_VERSION(0, 113, 0)
213 authority->checkAuthorizationWithDetails(action, subject, PolkitQt1::Authority::AllowUserInteraction, polkit1Details);
214#else
216#endif
217 e.exec();
218
219 if (authority->hasError()) {
220 qCDebug(KAUTH) << "Encountered error while checking authorization, error code:" << authority->lastError() << authority->errorDetails();
221 authority->clearError();
222 }
223
224 switch (result) {
226 return true;
227 default:
228 return false;
229 }
230}
231
232void Polkit1Backend::checkForResultChanged()
233{
234 for (auto it = m_cachedResults.begin(); it != m_cachedResults.end(); ++it) {
235 const QString action = it.key();
236 if (it.value() != actionStatus(action)) {
237 *it = actionStatus(action);
238 Q_EMIT actionStatusChanged(action, *it);
239 }
240 }
241}
242
243QVariantMap Polkit1Backend::backendDetails(const DetailsMap &details)
244{
245 QVariantMap backendDetails;
246 for (auto it = details.cbegin(); it != details.cend(); ++it) {
247 switch (it.key()) {
248 case Action::AuthDetail::DetailMessage:
249 backendDetails.insert(QStringLiteral("polkit.message"), it.value());
250 break;
251 case Action::AuthDetail::DetailOther:
252 default:
253 backendDetails.insert(QStringLiteral("other_details"), it.value());
254 break;
255 }
256 }
257 return backendDetails;
258}
259
260} // namespace Auth
261
262#include "Polkit1Backend.moc"
void xdgActivationTokenArrived(int serial, const QString &token)
static Q_INVOKABLE void requestXdgActivationToken(QWindow *win, uint32_t serial, const QString &app_id)
void windowExported(QWindow *window, const QString &handle)
static Q_INVOKABLE void exportWindow(QWindow *window)
static Q_INVOKABLE quint32 lastInputSerial(QWindow *window)
static bool isPlatformWayland()
ErrorCode lastError() const
static Authority * instance(PolkitAuthority *authority=nullptr)
const QString errorDetails() const
void checkAuthorizationWithDetails(const QString &actionId, const Subject &subject, AuthorizationFlags flags, const DetailsMap &details)
void checkAuthorization(const QString &actionId, const Subject &subject, AuthorizationFlags flags)
bool hasError() const
void checkAuthorizationFinished(PolkitQt1::Authority::Result)
QDBusPendingCall asyncCall(const QDBusMessage &message, int timeout) const const
QString baseService() const const
QDBusMessage call(const QDBusMessage &message, QDBus::CallMode mode, int timeout) const const
QDBusConnection sessionBus()
QDBusConnection systemBus()
QDBusMessage createMethodCall(const QString &service, const QString &path, const QString &interface, const QString &method)
QString errorMessage() const const
void finished(QDBusPendingCallWatcher *self)
bool isError() const const
int exec(ProcessEventsFlags flags)
void quit()
iterator insert(const Key &key, const T &value)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString number(double n, char format, int precision)
QByteArray toUtf8() const const
SingleShotConnection
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
WId winId() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:10 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.