File indexing completed on 2024-03-24 15:25:01

0001 /*
0002     SPDX-FileCopyrightText: 2009-2012 Dario Freddi <drf@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "executejob.h"
0008 
0009 #include "BackendsManager.h"
0010 #include "kauthdebug.h"
0011 
0012 #include <QCoreApplication>
0013 #include <QEventLoop>
0014 #include <QHash>
0015 #include <QTimer>
0016 
0017 namespace KAuth
0018 {
0019 class ExecuteJobPrivate
0020 {
0021     Q_DECLARE_TR_FUNCTIONS(KAuth::ExecuteJob)
0022 
0023 public:
0024     explicit ExecuteJobPrivate(ExecuteJob *parent)
0025         : q(parent)
0026     {
0027     }
0028 
0029     ExecuteJob *q;
0030     Action action;
0031 
0032     Action::ExecutionMode mode;
0033     QVariantMap data;
0034 
0035     void doExecuteAction();
0036     void doAuthorizeAction();
0037     void actionPerformedSlot(const QString &action, const ActionReply &reply);
0038     void progressStepSlot(const QString &action, int i);
0039     void progressStepSlot(const QString &action, const QVariantMap &data);
0040     void statusChangedSlot(const QString &action, KAuth::Action::AuthStatus status);
0041 };
0042 
0043 static QHash<QString, ExecuteJob *> s_watchers;
0044 
0045 ExecuteJob::ExecuteJob(const Action &action, Action::ExecutionMode mode, QObject *parent)
0046     : KJob(parent)
0047     , d(new ExecuteJobPrivate(this))
0048 {
0049     d->action = action;
0050     d->mode = mode;
0051 
0052     HelperProxy *helper = BackendsManager::helperProxy();
0053 
0054     connect(helper, &KAuth::HelperProxy::actionPerformed, this, [this](const QString &action, const ActionReply &reply) {
0055         d->actionPerformedSlot(action, reply);
0056     });
0057     connect(helper, &KAuth::HelperProxy::progressStep, this, [this](const QString &action, int i) {
0058         d->progressStepSlot(action, i);
0059     });
0060     connect(helper, &KAuth::HelperProxy::progressStepData, this, [this](const QString &action, const QVariantMap &data) {
0061         d->progressStepSlot(action, data);
0062     });
0063 
0064     connect(BackendsManager::authBackend(), &KAuth::AuthBackend::actionStatusChanged, this, [this](const QString &action, Action::AuthStatus status) {
0065         d->statusChangedSlot(action, status);
0066     });
0067 }
0068 
0069 ExecuteJob::~ExecuteJob() = default;
0070 
0071 Action ExecuteJob::action() const
0072 {
0073     return d->action;
0074 }
0075 
0076 QVariantMap ExecuteJob::data() const
0077 {
0078     return d->data;
0079 }
0080 
0081 void ExecuteJob::start()
0082 {
0083     if (!d->action.isValid()) {
0084         qCWarning(KAUTH) << "Tried to start an invalid action: " << d->action.name();
0085         ActionReply reply(ActionReply::InvalidActionError);
0086         reply.setErrorDescription(tr("Tried to start an invalid action"));
0087         d->actionPerformedSlot(d->action.name(), reply);
0088         return;
0089     }
0090 
0091     switch (d->mode) {
0092     case Action::ExecuteMode:
0093         QTimer::singleShot(0, this, [this]() {
0094             d->doExecuteAction();
0095         });
0096         break;
0097     case Action::AuthorizeOnlyMode:
0098         QTimer::singleShot(0, this, [this]() {
0099             d->doAuthorizeAction();
0100         });
0101         break;
0102     default: {
0103         ActionReply reply(ActionReply::InvalidActionError);
0104         reply.setErrorDescription(tr("Unknown execution mode chosen"));
0105         d->actionPerformedSlot(d->action.name(), reply);
0106         break;
0107     }
0108     }
0109 }
0110 
0111 bool ExecuteJob::kill(KillVerbosity verbosity)
0112 {
0113     BackendsManager::helperProxy()->stopAction(d->action.name(), d->action.helperId());
0114     KJob::kill(verbosity);
0115     return true;
0116 }
0117 
0118 void ExecuteJobPrivate::doExecuteAction()
0119 {
0120     // If this action authorizes from the client, let's do it now
0121     if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::AuthorizeFromClientCapability) {
0122         if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::PreAuthActionCapability) {
0123             BackendsManager::authBackend()->preAuthAction(action.name(), action.parentWidget());
0124         }
0125 
0126         Action::AuthStatus s = BackendsManager::authBackend()->authorizeAction(action.name());
0127 
0128         if (s == Action::AuthorizedStatus) {
0129             if (action.hasHelper()) {
0130                 BackendsManager::helperProxy()->executeAction(action.name(), action.helperId(), action.detailsV2(), action.arguments(), action.timeout());
0131             } else {
0132                 // Done
0133                 actionPerformedSlot(action.name(), ActionReply::SuccessReply());
0134             }
0135         } else {
0136             // Abort if authorization fails
0137             switch (s) {
0138             case Action::DeniedStatus:
0139                 actionPerformedSlot(action.name(), ActionReply::AuthorizationDeniedReply());
0140                 break;
0141             case Action::InvalidStatus:
0142                 actionPerformedSlot(action.name(), ActionReply::InvalidActionReply());
0143                 break;
0144             case Action::UserCancelledStatus:
0145                 actionPerformedSlot(action.name(), ActionReply::UserCancelledReply());
0146                 break;
0147             default: {
0148                 ActionReply r(ActionReply::BackendError);
0149                 r.setErrorDescription(tr("Unknown status for the authentication procedure"));
0150                 actionPerformedSlot(action.name(), r);
0151                 break;
0152             }
0153             }
0154         }
0155     } else if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::AuthorizeFromHelperCapability) {
0156         if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::PreAuthActionCapability) {
0157             BackendsManager::authBackend()->preAuthAction(action.name(), action.parentWidget());
0158         }
0159         if (!action.hasHelper()) {
0160             ActionReply r(ActionReply::InvalidActionReply());
0161             r.setErrorDescription(tr("The current backend only allows helper authorization, but this action does not have a helper."));
0162             actionPerformedSlot(action.name(), r);
0163             return;
0164         }
0165         BackendsManager::helperProxy()->executeAction(action.name(), action.helperId(), action.detailsV2(), action.arguments(), action.timeout());
0166     } else {
0167         // There's something totally wrong here
0168         ActionReply r(ActionReply::BackendError);
0169         r.setErrorDescription(tr("The backend does not specify how to authorize"));
0170         actionPerformedSlot(action.name(), r);
0171     }
0172 }
0173 
0174 void ExecuteJobPrivate::doAuthorizeAction()
0175 {
0176     // Check the status first
0177     Action::AuthStatus s = action.status();
0178     if (s == Action::AuthRequiredStatus) {
0179         // Let's check what to do
0180         if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::AuthorizeFromClientCapability) {
0181             // In this case we can actually try an authorization
0182             if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::PreAuthActionCapability) {
0183                 BackendsManager::authBackend()->preAuthAction(action.name(), action.parentWidget());
0184             }
0185 
0186             s = BackendsManager::authBackend()->authorizeAction(action.name());
0187         } else if (BackendsManager::authBackend()->capabilities() & KAuth::AuthBackend::AuthorizeFromHelperCapability) {
0188             // In this case, just throw out success, as the auth will take place later
0189             s = Action::AuthorizedStatus;
0190         } else {
0191             // This should never, never happen
0192             ActionReply r(ActionReply::BackendError);
0193             r.setErrorDescription(tr("The backend does not specify how to authorize"));
0194             actionPerformedSlot(action.name(), r);
0195         }
0196     }
0197 
0198     // Return based on the current status
0199     if (s == Action::AuthorizedStatus) {
0200         actionPerformedSlot(action.name(), ActionReply::SuccessReply());
0201     } else {
0202         actionPerformedSlot(action.name(), ActionReply::AuthorizationDeniedReply());
0203     }
0204 }
0205 
0206 void ExecuteJobPrivate::actionPerformedSlot(const QString &taction, const ActionReply &reply)
0207 {
0208     if (taction == action.name()) {
0209         if (reply.failed()) {
0210             q->setError(reply.errorCode());
0211             q->setErrorText(reply.errorDescription());
0212         } else {
0213             data = reply.data();
0214         }
0215 
0216         q->emitResult();
0217     }
0218 }
0219 
0220 void ExecuteJobPrivate::progressStepSlot(const QString &taction, int i)
0221 {
0222     if (taction == action.name()) {
0223         q->setPercent(i);
0224     }
0225 }
0226 
0227 void ExecuteJobPrivate::progressStepSlot(const QString &taction, const QVariantMap &data)
0228 {
0229     if (taction == action.name()) {
0230         Q_EMIT q->newData(data);
0231     }
0232 }
0233 
0234 void ExecuteJobPrivate::statusChangedSlot(const QString &taction, Action::AuthStatus status)
0235 {
0236     if (taction == action.name()) {
0237         Q_EMIT q->statusChanged(status);
0238     }
0239 }
0240 
0241 } // namespace Auth
0242 
0243 #include "moc_executejob.cpp"