File indexing completed on 2024-05-05 03:56:10

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2010 David Faure <faure@kde.org>
0004     SPDX-FileCopyrightText: 2012 Dawit Alemayehu <adawit@kde.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include <kpasswdserver.h>
0010 
0011 #include <KPasswordDialog>
0012 
0013 #include <QApplication>
0014 #include <QSignalSpy>
0015 #include <QTest>
0016 
0017 // For the retry dialog (and only that one)
0018 static QDialogButtonBox::StandardButton s_buttonYes = QDialogButtonBox::Yes;
0019 static QDialogButtonBox::StandardButton s_buttonCancel = QDialogButtonBox::Cancel;
0020 
0021 Q_DECLARE_METATYPE(QDialogButtonBox::StandardButton)
0022 Q_DECLARE_METATYPE(QDialog::DialogCode)
0023 
0024 static QString getUserNameFrom(const KIO::AuthInfo &auth)
0025 {
0026     if (auth.username.isEmpty() && !auth.url.userName().isEmpty()) {
0027         return auth.url.userName();
0028     }
0029 
0030     return auth.username;
0031 }
0032 
0033 class KPasswdServerTest : public QObject
0034 {
0035     Q_OBJECT
0036 
0037 private Q_SLOTS:
0038     void initTestCase()
0039     {
0040         qRegisterMetaType<QDialogButtonBox::StandardButton>();
0041         qRegisterMetaType<QDialog::DialogCode>();
0042     }
0043 
0044     void simpleTest()
0045     {
0046         KPasswdServer server(this);
0047         server.setWalletDisabled(true);
0048 
0049         // Check that processRequest doesn't crash when it has nothing to do
0050         server.processRequest();
0051 
0052         KIO::AuthInfo info;
0053         info.url = QUrl(QStringLiteral("http://www.example.com"));
0054         info.keepPassword = true;
0055 
0056         // Make a check for that host, should say "not found"
0057         QVERIFY(noCheckAuth(server, info));
0058 
0059         // Now add auth to the cache
0060         const qlonglong windowId = 42;
0061         KIO::AuthInfo realInfo = info;
0062         realInfo.username = QStringLiteral("toto"); // you can see I'm french
0063         realInfo.password = QStringLiteral("foobar");
0064         server.addAuthInfo(realInfo, windowId); // seqnr=2
0065 
0066         // queryAuth without the ability to prompt, will just return info unmodified
0067         KIO::AuthInfo resultInfo;
0068         queryAuth(server, info, resultInfo);
0069         QCOMPARE(resultInfo.url, info.url);
0070         QCOMPARE(resultInfo.username, QString());
0071         QCOMPARE(resultInfo.password, QString());
0072         QCOMPARE(resultInfo.isModified(), false);
0073 
0074         // Check that checkAuth finds it
0075         QVERIFY(successCheckAuth(server, info, realInfo));
0076 
0077         // Now remove auth
0078         server.removeAuthInfo(info.url.host(), info.url.scheme(), info.username);
0079         // Check we can't find that auth anymore
0080         QVERIFY(noCheckAuth(server, info));
0081     }
0082 
0083     void testCheckDuringQuery()
0084     {
0085         KPasswdServer server(this);
0086         server.setWalletDisabled(true);
0087         KIO::AuthInfo info;
0088         info.url = QUrl(QStringLiteral("http://www.kde.org"));
0089 
0090         // Start a query
0091         QSignalSpy spyQuery(&server, &KPasswdServer::queryAuthInfoAsyncResult);
0092         const qlonglong windowId = 42;
0093         const qlonglong seqNr = 2;
0094         const qlonglong id = server.queryAuthInfoAsync(info,
0095                                                        QStringLiteral("<NoAuthPrompt>"), // magic string to avoid a dialog
0096                                                        windowId,
0097                                                        seqNr,
0098                                                        16 /*usertime*/);
0099 
0100         // Before it is processed, do a check, it will reply delayed.
0101         QSignalSpy spyCheck(&server, &KPasswdServer::checkAuthInfoAsyncResult);
0102         const qlonglong idCheck = server.checkAuthInfoAsync(info, windowId, 17 /*usertime*/);
0103         QCOMPARE(idCheck, 0LL); // always
0104         QCOMPARE(spyCheck.count(), 0); // no reply yet
0105 
0106         // Wait for the query to be processed
0107         QVERIFY(QSignalSpy(&server, &KPasswdServer::queryAuthInfoAsyncResult).wait(1000));
0108         QCOMPARE(spyQuery.count(), 1);
0109         QCOMPARE(spyQuery[0][0].toLongLong(), id);
0110         KIO::AuthInfo result = spyQuery[0][2].value<KIO::AuthInfo>();
0111 
0112         // Now the check will have replied
0113         QCOMPARE(spyCheck.count(), 1);
0114         QCOMPARE(spyCheck[0][0].toLongLong(), id + 1); // it was the next request after the query
0115         KIO::AuthInfo resultCheck = spyCheck[0][2].value<KIO::AuthInfo>();
0116         QCOMPARE(result.username, resultCheck.username);
0117         QCOMPARE(result.password, resultCheck.password);
0118     }
0119 
0120     void testExpiry()
0121     {
0122         KPasswdServer server(this);
0123         server.setWalletDisabled(true);
0124         KIO::AuthInfo info;
0125         info.url = QUrl(QStringLiteral("http://www.example.com"));
0126         info.keepPassword = true;
0127 
0128         // Add auth to the cache
0129         const qlonglong windowId = 42;
0130         KIO::AuthInfo realInfo = info;
0131         realInfo.username = QStringLiteral("toto");
0132         realInfo.password = QStringLiteral("foobar");
0133         server.addAuthInfo(realInfo, windowId);
0134 
0135         QVERIFY(successCheckAuth(server, info, realInfo));
0136 
0137         // Close another window, shouldn't hurt
0138         server.removeAuthForWindowId(windowId + 1);
0139         QVERIFY(successCheckAuth(server, info, realInfo));
0140 
0141         // Close window
0142         server.removeAuthForWindowId(windowId);
0143 
0144         // Check we can't find that auth anymore
0145         QVERIFY(noCheckAuth(server, info));
0146     }
0147 
0148     void testFillDialog()
0149     {
0150         KPasswdServer server(this);
0151         server.setWalletDisabled(true);
0152         // What the app would ask
0153         KIO::AuthInfo info;
0154         info.url = QUrl(QStringLiteral("http://www.example.com"));
0155 
0156         // What the user would type
0157         KIO::AuthInfo filledInfo(info);
0158         filledInfo.username = QStringLiteral("dfaure");
0159         filledInfo.password = QStringLiteral("toto");
0160 
0161         KIO::AuthInfo result;
0162         queryAuthWithDialog(server, info, filledInfo, result);
0163     }
0164 
0165     void testRejectRetryDialog()
0166     {
0167         KPasswdServer server(this);
0168         server.setWalletDisabled(true);
0169 
0170         // What the app would ask
0171         KIO::AuthInfo info;
0172         info.url = QUrl(QStringLiteral("http://www.example.com"));
0173 
0174         // What the user would type
0175         KIO::AuthInfo filledInfo(info);
0176         filledInfo.username = QStringLiteral("username");
0177         filledInfo.password = QStringLiteral("password");
0178 
0179         KIO::AuthInfo result;
0180         queryAuthWithDialog(server, info, filledInfo, result);
0181 
0182         // Pretend that the returned credentials failed and initiate a retry,
0183         // but cancel the retry dialog.
0184         info.password.clear();
0185         result = KIO::AuthInfo();
0186         queryAuthWithDialog(server, info, filledInfo, result, s_buttonCancel, QDialog::Accepted /*unused*/, QStringLiteral("Invalid username or password"));
0187     }
0188 
0189     void testAcceptRetryDialog()
0190     {
0191         KPasswdServer server(this);
0192         server.setWalletDisabled(true);
0193 
0194         // What the app would ask
0195         KIO::AuthInfo info;
0196         info.url = QUrl(QStringLiteral("http://www.example.com"));
0197 
0198         // What the user would type
0199         KIO::AuthInfo filledInfo(info);
0200         filledInfo.username = QStringLiteral("username");
0201         filledInfo.password = QStringLiteral("password");
0202 
0203         KIO::AuthInfo result;
0204         queryAuthWithDialog(server, info, filledInfo, result);
0205 
0206         // Pretend that the returned credentials failed and initiate a retry,
0207         // but this time continue the retry.
0208         info.password.clear();
0209         result = KIO::AuthInfo();
0210 
0211         queryAuthWithDialog(server, info, filledInfo, result, s_buttonYes, QDialog::Accepted, QStringLiteral("Invalid username or password"));
0212     }
0213 
0214     void testUsernameMistmatch()
0215     {
0216         KPasswdServer server(this);
0217         server.setWalletDisabled(true);
0218 
0219         // What the app would ask. Note the username in the URL.
0220         KIO::AuthInfo info;
0221         info.url = QUrl(QStringLiteral("http://foo@www.example.com"));
0222 
0223         // What the user would type
0224         KIO::AuthInfo filledInfo(info);
0225         filledInfo.username = QStringLiteral("bar");
0226         filledInfo.password = QStringLiteral("blah");
0227 
0228         KIO::AuthInfo result;
0229         queryAuthWithDialog(server, info, filledInfo, result);
0230 
0231         // Check the returned url does not match the request url because of the
0232         // username mismatch between the request URL and the filled in one.
0233         QVERIFY(result.url != filledInfo.url);
0234 
0235         // Verify there is NO cached auth data if the request URL contains the
0236         // original user name (foo).
0237         QVERIFY(noCheckAuth(server, info));
0238 
0239         // Verify there is a cached auth data if the request URL contains the
0240         // new user name (bar).
0241         filledInfo.url = QUrl(QStringLiteral("http://bar@www.example.com"));
0242         QVERIFY(successCheckAuth(server, filledInfo, result));
0243 
0244         // Now the URL check should be valid too.
0245         QCOMPARE(result.url, filledInfo.url);
0246     }
0247 
0248     void testCancelPasswordDialog()
0249     {
0250         KPasswdServer server(this);
0251         server.setWalletDisabled(true);
0252 
0253         // What the app would ask.
0254         KIO::AuthInfo info;
0255         info.url = QUrl(QStringLiteral("http://www.example.com"));
0256         info.username = info.url.userName();
0257 
0258         KIO::AuthInfo result;
0259         queryAuthWithDialog(server, info, KIO::AuthInfo(), result, QDialogButtonBox::NoButton, QDialog::Rejected);
0260     }
0261 
0262     void testVerifyPath()
0263     {
0264         KPasswdServer server(this);
0265         server.setWalletDisabled(true);
0266 
0267         // Add auth to the cache
0268         const qlonglong windowId = 42;
0269         KIO::AuthInfo authInfo;
0270         authInfo.url = QUrl(QStringLiteral("http://www.example.com/test/test.html"));
0271         authInfo.username = QStringLiteral("toto");
0272         authInfo.password = QStringLiteral("foobar");
0273         authInfo.keepPassword = true;
0274         server.addAuthInfo(authInfo, windowId);
0275 
0276         KIO::AuthInfo queryAuthInfo;
0277         queryAuthInfo.url = QUrl(QStringLiteral("http://www.example.com/test/test2/test.html"));
0278         queryAuthInfo.verifyPath = true;
0279 
0280         KIO::AuthInfo expectedAuthInfo;
0281         expectedAuthInfo.username = QStringLiteral("toto");
0282         expectedAuthInfo.password = QStringLiteral("foobar");
0283 
0284         QVERIFY(successCheckAuth(server, queryAuthInfo, expectedAuthInfo));
0285     }
0286 
0287     void testConcurrentQueryAuth()
0288     {
0289         KPasswdServer server(this);
0290         server.setWalletDisabled(true);
0291 
0292         QList<KIO::AuthInfo> authInfos;
0293         for (int i = 0; i < 10; ++i) {
0294             KIO::AuthInfo info;
0295             info.url = QUrl(QLatin1String("http://www.example.com/test") + QString::number(i) + QLatin1String(".html"));
0296             authInfos << info;
0297         }
0298 
0299         // What the user would type
0300         KIO::AuthInfo filledInfo;
0301         filledInfo.username = QStringLiteral("bar");
0302         filledInfo.password = QStringLiteral("blah");
0303 
0304         QList<KIO::AuthInfo> results;
0305         concurrentQueryAuthWithDialog(server, authInfos, filledInfo, results);
0306     }
0307 
0308     void testConcurrentCheckAuth()
0309     {
0310         KPasswdServer server(this);
0311         server.setWalletDisabled(true);
0312 
0313         QList<KIO::AuthInfo> authInfos;
0314         for (int i = 0; i < 10; ++i) {
0315             KIO::AuthInfo info;
0316             info.url = QUrl(QLatin1String("http://www.example.com/test") + QString::number(i) + QStringLiteral(".html"));
0317             authInfos << info;
0318         }
0319 
0320         // What the user would type
0321         KIO::AuthInfo filledInfo;
0322         filledInfo.username = QStringLiteral("bar");
0323         filledInfo.password = QStringLiteral("blah");
0324 
0325         QList<KIO::AuthInfo> results;
0326         concurrentQueryAuthWithDialog(server, authInfos, filledInfo, results);
0327     }
0328 
0329 private:
0330     // Checks that no auth is available for @p info
0331     bool noCheckAuth(KPasswdServer &server, const KIO::AuthInfo &info)
0332     {
0333         KIO::AuthInfo result;
0334         checkAuth(server, info, result);
0335         return (result.username == info.username) && (result.password == info.password) && !result.isModified();
0336     }
0337 
0338     // Check that the auth is available and equal to @expectedInfo
0339     bool successCheckAuth(KPasswdServer &server, const KIO::AuthInfo &info, const KIO::AuthInfo &expectedInfo)
0340     {
0341         KIO::AuthInfo result;
0342         checkAuth(server, info, result);
0343         return (result.username == expectedInfo.username) && (result.password == expectedInfo.password) && result.isModified();
0344     }
0345 
0346     void checkAuth(KPasswdServer &server, const KIO::AuthInfo &info, KIO::AuthInfo &result)
0347     {
0348         QSignalSpy spy(&server, &KPasswdServer::checkAuthInfoAsyncResult);
0349         const qlonglong windowId = 42;
0350         const qlonglong id = server.checkAuthInfoAsync(info, windowId, 17 /*usertime*/);
0351         QCOMPARE(id, 0LL); // always
0352         if (spy.isEmpty()) {
0353             QVERIFY(QSignalSpy(&server, &KPasswdServer::checkAuthInfoAsyncResult).wait(1000));
0354         }
0355         QCOMPARE(spy.count(), 1);
0356         // kpasswdserver emits a requestId via dbus, we can't get that id here
0357         QVERIFY(spy[0][0].toLongLong() >= 0);
0358         // QCOMPARE(spy[0][1].toLongLong(), 3LL); // seqNr
0359         result = spy[0][2].value<KIO::AuthInfo>();
0360     }
0361 
0362     void queryAuth(KPasswdServer &server, const KIO::AuthInfo &info, KIO::AuthInfo &result)
0363     {
0364         QSignalSpy spy(&server, &KPasswdServer::queryAuthInfoAsyncResult);
0365         const qlonglong windowId = 42;
0366         const qlonglong seqNr = 2;
0367         const qlonglong id = server.queryAuthInfoAsync(info,
0368                                                        QStringLiteral("<NoAuthPrompt>"), // magic string to avoid a dialog
0369                                                        windowId,
0370                                                        seqNr,
0371                                                        16 /*usertime*/);
0372         QVERIFY(id >= 0); // requestId, ever increasing
0373         if (spy.isEmpty()) {
0374             QVERIFY(QSignalSpy(&server, &KPasswdServer::queryAuthInfoAsyncResult).wait(1000));
0375         }
0376         QCOMPARE(spy.count(), 1);
0377         QCOMPARE(spy[0][0].toLongLong(), id);
0378         // QCOMPARE(spy[0][1].toLongLong(), 3LL); // seqNr
0379         result = spy[0][2].value<KIO::AuthInfo>();
0380     }
0381 
0382     void queryAuthWithDialog(KPasswdServer &server,
0383                              const KIO::AuthInfo &info,
0384                              const KIO::AuthInfo &filledInfo,
0385                              KIO::AuthInfo &result,
0386                              QDialogButtonBox::StandardButton retryButton = s_buttonYes,
0387                              QDialog::DialogCode code = QDialog::Accepted,
0388                              const QString &errMsg = QString())
0389     {
0390         QSignalSpy spy(&server, &KPasswdServer::queryAuthInfoAsyncResult);
0391         const qlonglong windowId = 42;
0392         const qlonglong seqNr = 2;
0393         const qlonglong id = server.queryAuthInfoAsync(info, errMsg, windowId, seqNr, 16 /*usertime*/);
0394         QVERIFY(id >= 0); // requestId, ever increasing
0395         QVERIFY(spy.isEmpty());
0396 
0397         const bool hasErrorMessage = (!errMsg.isEmpty());
0398         const bool isCancelRetryDialogTest = (hasErrorMessage && retryButton == s_buttonCancel);
0399 
0400         if (hasErrorMessage) {
0401             auto checkRetryFunc = [this, retryButton]() {
0402                 checkRetryDialog(retryButton);
0403             };
0404             // Retry dialog only knows Yes/No
0405             QMetaObject::invokeMethod(this, checkRetryFunc, Qt::QueuedConnection);
0406         }
0407 
0408         if (!isCancelRetryDialogTest) {
0409             auto checkFillFunc = [this, info, filledInfo, code]() {
0410                 checkAndFillDialog(info, filledInfo, code);
0411             };
0412             QMetaObject::invokeMethod(this, checkFillFunc, Qt::QueuedConnection);
0413         }
0414         // Force KPasswdServer to process the request now, otherwise the checkAndFillDialog needs a timer too...
0415         server.processRequest();
0416         if (spy.isEmpty()) {
0417             QVERIFY(QSignalSpy(&server, &KPasswdServer::queryAuthInfoAsyncResult).wait(1000));
0418         }
0419         QCOMPARE(spy.count(), 1);
0420         QCOMPARE(spy[0][0].toLongLong(), id);
0421         // QCOMPARE(spy[0][1].toLongLong(), 3LL); // seqNr
0422         result = spy[0][2].value<KIO::AuthInfo>();
0423         const QString username = (isCancelRetryDialogTest ? QString() : filledInfo.username);
0424         const QString password = (isCancelRetryDialogTest ? QString() : filledInfo.password);
0425         QCOMPARE(result.username, username);
0426         QCOMPARE(result.password, password);
0427         QCOMPARE(result.isModified(), retryButton == s_buttonYes && code == QDialog::Accepted);
0428     }
0429 
0430     void concurrentQueryAuthWithDialog(KPasswdServer &server,
0431                                        const QList<KIO::AuthInfo> &infos,
0432                                        const KIO::AuthInfo &filledInfo,
0433                                        QList<KIO::AuthInfo> &results,
0434                                        QDialog::DialogCode code = QDialog::Accepted)
0435     {
0436         QSignalSpy spy(&server, &KPasswdServer::queryAuthInfoAsyncResult);
0437         const qlonglong windowId = 42;
0438         qlonglong seqNr = 0;
0439         QList<qlonglong> idList;
0440 
0441         for (const KIO::AuthInfo &info : infos) {
0442             const qlonglong id = server.queryAuthInfoAsync(info, QString(), windowId, seqNr, 16 /*usertime*/);
0443             QVERIFY(id >= 0); // requestId, ever increasing
0444             idList << id;
0445         }
0446 
0447         QVERIFY(spy.isEmpty());
0448         auto checkFillFunc = [this, first = infos.first(), filledInfo, code]() {
0449             checkAndFillDialog(first, filledInfo, code);
0450         };
0451         QMetaObject::invokeMethod(this, checkFillFunc, Qt::QueuedConnection);
0452 
0453         // Force KPasswdServer to process the request now, otherwise the checkAndFillDialog needs a timer too...
0454         server.processRequest();
0455         while (spy.count() < infos.count()) {
0456             QVERIFY(QSignalSpy(&server, &KPasswdServer::queryAuthInfoAsyncResult).wait(1000));
0457         }
0458 
0459         QCOMPARE(spy.count(), infos.count());
0460 
0461         for (int i = 0, count = spy.count(); i < count; ++i) {
0462             QCOMPARE(spy[i][0].toLongLong(), idList.at(i));
0463             // QCOMPARE(spy[0][1].toLongLong(), 3LL); // seqNr
0464             KIO::AuthInfo result = spy[i][2].value<KIO::AuthInfo>();
0465             QCOMPARE(result.username, filledInfo.username);
0466             QCOMPARE(result.password, filledInfo.password);
0467             QCOMPARE(result.isModified(), code == QDialog::Accepted);
0468             results << result;
0469         }
0470     }
0471 
0472     void concurrentCheckAuthWithDialog(KPasswdServer &server,
0473                                        const QList<KIO::AuthInfo> &infos,
0474                                        const KIO::AuthInfo &filledInfo,
0475                                        QList<KIO::AuthInfo> &results,
0476                                        QDialog::DialogCode code = QDialog::Accepted)
0477     {
0478         QSignalSpy spy(&server, &KPasswdServer::queryAuthInfoAsyncResult);
0479         const qlonglong windowId = 42;
0480         qlonglong seqNr = 0;
0481         QList<qlonglong> idList;
0482 
0483         QListIterator<KIO::AuthInfo> it(infos);
0484         if (it.hasNext()) {
0485             const qlonglong id = server.queryAuthInfoAsync(it.next(), QString(), windowId, seqNr, 16 /*usertime*/);
0486             QVERIFY(id >= 0); // requestId, ever increasing
0487             idList << id;
0488         }
0489 
0490         while (it.hasNext()) {
0491             const qlonglong id = server.checkAuthInfoAsync(it.next(), windowId, 16 /*usertime*/);
0492             QVERIFY(id >= 0); // requestId, ever increasing
0493             idList << id;
0494         }
0495 
0496         QVERIFY(spy.isEmpty());
0497         auto checkAndFillFunc = [this, first = infos.first(), filledInfo, code]() {
0498             checkAndFillDialog(first, filledInfo, code);
0499         };
0500         QMetaObject::invokeMethod(this, checkAndFillFunc, Qt::QueuedConnection);
0501 
0502         // Force KPasswdServer to process the request now, otherwise the checkAndFillDialog needs a timer too...
0503         server.processRequest();
0504         if (spy.isEmpty()) {
0505             QVERIFY(QSignalSpy(&server, &KPasswdServer::queryAuthInfoAsyncResult).wait(1000));
0506         }
0507 
0508         while ((spy.count() - 1) < infos.count()) {
0509             QVERIFY(QSignalSpy(&server, &KPasswdServer::checkAuthInfoAsyncResult).wait(1000));
0510         }
0511 
0512         for (int i = 0, count = spy.count(); i < count; ++i) {
0513             QCOMPARE(spy[i][0].toLongLong(), idList.at(i));
0514             // QCOMPARE(spy[0][1].toLongLong(), 3LL); // seqNr
0515             KIO::AuthInfo result = spy[i][2].value<KIO::AuthInfo>();
0516             QCOMPARE(result.username, filledInfo.username);
0517             QCOMPARE(result.password, filledInfo.password);
0518             QCOMPARE(result.isModified(), code == QDialog::Accepted);
0519             results << result;
0520         }
0521     }
0522 
0523 protected Q_SLOTS:
0524     void checkAndFillDialog(const KIO::AuthInfo &info, const KIO::AuthInfo &filledInfo, QDialog::DialogCode code)
0525     {
0526         const QList<QWidget *> widgetsList = QApplication::topLevelWidgets();
0527         for (QWidget *widget : widgetsList) {
0528             if (KPasswordDialog *dialog = qobject_cast<KPasswordDialog *>(widget)) {
0529                 qDebug() << "Found dialog" << dialog;
0530                 if (code == QDialog::Accepted) {
0531                     QCOMPARE(dialog->username(), getUserNameFrom(info));
0532                     QCOMPARE(dialog->password(), info.password);
0533                     dialog->setUsername(filledInfo.username);
0534                     dialog->setPassword(filledInfo.password);
0535                     qDebug() << "Filled dialog with" << filledInfo.username << filledInfo.password;
0536                 }
0537                 dialog->done(code);
0538                 return;
0539             }
0540         }
0541         qWarning() << "No KPasswordDialog found!";
0542     }
0543 
0544     void checkRetryDialog(QDialogButtonBox::StandardButton code = s_buttonYes)
0545     {
0546         const QList<QWidget *> widgetsList = QApplication::topLevelWidgets();
0547         for (QWidget *widget : widgetsList) {
0548             QDialog *dialog = qobject_cast<QDialog *>(widget);
0549             if (dialog && !dialog->inherits("KPasswordDialog")) {
0550                 qDebug() << "Closing dialog" << dialog << "with code" << code;
0551                 dialog->done(code);
0552                 return;
0553             }
0554         }
0555         qWarning() << "No retry dialog found";
0556     }
0557 };
0558 
0559 QTEST_MAIN(KPasswdServerTest)
0560 
0561 #include "kpasswdservertest.moc"