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"