1 /*
2 * Copyright (C) 2016 Canonical Ltd.
3 *
4 * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
5 *
6 * This file is part of signond
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * version 2.1 as published by the Free Software Foundation.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 */
22
23 #include <QDebug>
24 #include <QDir>
25 #include <QProcess>
26 #include <QSignalSpy>
27 #include <QTemporaryDir>
28 #include <QTest>
29 #include <libqtdbusmock/DBusMock.h>
30 #include <signal.h>
31 #include <sys/types.h>
32
33 #include "SignOn/UiSessionData"
34 #include "signond/signoncommon.h"
35
36 #include "fake_signonui.h"
37
38 namespace QTest {
39 template<>
toString(const QSet<QString> & set)40 char *toString(const QSet<QString> &set)
41 {
42 QByteArray ba = "QSet<QString>(";
43 QStringList list = set.toList();
44 ba += list.join(", ");
45 ba += ")";
46 return qstrdup(ba.data());
47 }
48 } // QTest namespace
49
50 using namespace QtDBusMock;
51
52 struct SignondSecurityContextTest
53 {
54 public:
SignondSecurityContextTestSignondSecurityContextTest55 SignondSecurityContextTest(const QString &systemContext = QStringLiteral("*"), const QString &applicationContext = QStringLiteral("*")):
56 m_systemContext(systemContext),
57 m_applicationContext(applicationContext)
58 {
59 }
60
operator ==SignondSecurityContextTest61 bool operator==(const SignondSecurityContextTest &sec) const
62 {
63 if (m_systemContext != sec.m_systemContext)
64 return false;
65
66 return m_applicationContext == sec.m_applicationContext;
67 }
68
operator <SignondSecurityContextTest69 bool operator<(const SignondSecurityContextTest &sec) const
70 {
71 if (m_systemContext != sec.m_systemContext)
72 return m_systemContext < sec.m_systemContext;
73
74 return m_applicationContext < sec.m_applicationContext;
75 }
76
77 QString m_systemContext;
78 QString m_applicationContext;
79 };
80 typedef QList<SignondSecurityContextTest> SignondSecurityContextTestList;
81
operator <<(QDebug & dbg,const SignondSecurityContextTestList & securityContextList)82 QDebug operator<<(QDebug &dbg, const SignondSecurityContextTestList&securityContextList)
83 {
84 dbg << "{ ";
85 for (const SignondSecurityContextTest &secCtx : securityContextList) {
86 dbg << " ( "
87 << secCtx.m_systemContext
88 << ", "
89 << secCtx.m_applicationContext
90 << " )";
91 }
92
93 dbg << " }";
94 return dbg;
95 }
96
operator <<(QDBusArgument & argument,const SignondSecurityContextTest & securityContext)97 QDBusArgument &operator<<(QDBusArgument &argument, const SignondSecurityContextTest &securityContext)
98 {
99 argument.beginStructure();
100 argument << securityContext.m_systemContext << securityContext.m_applicationContext;
101 argument.endStructure();
102 return argument;
103 }
104
operator >>(const QDBusArgument & argument,SignondSecurityContextTest & securityContext)105 const QDBusArgument &operator>>(const QDBusArgument &argument, SignondSecurityContextTest &securityContext)
106 {
107 argument.beginStructure();
108 argument >> securityContext.m_systemContext >> securityContext.m_applicationContext;
109 argument.endStructure();
110 return argument;
111 }
112
113 Q_DECLARE_METATYPE(SignondSecurityContextTest)
114 Q_DECLARE_METATYPE(SignondSecurityContextTestList)
115
116 class SignondTest: public QObject
117 {
118 Q_OBJECT
119
120 public:
121 SignondTest();
122
123 private Q_SLOTS:
124 void initTestCase();
125 void cleanup();
126 void testStart();
127 void testQueryMethods();
128 void testQueryMechanisms_data();
129 void testQueryMechanisms();
130 void testIdentityCreation();
131 void testIdentityRemoval();
132 void testIdentityReferences();
133 void testAuthSessionMechanisms_data();
134 void testAuthSessionMechanisms();
135 void testAuthSessionProcess();
136 void testAuthSessionProcessFromOtherProcess();
137 void testAuthSessionProcessUi();
138 void testAuthSessionCloseUi_data();
139 void testAuthSessionCloseUi();
140
141 private:
142 void setupEnvironment();
143 bool signondIsRunning();
144 bool killSignond();
145 void clearBaseDir();
connection()146 const QDBusConnection &connection() { return m_dbus.sessionConnection(); }
methodCall(const QString & path,const QString & interface,const QString & method)147 QDBusMessage methodCall(const QString &path, const QString &interface,
148 const QString &method) {
149 return QDBusMessage::createMethodCall(SIGNOND_SERVICE, path,
150 interface, method);
151 }
152 bool replyIsValid(const QDBusMessage &reply);
153 QString createIdentity(const QVariantMap &data, uint *id = 0);
154
155 private:
156 QTemporaryDir m_baseDir;
157 QtDBusTest::DBusTestRunner m_dbus;
158 QtDBusMock::DBusMock m_mock;
159 FakeSignOnUi m_signonUi;
160 };
161
mapIsSuperset(const QVariantMap & superSet,const QVariantMap & set)162 static bool mapIsSuperset(const QVariantMap &superSet, const QVariantMap &set)
163 {
164 QMapIterator<QString, QVariant> it(set);
165 while (it.hasNext()) {
166 it.next();
167 if (!superSet.contains(it.key())) {
168 qDebug() << "Missing key" << it.key();
169 return false;
170 }
171
172 if (it.key() == SIGNOND_IDENTITY_INFO_ACL) {
173 QDBusArgument container = superSet.value(it.key()).value<QDBusArgument>();
174 SignondSecurityContextTestList accessControlList = qdbus_cast<SignondSecurityContextTestList>(container);
175 SignondSecurityContextTestList sub_acl = it.value().value<SignondSecurityContextTestList>();
176 if (accessControlList != sub_acl) {
177
178 qDebug() << it.key() << "is" << accessControlList << " expecting " << sub_acl;
179 return false;
180 }
181
182 continue;
183 }
184
185 if (superSet.value(it.key()) != it.value()) {
186 qDebug() << it.key() << "is" << superSet.value(it.key()) << " expecting " << it.value();
187 return false;
188 }
189 }
190
191 return true;
192 }
193
SignondTest()194 SignondTest::SignondTest():
195 QObject(0),
196 m_dbus((setupEnvironment(), TEST_DBUS_CONFIG_FILE)),
197 m_mock(m_dbus),
198 m_signonUi(&m_mock)
199 {
200 DBusMock::registerMetaTypes();
201 qDBusRegisterMetaType<SignondSecurityContextTest>();
202 qDBusRegisterMetaType<SignondSecurityContextTestList>();
203 QMetaType::registerComparators<SignondSecurityContextTest>();
204 }
205
setupEnvironment()206 void SignondTest::setupEnvironment()
207 {
208 QVERIFY(m_baseDir.isValid());
209 QByteArray baseDirPath = m_baseDir.path().toUtf8();
210 QDir baseDir(m_baseDir.path());
211
212 qunsetenv("XDG_DATA_DIR");
213 qputenv("BUILDDIR", BUILDDIR);
214 qputenv("HOME", baseDirPath);
215 qputenv("XDG_RUNTIME_DIR", baseDirPath + "/runtime-dir");
216 baseDir.mkpath("runtime-dir");
217 qputenv("SSO_STORAGE_PATH", baseDirPath);
218 qputenv("SSO_EXTENSIONS_DIR", baseDirPath + "/non-existing-dir");
219 qputenv("SSO_USE_PEER_BUS", "0");
220 qputenv("SSO_LOGGING_LEVEL", "2");
221 qputenv("SSO_PLUGINS_DIR", BUILDDIR "/src/plugins/test");
222 QByteArray ldLibraryPath = qgetenv("LD_LIBRARY_PATH");
223 qputenv("LD_LIBRARY_PATH",
224 BUILDDIR "/lib/plugins:"
225 BUILDDIR "/lib/plugins/signon-plugins-common:"
226 BUILDDIR "/lib/signond/SignOn:" +
227 ldLibraryPath);
228 QByteArray execPath = qgetenv("PATH");
229 qputenv("PATH",
230 BUILDDIR "/src/remotepluginprocess:" +
231 execPath);
232
233 /* Make sure we accidentally don't talk to the developer's signond running
234 * in the session bus */
235 qunsetenv("DBUS_SESSION_BUS_ADDRESS");
236 }
237
replyIsValid(const QDBusMessage & msg)238 bool SignondTest::replyIsValid(const QDBusMessage &msg)
239 {
240 if (msg.type() == QDBusMessage::ErrorMessage) {
241 qDebug() << "Error name:" << msg.errorName();
242 qDebug() << "Error text:" << msg.errorMessage();
243 }
244 return msg.type() == QDBusMessage::ReplyMessage;
245 }
246
signondIsRunning()247 bool SignondTest::signondIsRunning()
248 {
249 return connection().
250 interface()->isServiceRegistered(SIGNOND_SERVICE).value();
251 }
252
killSignond()253 bool SignondTest::killSignond()
254 {
255 uint pid = connection().interface()->servicePid(SIGNOND_SERVICE).value();
256 if (pid == 0) return true;
257 return kill(pid, SIGTERM) == 0 || errno == ESRCH;
258 }
259
clearBaseDir()260 void SignondTest::clearBaseDir()
261 {
262 QDir baseDir(m_baseDir.path());
263 baseDir.removeRecursively();
264 baseDir.mkpath(".");
265 }
266
createIdentity(const QVariantMap & data,uint * id)267 QString SignondTest::createIdentity(const QVariantMap &data, uint *id)
268 {
269 QDBusMessage msg = methodCall(SIGNOND_DAEMON_OBJECTPATH,
270 SIGNOND_DAEMON_INTERFACE,
271 "registerNewIdentity");
272 msg << QString("application_security_context");
273 QDBusMessage reply = connection().call(msg);
274 if (!replyIsValid(reply)) return QString();
275
276 QString objectPath =
277 reply.arguments()[0].value<QDBusObjectPath>().path();
278
279 msg = methodCall(objectPath, SIGNOND_IDENTITY_INTERFACE, "store");
280 msg << data;
281 reply = connection().call(msg);
282 if (!replyIsValid(reply)) return QString();
283
284 if (id) {
285 *id = reply.arguments()[0].toUInt();
286 }
287 return objectPath;
288 }
289
initTestCase()290 void SignondTest::initTestCase()
291 {
292 m_dbus.startServices();
293 }
294
cleanup()295 void SignondTest::cleanup()
296 {
297 if (QTest::currentTestFailed()) {
298 m_baseDir.setAutoRemove(false);
299 qDebug() << "Base dir:" << m_baseDir.path();
300 }
301 }
302
testStart()303 void SignondTest::testStart()
304 {
305 QVERIFY(killSignond());
306 QTRY_VERIFY(!signondIsRunning());
307
308 QDBusMessage reply =
309 connection().interface()->call("StartServiceByName",
310 SIGNOND_SERVICE, uint(0));
311 QVERIFY(replyIsValid(reply));
312 QTRY_VERIFY(signondIsRunning());
313 }
314
testQueryMethods()315 void SignondTest::testQueryMethods()
316 {
317 QDBusMessage msg = methodCall(SIGNOND_DAEMON_OBJECTPATH,
318 SIGNOND_DAEMON_INTERFACE,
319 "queryMethods");
320 QDBusMessage reply = connection().call(msg);
321 QVERIFY(replyIsValid(reply));
322 QCOMPARE(reply.arguments().count(), 1);
323 QStringList methods = reply.arguments()[0].toStringList();
324 QStringList expectedMethods { "ssotest", "ssotest2" };
325 QCOMPARE(methods.toSet(), expectedMethods.toSet());
326 }
327
testQueryMechanisms_data()328 void SignondTest::testQueryMechanisms_data()
329 {
330 QTest::addColumn<QString>("method");
331 QTest::addColumn<QStringList>("expectedMechanisms");
332
333 QTest::newRow("ssotest") <<
334 "ssotest" << QStringList { "mech1", "mech2", "mech3", "BLOB" };
335 QTest::newRow("ssotest2") <<
336 "ssotest2" << QStringList { "mech1", "mech2", "mech3" };
337 }
338
testQueryMechanisms()339 void SignondTest::testQueryMechanisms()
340 {
341 QFETCH(QString, method);
342 QFETCH(QStringList, expectedMechanisms);
343
344 QDBusMessage msg = methodCall(SIGNOND_DAEMON_OBJECTPATH,
345 SIGNOND_DAEMON_INTERFACE,
346 "queryMechanisms");
347 msg << method;
348 QDBusMessage reply = connection().call(msg);
349 QVERIFY(replyIsValid(reply));
350
351 QCOMPARE(reply.arguments().count(), 1);
352 QStringList mechanisms = reply.arguments()[0].toStringList();
353 QCOMPARE(mechanisms.toSet(), expectedMechanisms.toSet());
354 }
355
testIdentityCreation()356 void SignondTest::testIdentityCreation()
357 {
358 QDBusMessage msg = methodCall(SIGNOND_DAEMON_OBJECTPATH,
359 SIGNOND_DAEMON_INTERFACE,
360 "registerNewIdentity");
361 msg << QString("application_security_context");
362 QDBusMessage reply = connection().call(msg);
363 QVERIFY(replyIsValid(reply));
364
365 QCOMPARE(reply.arguments().count(), 1);
366
367 QString objectPath =
368 reply.arguments()[0].value<QDBusObjectPath>().path();
369 QVERIFY(objectPath.startsWith('/'));
370
371 SignondSecurityContextTestList acl = { SignondSecurityContextTest() };
372 QVariantMap identityData {
373 { SIGNOND_IDENTITY_INFO_USERNAME, "John" },
374 { SIGNOND_IDENTITY_INFO_CAPTION, "John's account" },
375 { SIGNOND_IDENTITY_INFO_ACL, QVariant::fromValue(acl) },
376 };
377 msg = methodCall(objectPath, SIGNOND_IDENTITY_INTERFACE, "store");
378 msg << identityData;
379 reply = connection().call(msg);
380 QVERIFY(replyIsValid(reply));
381
382 uint id = reply.arguments()[0].toUInt();
383 QVERIFY(id != 0);
384
385 /* Read the current identity info */
386 msg = methodCall(objectPath, SIGNOND_IDENTITY_INTERFACE, "getInfo");
387 reply = connection().call(msg);
388 QVERIFY(replyIsValid(reply));
389
390 QVariantMap storedData = QDBusReply<QVariantMap>(reply).value();
391 QVERIFY(mapIsSuperset(storedData, identityData));
392
393 /* Reload the identity */
394 msg = methodCall(SIGNOND_DAEMON_OBJECTPATH, SIGNOND_DAEMON_INTERFACE,
395 "getIdentity");
396 msg << id;
397 msg << QString("application_security_context");
398 reply = connection().call(msg);
399 QVERIFY(replyIsValid(reply));
400
401 QCOMPARE(reply.arguments().count(), 2);
402 objectPath = reply.arguments()[0].value<QDBusObjectPath>().path();
403 QVERIFY(objectPath.startsWith('/'));
404 storedData =
405 qdbus_cast<QVariantMap>(reply.arguments()[1].value<QDBusArgument>());
406 QVERIFY(mapIsSuperset(storedData, identityData));
407
408 /* Query identities */
409 msg = methodCall(SIGNOND_DAEMON_OBJECTPATH, SIGNOND_DAEMON_INTERFACE,
410 "queryIdentities");
411 msg << QVariantMap();
412 msg << QString("application_security_context");
413 reply = connection().call(msg);
414 QVERIFY(replyIsValid(reply));
415
416 QCOMPARE(reply.arguments().count(), 1);
417 QList<QVariantMap> identities =
418 qdbus_cast<QList<QVariantMap>>(reply.arguments()[0].value<QDBusArgument>());
419 QCOMPARE(identities.count(), 1);
420 storedData = identities[0];
421 QVERIFY(mapIsSuperset(storedData, identityData));
422 }
423
testIdentityRemoval()424 void SignondTest::testIdentityRemoval()
425 {
426 m_signonUi.mockedService().ClearCalls().waitForFinished();
427
428 SignondSecurityContextTestList acl = { SignondSecurityContextTest() };
429 QVariantMap identityData {
430 { SIGNOND_IDENTITY_INFO_USERNAME, "John Deleteme" },
431 { SIGNOND_IDENTITY_INFO_CAPTION, "John's account" },
432 { SIGNOND_IDENTITY_INFO_ACL, QVariant::fromValue(acl) },
433 };
434 uint id;
435 QString objectPath = createIdentity(identityData, &id);
436 QVERIFY(objectPath.startsWith('/'));
437 QVERIFY(id > 0);
438
439 QDBusMessage msg =
440 methodCall(objectPath, SIGNOND_IDENTITY_INTERFACE, "remove");
441 QDBusMessage reply = connection().call(msg);
442 QVERIFY(replyIsValid(reply));
443
444 /* Check that signonui has been asked to remove its data */
445 QList<MethodCall> calls =
446 m_signonUi.mockedService().GetMethodCalls("removeIdentityData");
447 QCOMPARE(calls.count(), 1);
448 MethodCall call = calls.first();
449 QCOMPARE(call.args().count(), 1);
450 QCOMPARE(call.args().first().toUInt(), id);
451
452 /* Read the current identity info */
453 msg = methodCall(objectPath, SIGNOND_IDENTITY_INTERFACE, "getInfo");
454 reply = connection().call(msg);
455 QCOMPARE(reply.type(), QDBusMessage::ErrorMessage);
456
457 /* Load the identity again */
458 msg = methodCall(SIGNOND_DAEMON_OBJECTPATH, SIGNOND_DAEMON_INTERFACE,
459 "getIdentity");
460 msg << id;
461 msg << QString("application_security_context");
462 reply = connection().call(msg);
463 QCOMPARE(reply.type(), QDBusMessage::ErrorMessage);
464 }
465
testIdentityReferences()466 void SignondTest::testIdentityReferences()
467 {
468 SignondSecurityContextTestList acl = { SignondSecurityContextTest() };
469 QVariantMap identityData {
470 { SIGNOND_IDENTITY_INFO_USERNAME, "John" },
471 { SIGNOND_IDENTITY_INFO_CAPTION, "John's account" },
472 { SIGNOND_IDENTITY_INFO_ACL, QVariant::fromValue(acl) },
473 };
474 QString objectPath = createIdentity(identityData);
475
476 /* Read the current identity info */
477 QDBusMessage msg =
478 methodCall(objectPath, SIGNOND_IDENTITY_INTERFACE, "getInfo");
479 QDBusMessage reply = connection().call(msg);
480 QVERIFY(replyIsValid(reply));
481
482 QVariantMap storedData = QDBusReply<QVariantMap>(reply).value();
483 QCOMPARE(storedData.value(SIGNOND_IDENTITY_INFO_REFCOUNT).toInt(), 0);
484
485 /* Add a reference */
486 msg = methodCall(objectPath, SIGNOND_IDENTITY_INTERFACE, "addReference");
487 msg << "hello";
488 reply = connection().call(msg);
489 QVERIFY(replyIsValid(reply));
490
491 /* Check new refcount */
492 msg = methodCall(objectPath, SIGNOND_IDENTITY_INTERFACE, "getInfo");
493 reply = connection().call(msg);
494 QVERIFY(replyIsValid(reply));
495 storedData = QDBusReply<QVariantMap>(reply).value();
496 QEXPECT_FAIL("", "refcount not updated: https://gitlab.com/accounts-sso/signond/issues/1", Continue);
497 QCOMPARE(storedData.value(SIGNOND_IDENTITY_INFO_REFCOUNT).toInt(), 1);
498 }
499
testAuthSessionMechanisms_data()500 void SignondTest::testAuthSessionMechanisms_data()
501 {
502 QTest::addColumn<QString>("method");
503 QTest::addColumn<QStringList>("wantedMechanisms");
504 QTest::addColumn<QStringList>("expectedMechanisms");
505
506 QTest::newRow("ssotest - any") <<
507 "ssotest" <<
508 QStringList {} <<
509 QStringList { "mech1", "mech2", "mech3", "BLOB" };
510 QTest::newRow("ssotest - subset") <<
511 "ssotest" <<
512 QStringList { "BLOB", "mech1" } <<
513 QStringList { "BLOB", "mech1" };
514 QTest::newRow("ssotest2 - any") <<
515 "ssotest2" <<
516 QStringList {} <<
517 QStringList { "mech1", "mech2", "mech3" };
518 QTest::newRow("ssotest2 - mix") <<
519 "ssotest2" <<
520 QStringList { "BLOB", "mech1" } <<
521 QStringList { "mech1" };
522 }
523
testAuthSessionMechanisms()524 void SignondTest::testAuthSessionMechanisms()
525 {
526 QFETCH(QString, method);
527 QFETCH(QStringList, wantedMechanisms);
528 QFETCH(QStringList, expectedMechanisms);
529
530 QDBusMessage msg = methodCall(SIGNOND_DAEMON_OBJECTPATH,
531 SIGNOND_DAEMON_INTERFACE,
532 "getAuthSessionObjectPath");
533 msg << uint(0);
534 msg << QString("*");
535 msg << method;
536 QDBusMessage reply = connection().call(msg);
537 QVERIFY(replyIsValid(reply));
538
539 QCOMPARE(reply.arguments().count(), 1);
540
541 QString objectPath = reply.arguments()[0].value<QDBusObjectPath>().path();
542 QVERIFY(objectPath.startsWith('/'));
543
544 /* Check the available mechanisms */
545 msg = methodCall(objectPath, SIGNOND_AUTH_SESSION_INTERFACE,
546 "queryAvailableMechanisms");
547 msg << wantedMechanisms;
548 reply = connection().call(msg);
549 QVERIFY(replyIsValid(reply));
550
551 QStringList mechanisms = QDBusReply<QStringList>(reply).value();
552 QCOMPARE(mechanisms.toSet(), expectedMechanisms.toSet());
553 }
554
testAuthSessionProcess()555 void SignondTest::testAuthSessionProcess()
556 {
557 QDBusMessage msg = methodCall(SIGNOND_DAEMON_OBJECTPATH,
558 SIGNOND_DAEMON_INTERFACE,
559 "getAuthSessionObjectPath");
560 msg << uint(0);
561 msg << QString("*");
562 msg << QString("ssotest");
563 QDBusMessage reply = connection().call(msg);
564 QVERIFY(replyIsValid(reply));
565
566 QCOMPARE(reply.arguments().count(), 1);
567
568 QString objectPath = reply.arguments()[0].value<QDBusObjectPath>().path();
569 QVERIFY(objectPath.startsWith('/'));
570
571 QVariantMap sessionData {
572 { "Some key", "its value" },
573 { "height", 123 },
574 };
575 /* Read the current identity info */
576 msg = methodCall(objectPath, SIGNOND_AUTH_SESSION_INTERFACE, "process");
577 msg << sessionData;
578 msg << QString("mech1");
579 reply = connection().call(msg);
580 QVERIFY(replyIsValid(reply));
581
582 QVariantMap response = QDBusReply<QVariantMap>(reply).value();
583 QVariantMap expectedResponse = sessionData;
584 expectedResponse["Realm"] = "testRealm_after_test";
585 QCOMPARE(response, expectedResponse);
586 }
587
testAuthSessionProcessFromOtherProcess()588 void SignondTest::testAuthSessionProcessFromOtherProcess()
589 {
590 QDBusMessage msg = methodCall(SIGNOND_DAEMON_OBJECTPATH,
591 SIGNOND_DAEMON_INTERFACE,
592 "getAuthSessionObjectPath");
593 msg << uint(0);
594 msg << QString("*");
595 msg << QString("ssotest");
596 QDBusMessage reply = connection().call(msg);
597 QVERIFY(replyIsValid(reply));
598
599 QCOMPARE(reply.arguments().count(), 1);
600
601 QString objectPath = reply.arguments()[0].value<QDBusObjectPath>().path();
602 QVERIFY(objectPath.startsWith('/'));
603
604 /* Pass this object path to another process, and verify that it's not
605 * allowed to use the session */
606 QProcess sessionTool;
607 sessionTool.start("./session_tool", { "--sessionPath", objectPath });
608 QVERIFY(sessionTool.waitForStarted());
609 QVERIFY(sessionTool.waitForFinished());
610
611 QByteArray output = sessionTool.readAllStandardOutput();
612 QVERIFY(output.startsWith("Error:"));
613
614 QString errorName = output.mid(6);
615 QCOMPARE(errorName, SIGNOND_PERMISSION_DENIED_ERR_NAME);
616 }
617
testAuthSessionProcessUi()618 void SignondTest::testAuthSessionProcessUi()
619 {
620 QDBusMessage msg = methodCall(SIGNOND_DAEMON_OBJECTPATH,
621 SIGNOND_DAEMON_INTERFACE,
622 "getAuthSessionObjectPath");
623 msg << uint(0);
624 msg << QString("*");
625 msg << QString("ssotest");
626 QDBusMessage reply = connection().call(msg);
627 QVERIFY(replyIsValid(reply));
628 QString objectPath = reply.arguments()[0].value<QDBusObjectPath>().path();
629 QVERIFY(objectPath.startsWith('/'));
630
631 /* prepare SignOnUi */
632 m_signonUi.mockedService().ClearCalls().waitForFinished();
633 QVariantMap uiReply {
634 { "data",
635 QVariantMap {
636 { "UserName", "the user" },
637 { "Secret", "s3c'r3t" },
638 { "QueryErrorCode", 0 },
639 }
640 },
641 };
642 m_signonUi.setNextReply(uiReply);
643
644 /* Start the authentication, using a mechanism requiring UI interaction */
645 QVariantMap sessionData {
646 { "Some key", "its value" },
647 { "height", 123 },
648 };
649 msg = methodCall(objectPath, SIGNOND_AUTH_SESSION_INTERFACE, "process");
650 msg << sessionData;
651 msg << QString("mech2");
652 reply = connection().call(msg);
653 QVERIFY(replyIsValid(reply));
654
655 QVariantMap response = QDBusReply<QVariantMap>(reply).value();
656 QVariantMap expectedResponse {
657 { "UserName", "the user" },
658 };
659 QCOMPARE(response, expectedResponse);
660 }
661
testAuthSessionCloseUi_data()662 void SignondTest::testAuthSessionCloseUi_data()
663 {
664 QTest::addColumn<QVariantMap>("uiReply");
665 QTest::addColumn<bool>("expectedCancellation");
666
667 QTest::newRow("no UI") <<
668 QVariantMap {} <<
669 false;
670
671 QTest::newRow("with UI") <<
672 QVariantMap {
673 { "data",
674 QVariantMap {
675 { "UserName", "the user" },
676 { "Secret", "s3c'r3t" },
677 { "QueryErrorCode", 0 },
678 }
679 }
680 } <<
681 true;
682
683 QTest::newRow("with UI error") <<
684 QVariantMap {
685 { "error", "some.Dbus.Error" }
686 } <<
687 false;
688
689 QTest::newRow("with UI canceled") <<
690 QVariantMap {
691 { "data",
692 QVariantMap {
693 { "UserName", "the user" },
694 { "Secret", "s3c'r3t" },
695 { "QueryErrorCode", SignOn::QUERY_ERROR_CANCELED },
696 }
697 }
698 } <<
699 false;
700 }
701
testAuthSessionCloseUi()702 void SignondTest::testAuthSessionCloseUi()
703 {
704 QFETCH(QVariantMap, uiReply);
705 QFETCH(bool, expectedCancellation);
706
707 QDBusMessage msg = methodCall(SIGNOND_DAEMON_OBJECTPATH,
708 SIGNOND_DAEMON_INTERFACE,
709 "getAuthSessionObjectPath");
710 msg << uint(0);
711 msg << QString("*");
712 msg << QString("ssotest");
713 QDBusMessage reply = connection().call(msg);
714 QVERIFY(replyIsValid(reply));
715 QString objectPath = reply.arguments()[0].value<QDBusObjectPath>().path();
716 QVERIFY(objectPath.startsWith('/'));
717
718 /* prepare SignOnUi */
719 OrgFreedesktopDBusMockInterface &mockInterface =
720 m_signonUi.mockedService();
721 mockInterface.ClearCalls().waitForFinished();
722 m_signonUi.setNextReply(uiReply);
723
724 /* Start the authentication; if uiReply is empty, don't invoke signonUI */
725 QString mechanism = uiReply.isEmpty() ? "mech1" : "mech2";
726 QVariantMap sessionData {
727 { "Some key", "its value" },
728 { "height", 123 },
729 };
730 msg = methodCall(objectPath, SIGNOND_AUTH_SESSION_INTERFACE, "process");
731 msg << sessionData;
732 msg << mechanism;
733 connection().call(msg);
734
735 /* Check whether signonui has been asked to close */
736 QTRY_COMPARE(mockInterface.GetMethodCalls("cancelUiRequest").value().count(),
737 expectedCancellation ? 1 : 0);
738 }
739
740 QTEST_GUILESS_MAIN(SignondTest);
741
742 #include "tst_signond.moc"
743