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 | |
17 | namespace KAuth |
18 | { |
19 | class ExecuteJobPrivate |
20 | { |
21 | Q_DECLARE_TR_FUNCTIONS(KAuth::ExecuteJob) |
22 | |
23 | public: |
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 | |
43 | ExecuteJob::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 | |
67 | ExecuteJob::~ExecuteJob() = default; |
68 | |
69 | Action ExecuteJob::action() const |
70 | { |
71 | return d->action; |
72 | } |
73 | |
74 | QVariantMap ExecuteJob::data() const |
75 | { |
76 | return d->data; |
77 | } |
78 | |
79 | void 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 | |
109 | bool 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 | |
116 | void 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 | |
172 | void 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 | |
204 | void 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 | |
218 | void ExecuteJobPrivate::progressStepSlot(const QString &taction, int i) |
219 | { |
220 | if (taction == action.name()) { |
221 | q->setPercent(i); |
222 | } |
223 | } |
224 | |
225 | void ExecuteJobPrivate::progressStepSlot(const QString &taction, const QVariantMap &data) |
226 | { |
227 | if (taction == action.name()) { |
228 | Q_EMIT q->newData(data); |
229 | } |
230 | } |
231 | |
232 | void 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 | |