1/*
2 SPDX-FileCopyrightText: 2009-2012 Dario Freddi <drf@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-or-later
5*/
6
7#include "executejob.h"
8
9#include "BackendsManager.h"
10#include "kauthdebug.h"
11
12#include <QCoreApplication>
13#include <QEventLoop>
14#include <QGuiApplication>
15#include <QHash>
16#include <QTimer>
17#include <QWindow>
18
19namespace KAuth
20{
21class ExecuteJobPrivate
22{
23 Q_DECLARE_TR_FUNCTIONS(KAuth::ExecuteJob)
24
25public:
26 explicit ExecuteJobPrivate(ExecuteJob *parent)
27 : q(parent)
28 {
29 }
30
31 ExecuteJob *q;
32 Action action;
33
34 Action::ExecutionMode mode;
35 QVariantMap data;
36
37 void doExecuteAction();
38 void doAuthorizeAction();
39 void actionPerformedSlot(const QString &action, const ActionReply &reply);
40 void progressStepSlot(const QString &action, int i);
41 void progressStepSlot(const QString &action, const QVariantMap &data);
42 void statusChangedSlot(const QString &action, KAuth::Action::AuthStatus status);
43};
44
45static QWindow *parentWindow(const Action &action)
46{
47 QWindow *window = action.parentWindow();
48 if (!window && qGuiApp) {
49 window = qGuiApp->focusWindow();
50 if (!window) {
51 if (const auto windows = qGuiApp->topLevelWindows(); !windows.isEmpty()) {
52 window = windows.first();
53 }
54 }
55 if (window) {
56 // Don't warn for background services with no windows.
57 qCWarning(KAUTH) << "Action" << action.name() << "has no parentWindow, assuming" << window;
58 }
59 }
60 return window;
61}
62
63ExecuteJob::ExecuteJob(const Action &action, Action::ExecutionMode mode, QObject *parent)
64 : KJob(parent)
65 , d(new ExecuteJobPrivate(this))
66{
67 d->action = action;
68 d->mode = mode;
69
70 HelperProxy *helper = BackendsManager::helperProxy();
71
72 connect(sender: helper, signal: &KAuth::HelperProxy::actionPerformed, context: this, slot: [this](const QString &action, const ActionReply &reply) {
73 d->actionPerformedSlot(action, reply);
74 });
75 connect(sender: helper, signal: &KAuth::HelperProxy::progressStep, context: this, slot: [this](const QString &action, int i) {
76 d->progressStepSlot(action, i);
77 });
78 connect(sender: helper, signal: &KAuth::HelperProxy::progressStepData, context: this, slot: [this](const QString &action, const QVariantMap &data) {
79 d->progressStepSlot(action, data);
80 });
81
82 connect(sender: BackendsManager::authBackend(), signal: &KAuth::AuthBackend::actionStatusChanged, context: this, slot: [this](const QString &action, Action::AuthStatus status) {
83 d->statusChangedSlot(action, status);
84 });
85}
86
87ExecuteJob::~ExecuteJob() = default;
88
89Action ExecuteJob::action() const
90{
91 return d->action;
92}
93
94QVariantMap ExecuteJob::data() const
95{
96 return d->data;
97}
98
99void ExecuteJob::start()
100{
101 if (!d->action.isValid()) {
102 qCWarning(KAUTH) << "Tried to start an invalid action: " << d->action.name();
103 ActionReply reply(ActionReply::InvalidActionError);
104 reply.setErrorDescription(tr(s: "Tried to start an invalid action"));
105 d->actionPerformedSlot(action: d->action.name(), reply);
106 return;
107 }
108
109 switch (d->mode) {
110 case Action::ExecuteMode:
111 QTimer::singleShot(interval: 0, receiver: this, slot: [this]() {
112 d->doExecuteAction();
113 });
114 break;
115 case Action::AuthorizeOnlyMode:
116 QTimer::singleShot(interval: 0, receiver: this, slot: [this]() {
117 d->doAuthorizeAction();
118 });
119 break;
120 default: {
121 ActionReply reply(ActionReply::InvalidActionError);
122 reply.setErrorDescription(tr(s: "Unknown execution mode chosen"));
123 d->actionPerformedSlot(action: d->action.name(), reply);
124 break;
125 }
126 }
127}
128
129bool ExecuteJob::kill(KillVerbosity verbosity)
130{
131 BackendsManager::helperProxy()->stopAction(action: d->action.name(), helperID: d->action.helperId());
132 KJob::kill(verbosity);
133 return true;
134}
135
136void ExecuteJobPrivate::doExecuteAction()
137{
138 // If this action authorizes from the client, let's do it now
139 if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::AuthorizeFromClientCapability) {
140 if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::PreAuthActionCapability) {
141 BackendsManager::authBackend()->preAuthAction(action: action.name(), parent: parentWindow(action));
142 }
143
144 Action::AuthStatus s = BackendsManager::authBackend()->authorizeAction(action: action.name());
145
146 if (s == Action::AuthorizedStatus) {
147 if (action.hasHelper()) {
148 BackendsManager::helperProxy()->executeAction(action: action.name(), helperID: action.helperId(), details: action.detailsV2(), arguments: action.arguments(), timeout: action.timeout());
149 } else {
150 // Done
151 actionPerformedSlot(action: action.name(), reply: ActionReply::SuccessReply());
152 }
153 } else {
154 // Abort if authorization fails
155 switch (s) {
156 case Action::DeniedStatus:
157 actionPerformedSlot(action: action.name(), reply: ActionReply::AuthorizationDeniedReply());
158 break;
159 case Action::InvalidStatus:
160 actionPerformedSlot(action: action.name(), reply: ActionReply::InvalidActionReply());
161 break;
162 case Action::UserCancelledStatus:
163 actionPerformedSlot(action: action.name(), reply: ActionReply::UserCancelledReply());
164 break;
165 default: {
166 ActionReply r(ActionReply::BackendError);
167 r.setErrorDescription(tr(sourceText: "Unknown status for the authentication procedure"));
168 actionPerformedSlot(action: action.name(), reply: r);
169 break;
170 }
171 }
172 }
173 } else if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::AuthorizeFromHelperCapability) {
174 if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::PreAuthActionCapability) {
175 BackendsManager::authBackend()->preAuthAction(action: action.name(), parent: parentWindow(action));
176 }
177 if (!action.hasHelper()) {
178 ActionReply r(ActionReply::InvalidActionReply());
179 r.setErrorDescription(tr(sourceText: "The current backend only allows helper authorization, but this action does not have a helper."));
180 actionPerformedSlot(action: action.name(), reply: r);
181 return;
182 }
183 BackendsManager::helperProxy()->executeAction(action: action.name(), helperID: action.helperId(), details: action.detailsV2(), arguments: action.arguments(), timeout: action.timeout());
184 } else {
185 // There's something totally wrong here
186 ActionReply r(ActionReply::BackendError);
187 r.setErrorDescription(tr(sourceText: "The backend does not specify how to authorize"));
188 actionPerformedSlot(action: action.name(), reply: r);
189 }
190}
191
192void ExecuteJobPrivate::doAuthorizeAction()
193{
194 // Check the status first
195 Action::AuthStatus s = action.status();
196 if (s == Action::AuthRequiredStatus) {
197 // Let's check what to do
198 if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::AuthorizeFromClientCapability) {
199 // In this case we can actually try an authorization
200 if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::PreAuthActionCapability) {
201 BackendsManager::authBackend()->preAuthAction(action: action.name(), parent: parentWindow(action));
202 }
203
204 s = BackendsManager::authBackend()->authorizeAction(action: action.name());
205 } else if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::AuthorizeFromHelperCapability) {
206 // In this case, just throw out success, as the auth will take place later
207 s = Action::AuthorizedStatus;
208 } else {
209 // This should never, never happen
210 ActionReply r(ActionReply::BackendError);
211 r.setErrorDescription(tr(sourceText: "The backend does not specify how to authorize"));
212 actionPerformedSlot(action: action.name(), reply: r);
213 }
214 }
215
216 // Return based on the current status
217 if (s == Action::AuthorizedStatus) {
218 actionPerformedSlot(action: action.name(), reply: ActionReply::SuccessReply());
219 } else {
220 actionPerformedSlot(action: action.name(), reply: ActionReply::AuthorizationDeniedReply());
221 }
222}
223
224void ExecuteJobPrivate::actionPerformedSlot(const QString &taction, const ActionReply &reply)
225{
226 if (taction == action.name()) {
227 if (reply.failed()) {
228 q->setError(reply.errorCode());
229 q->setErrorText(reply.errorDescription());
230 } else {
231 data = reply.data();
232 }
233
234 q->emitResult();
235 }
236}
237
238void ExecuteJobPrivate::progressStepSlot(const QString &taction, int i)
239{
240 if (taction == action.name()) {
241 q->setPercent(i);
242 }
243}
244
245void ExecuteJobPrivate::progressStepSlot(const QString &taction, const QVariantMap &data)
246{
247 if (taction == action.name()) {
248 Q_EMIT q->newData(data);
249 }
250}
251
252void ExecuteJobPrivate::statusChangedSlot(const QString &taction, Action::AuthStatus status)
253{
254 if (taction == action.name()) {
255 Q_EMIT q->statusChanged(status);
256 }
257}
258
259} // namespace Auth
260
261#include "moc_executejob.cpp"
262

source code of kauth/src/executejob.cpp