File indexing completed on 2024-03-24 03:55:21

0001 /*
0002     SPDX-FileCopyrightText: 2012 Dario Freddi <drf@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "BackendsManager.h"
0008 #include "TestHelper.h"
0009 
0010 #include <kauth/actionreply.h>
0011 #include <kauth/executejob.h>
0012 
0013 #include <QRandomGenerator>
0014 #include <QSignalSpy>
0015 #include <QTest>
0016 #include <QThread>
0017 #include <QTimer>
0018 
0019 #include "../src/backends/dbus/DBusHelperProxy.h"
0020 
0021 Q_DECLARE_METATYPE(KAuth::Action::AuthStatus)
0022 
0023 class HelperHandler : public QObject
0024 {
0025     Q_OBJECT
0026 public:
0027     HelperHandler();
0028 
0029 private Q_SLOTS:
0030     void init();
0031 
0032 Q_SIGNALS:
0033     void ready();
0034 
0035 private:
0036     DBusHelperProxy *m_helperProxy;
0037     TestHelper *m_helper;
0038     QThread *m_thread;
0039 };
0040 
0041 class HelperTest : public QObject
0042 {
0043     Q_OBJECT
0044 
0045 public:
0046     HelperTest(QObject *parent = nullptr)
0047         : QObject(parent)
0048     {
0049     }
0050 
0051 private Q_SLOTS:
0052     void initTestCase();
0053     void init()
0054     {
0055     }
0056 
0057     void testBasicActionExecution();
0058     void testExecuteJobSignals();
0059     void testTwoCalls();
0060     void testActionData();
0061     void testHelperFailure();
0062 
0063     void cleanup()
0064     {
0065     }
0066     void cleanupTestCase()
0067     {
0068     }
0069 
0070 Q_SIGNALS:
0071     void changeCapabilities(KAuth::AuthBackend::Capabilities capabilities);
0072 
0073 private:
0074     HelperHandler *m_handler;
0075 };
0076 
0077 HelperHandler::HelperHandler()
0078     : QObject(nullptr)
0079 {
0080     /* Hello adventurer. What you see here might hurt your eyes, but let me explain why you don't want
0081        to touch this code. We are dealing with same-process async DBus requests, and if this seems obscure
0082        to you already please give up and forget about this function.
0083        Qt's local loop optimizations at the moment make it impossible to stream an async request to a process
0084        living on the same thread. So that's what we do: we instantiate a separate helperProxy and move it to
0085        a different thread - afterwards we can do everything as if we were in a separate process.
0086 
0087        If you are wondering if this means we'll have two helper proxies, you are right my friend. But please
0088        remember that helperProxy acts both as a client and as a server, so it makes total sense.
0089 
0090        tl;dr: Don't touch this and forget the weird questions in your head: it actually works.
0091     */
0092 
0093     m_thread = new QThread(this);
0094     moveToThread(m_thread);
0095     connect(m_thread, &QThread::started, this, &HelperHandler::init);
0096     m_thread->start();
0097 }
0098 
0099 void HelperHandler::init()
0100 {
0101     qDebug() << "Initializing helper handler";
0102     qputenv("KAUTH_TEST_CALLER_ID", QDBusConnection::sessionBus().baseService().toUtf8());
0103     // Set up our Helper - of course, it is not in a separate process here so we need to copy what
0104     // HelperProxy::helperMain() does
0105     m_helperProxy = new DBusHelperProxy(QDBusConnection::sessionBus());
0106     m_helper = new TestHelper;
0107     // The timer is here just to prevent the app from crashing.
0108     QTimer *timer = new QTimer(nullptr);
0109 
0110     QVERIFY(m_helperProxy->initHelper(QLatin1String("org.kde.kf6auth.autotest")));
0111 
0112     m_helperProxy->setHelperResponder(m_helper);
0113 
0114     m_helper->setProperty("__KAuth_Helper_Shutdown_Timer", QVariant::fromValue(timer));
0115     timer->setInterval(10000);
0116     timer->start();
0117 
0118     // Make BackendsManager aware
0119     BackendsManager::setProxyForThread(m_thread, m_helperProxy);
0120 
0121     Q_EMIT ready();
0122 }
0123 
0124 void HelperTest::initTestCase()
0125 {
0126     connect(this,
0127             SIGNAL(changeCapabilities(KAuth::AuthBackend::Capabilities)),
0128             KAuth::BackendsManager::authBackend(),
0129             SLOT(setNewCapabilities(KAuth::AuthBackend::Capabilities)));
0130 
0131     qRegisterMetaType<KAuth::Action::AuthStatus>();
0132     qRegisterMetaType<KJob *>();
0133 
0134     // Set up our HelperHandler
0135     m_handler = new HelperHandler;
0136     QEventLoop eventLoop;
0137     connect(m_handler, &HelperHandler::ready, &eventLoop, &QEventLoop::quit);
0138     qDebug() << "Waiting for HelperHandler to be initialized";
0139     eventLoop.exec();
0140 }
0141 
0142 void HelperTest::testBasicActionExecution()
0143 {
0144     Q_EMIT changeCapabilities(KAuth::AuthBackend::AuthorizeFromHelperCapability);
0145 
0146     KAuth::Action action(QLatin1String("org.kde.kf6auth.autotest.standardaction"));
0147     action.setHelperId(QLatin1String("org.kde.kf6auth.autotest"));
0148     QVERIFY(action.isValid());
0149 
0150     QCOMPARE(action.status(), KAuth::Action::AuthRequiredStatus);
0151     KAuth::ExecuteJob *job = action.execute();
0152 
0153     QVERIFY(job->exec());
0154 
0155     QVERIFY(!job->error());
0156     QVERIFY(job->data().isEmpty());
0157 }
0158 
0159 void HelperTest::testExecuteJobSignals()
0160 {
0161     KAuth::Action action(QLatin1String("org.kde.kf6auth.autotest.longaction"));
0162     action.setHelperId(QLatin1String("org.kde.kf6auth.autotest"));
0163     QVERIFY(action.isValid());
0164 
0165     QCOMPARE(action.status(), KAuth::Action::AuthRequiredStatus);
0166 
0167     KAuth::ExecuteJob *job = action.execute();
0168 
0169     QSignalSpy finishedSpy(job, &KJob::result);
0170     QSignalSpy newDataSpy(job, &KAuth::ExecuteJob::newData);
0171     QSignalSpy percentSpy(job, &KJob::percentChanged);
0172     QSignalSpy statusChangedSpy(job, &KAuth::ExecuteJob::statusChanged);
0173 
0174     QVERIFY(job->exec());
0175 
0176     QCOMPARE(finishedSpy.size(), 1);
0177     QCOMPARE(qobject_cast<KAuth::ExecuteJob *>(finishedSpy.first().first().value<KJob *>()), job);
0178     QCOMPARE(statusChangedSpy.size(), 1);
0179     QCOMPARE(statusChangedSpy.first().first().value<KAuth::Action::AuthStatus>(), KAuth::Action::AuthorizedStatus);
0180     QCOMPARE(percentSpy.size(), 100);
0181     for (ulong i = 1; i <= 100; ++i) {
0182         QCOMPARE((unsigned long)percentSpy.at(i - 1).last().toLongLong(), i);
0183         QCOMPARE(qobject_cast<KAuth::ExecuteJob *>(percentSpy.at(i - 1).first().value<KJob *>()), job);
0184     }
0185     QCOMPARE(newDataSpy.size(), 1);
0186     QCOMPARE(newDataSpy.first().first().value<QVariantMap>().value(QLatin1String("Answer")).toInt(), 42);
0187 
0188     QVERIFY(!job->error());
0189     QVERIFY(job->data().isEmpty());
0190 }
0191 
0192 void HelperTest::testTwoCalls()
0193 {
0194     KAuth::Action action(QLatin1String("org.kde.kf6auth.autotest.standardaction"));
0195     action.setHelperId(QLatin1String("org.kde.kf6auth.autotest"));
0196     QVERIFY(action.isValid());
0197 
0198     QCOMPARE(action.status(), KAuth::Action::AuthRequiredStatus);
0199     KAuth::ExecuteJob *job = action.execute();
0200     QVERIFY(job->exec());
0201 
0202     job = action.execute();
0203     QVERIFY(job->exec());
0204 }
0205 
0206 void HelperTest::testActionData()
0207 {
0208     KAuth::Action action(QLatin1String("org.kde.kf6auth.autotest.echoaction"));
0209     action.setHelperId(QLatin1String("org.kde.kf6auth.autotest"));
0210 
0211     QVariantMap args;
0212     // Fill with random data (and test heavy structures while we're at it)
0213     auto *generator = QRandomGenerator::global();
0214     for (int i = 0; i < 150; ++i) {
0215         args.insert(QUuid::createUuid().toString(), generator->generate());
0216     }
0217     action.setArguments(args);
0218 
0219     QVERIFY(action.isValid());
0220 
0221     QCOMPARE(action.status(), KAuth::Action::AuthRequiredStatus);
0222     KAuth::ExecuteJob *job = action.execute();
0223 
0224     QVERIFY(job->exec());
0225 
0226     QVERIFY(!job->error());
0227     QCOMPARE(job->data(), args);
0228 }
0229 
0230 void HelperTest::testHelperFailure()
0231 {
0232     KAuth::Action action(QLatin1String("org.kde.kf6auth.autotest.failingaction"));
0233     action.setHelperId(QLatin1String("org.kde.kf6auth.autotest"));
0234     QVERIFY(action.isValid());
0235     QCOMPARE(action.status(), KAuth::Action::AuthRequiredStatus);
0236     KAuth::ExecuteJob *job = action.execute();
0237     QVERIFY(!job->exec());
0238     QVERIFY(job->error());
0239 }
0240 
0241 QTEST_MAIN(HelperTest)
0242 #include "HelperTest.moc"