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

source code of kauth/src/executejob.cpp