File indexing completed on 2024-06-16 06:54:13

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