File indexing completed on 2024-12-01 04:35:23

0001 /*
0002 
0003  * SPDX-FileCopyrightText: 2020 Alessandro Ambrosano <alessandro.ambrosano@gmail.com>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.0-or-later
0006  *
0007  */
0008 
0009 #include "ddpauthenticationmanagertest.h"
0010 
0011 #include "rocketchataccount.h"
0012 #include "utils.h"
0013 
0014 #include "ddpapi/ddpauthenticationmanager.h"
0015 
0016 #include <QSignalSpy>
0017 #include <QTest>
0018 
0019 QTEST_GUILESS_MAIN(DDPAuthenticationManagerTest)
0020 
0021 DDPAuthenticationManagerTest::DDPAuthenticationManagerTest(QObject *parent)
0022     : QObject(parent)
0023 {
0024 }
0025 
0026 void DDPAuthenticationManagerTest::testLoginSuccess()
0027 {
0028     // Logging in with an existing token
0029     {
0030         RocketChatAccount dummyAccount;
0031         DDPAuthenticationManager authManager(dummyAccount.ddp());
0032 
0033         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0034         QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0035         authManager.setAuthToken(QStringLiteral("some token"));
0036         authManager.login();
0037         QCOMPARE(spyStatusChanged.count(), 1);
0038         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0039 
0040         authManager.processMethodResponse(0,
0041                                           Utils::strToJsonObject(QStringLiteral(
0042                                               R"(
0043         {
0044             "msg": "result",
0045             "id": "0",
0046             "result": {
0047                 "id": "some id",
0048                 "token": "some token",
0049                 "tokenExpires": {
0050                     "$date": 1590844250110
0051                 },
0052                 "type": "resume"
0053             }
0054         })")));
0055 
0056         QCOMPARE(spyStatusChanged.count(), 2);
0057         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoggedIn);
0058         QCOMPARE(authManager.userId(), QStringLiteral("some id"));
0059         QCOMPARE(authManager.authToken(), QStringLiteral("some token"));
0060     }
0061 
0062     // Logging in with username and password
0063     {
0064         RocketChatAccount dummyAccount;
0065         DDPAuthenticationManager authManager(dummyAccount.ddp());
0066 
0067         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0068         QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0069         authManager.login(QStringLiteral("someuser"), QStringLiteral("somepassword"));
0070         QCOMPARE(spyStatusChanged.count(), 1);
0071         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0072 
0073         authManager.processMethodResponse(0,
0074                                           Utils::strToJsonObject(QStringLiteral(
0075                                               R"(
0076         {
0077             "msg": "result",
0078             "id": "0",
0079             "result": {
0080                 "id": "some id",
0081                 "token": "some token",
0082                 "tokenExpires": {
0083                     "$date": 1590844250110
0084                 },
0085                 "type": "?????"
0086             }
0087         })"))); // TODO: check the type for non-ldap, non-2fa successful login messages
0088 
0089         QCOMPARE(spyStatusChanged.count(), 2);
0090         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoggedIn);
0091         QCOMPARE(authManager.userId(), QStringLiteral("some id"));
0092         QCOMPARE(authManager.authToken(), QStringLiteral("some token"));
0093     }
0094 
0095     // Logging in using oauth
0096     {
0097         RocketChatAccount dummyAccount;
0098         DDPAuthenticationManager authManager(dummyAccount.ddp());
0099 
0100         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0101         QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0102         authManager.loginOAuth(QStringLiteral("someuser"), QStringLiteral("somepassword"));
0103         QCOMPARE(spyStatusChanged.count(), 1);
0104         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0105 
0106         authManager.processMethodResponse(0,
0107                                           Utils::strToJsonObject(QStringLiteral(
0108                                               R"(
0109         {
0110             "msg": "result",
0111             "id": "0",
0112             "result": {
0113                 "id": "some id",
0114                 "token": "some token",
0115                 "tokenExpires": {
0116                     "$date": 1590844250110
0117                 },
0118                 "type": "?????"
0119             }
0120         })"))); // TODO: check the type for non-ldap, non-2fa successful login messages
0121 
0122         QCOMPARE(spyStatusChanged.count(), 2);
0123         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoggedIn);
0124         QCOMPARE(authManager.userId(), QStringLiteral("some id"));
0125         QCOMPARE(authManager.authToken(), QStringLiteral("some token"));
0126     }
0127 
0128     // Logging in with username and password
0129     {
0130         RocketChatAccount dummyAccount;
0131         DDPAuthenticationManager authManager(dummyAccount.ddp());
0132 
0133         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0134         QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0135         authManager.login(QStringLiteral("someuser"), QStringLiteral("somepassword"));
0136         QCOMPARE(spyStatusChanged.count(), 1);
0137         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0138 
0139         authManager.processMethodResponse(0,
0140                                           Utils::strToJsonObject(QStringLiteral(
0141                                               R"(
0142         {
0143             "msg": "result",
0144             "id": "0",
0145             "result": {
0146                 "id": "some id",
0147                 "token": "some token",
0148                 "tokenExpires": {
0149                     "$date": 1590844250110
0150                 },
0151                 "type": "ldap"
0152             }
0153         })")));
0154 
0155         QCOMPARE(spyStatusChanged.count(), 2);
0156         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoggedIn);
0157         QCOMPARE(authManager.userId(), QStringLiteral("some id"));
0158         QCOMPARE(authManager.authToken(), QStringLiteral("some token"));
0159     }
0160 
0161     // Logging in with OAuth
0162     {
0163         RocketChatAccount dummyAccount;
0164         DDPAuthenticationManager authManager(dummyAccount.ddp());
0165 
0166         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0167         QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0168         authManager.loginOAuth(QStringLiteral("sometoken"), QStringLiteral("somesecret"));
0169         QCOMPARE(spyStatusChanged.count(), 1);
0170         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0171 
0172         authManager.processMethodResponse(0,
0173                                           Utils::strToJsonObject(QStringLiteral(
0174                                               R"(
0175         {
0176             "msg": "result",
0177             "id": "0",
0178             "result": {
0179                 "id": "some id",
0180                 "token": "some token",
0181                 "tokenExpires": {
0182                     "$date": 1590844250110
0183                 },
0184                 "type": "????"
0185             }
0186         })")));
0187 
0188         QCOMPARE(spyStatusChanged.count(), 2);
0189         QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoggedIn);
0190         QCOMPARE(authManager.userId(), QStringLiteral("some id"));
0191         QCOMPARE(authManager.authToken(), QStringLiteral("some token"));
0192     }
0193 }
0194 
0195 void DDPAuthenticationManagerTest::testLoginInvalidLoginInfo()
0196 {
0197     RocketChatAccount dummyAccount;
0198     DDPAuthenticationManager authManager(dummyAccount.ddp());
0199 
0200     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0201     QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0202     authManager.login(QStringLiteral("wronguser"), QStringLiteral("wrongpassword"));
0203     QCOMPARE(spyStatusChanged.count(), 1);
0204     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0205 
0206     authManager.processMethodResponse(0,
0207                                       Utils::strToJsonObject(QStringLiteral(
0208                                           R"(
0209     {
0210         "msg": "result",
0211         "id": "0",
0212         "error": {
0213             "isClientSafe": true,
0214             "error": 403,
0215             "reason": "User not found",
0216             "message": "User not found [403]",
0217             "errorType": "Meteor.Error"
0218         }
0219     })")));
0220 
0221     QCOMPARE(spyStatusChanged.count(), 2);
0222     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginFailedInvalidUserOrPassword);
0223 }
0224 
0225 void DDPAuthenticationManagerTest::testLoginWithOtpSuccess()
0226 {
0227     RocketChatAccount dummyAccount;
0228     DDPAuthenticationManager authManager(dummyAccount.ddp());
0229 
0230     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0231     QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0232     authManager.login(QStringLiteral("wronguser"), QStringLiteral("wrongpassword"));
0233     QCOMPARE(spyStatusChanged.count(), 1);
0234     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0235 
0236     authManager.processMethodResponse(0,
0237                                       Utils::strToJsonObject(QStringLiteral(
0238                                           R"(
0239     {
0240         "msg": "result",
0241         "id": "0",
0242         "error": {
0243             "isClientSafe": true,
0244             "error": "totp-required",
0245             "reason": "TOTP Required",
0246             "details": {
0247                 "method": "email",
0248                 "codeGenerated": true,
0249                 "availableMethods": [
0250                     "email"
0251                 ]
0252             },
0253             "message": "TOTP Required [totp-required]",
0254             "errorType": "Meteor.Error"
0255         }
0256     })")));
0257 
0258     QCOMPARE(spyStatusChanged.count(), 2);
0259     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOtpRequired);
0260 
0261     authManager.sendOTP(QStringLiteral("otpcode"));
0262     QCOMPARE(spyStatusChanged.count(), 3);
0263     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOtpAuthOngoing);
0264 
0265     authManager.processMethodResponse(1,
0266                                       Utils::strToJsonObject(QStringLiteral(
0267                                           R"(
0268     {
0269         "msg": "result",
0270         "id": "1",
0271         "result": {
0272             "id": "some id",
0273             "token": "some token",
0274             "tokenExpires": {
0275                 "$date": 1596139026776
0276             },
0277             "type": "password"
0278         }
0279     })")));
0280 
0281     QCOMPARE(spyStatusChanged.count(), 4);
0282     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedIn);
0283     QCOMPARE(authManager.userId(), QStringLiteral("some id"));
0284     QCOMPARE(authManager.authToken(), QStringLiteral("some token"));
0285 }
0286 
0287 void DDPAuthenticationManagerTest::testLoginWithOtpFailure()
0288 {
0289     RocketChatAccount dummyAccount;
0290     DDPAuthenticationManager authManager(dummyAccount.ddp());
0291 
0292     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0293     QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0294     authManager.login(QStringLiteral("wronguser"), QStringLiteral("wrongpassword"));
0295     QCOMPARE(spyStatusChanged.count(), 1);
0296     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0297 
0298     authManager.processMethodResponse(0,
0299                                       Utils::strToJsonObject(QStringLiteral(
0300                                           R"(
0301     {
0302         "msg": "result",
0303         "id": "0",
0304         "error": {
0305             "isClientSafe": true,
0306             "error": "totp-required",
0307             "reason": "TOTP Required",
0308             "details": {
0309                 "method": "email",
0310                 "codeGenerated": true,
0311                 "availableMethods": [
0312                     "email"
0313                 ]
0314             },
0315             "message": "TOTP Required [totp-required]",
0316             "errorType": "Meteor.Error"
0317         }
0318     })")));
0319 
0320     QCOMPARE(spyStatusChanged.count(), 2);
0321     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOtpRequired);
0322 
0323     authManager.sendOTP(QStringLiteral("otpcode"));
0324     QCOMPARE(spyStatusChanged.count(), 3);
0325     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOtpAuthOngoing);
0326 
0327     authManager.processMethodResponse(1,
0328                                       Utils::strToJsonObject(QStringLiteral(
0329                                           R"(
0330     {
0331         "msg": "result",
0332         "id": "1",
0333         "error": {
0334             "isClientSafe": true,
0335             "error": "totp-invalid",
0336             "reason": "TOTP Invalid",
0337             "details": {
0338                 "method": "email"
0339             },
0340             "message": "TOTP Invalid [totp-invalid]",
0341             "errorType": "Meteor.Error"
0342         }
0343     })")));
0344 
0345     QCOMPARE(spyStatusChanged.count(), 4);
0346     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginFailedInvalidOtp);
0347 }
0348 
0349 void DDPAuthenticationManagerTest::testUnknownError()
0350 {
0351     RocketChatAccount dummyAccount;
0352     DDPAuthenticationManager authManager(dummyAccount.ddp());
0353 
0354     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0355     QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0356     authManager.login(QStringLiteral("someuser"), QStringLiteral("somepassword"));
0357     QCOMPARE(spyStatusChanged.count(), 1);
0358     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0359 
0360     authManager.processMethodResponse(0, Utils::strToJsonObject(QStringLiteral(R"(
0361     {
0362         "msg": "result",
0363         "id": "0",
0364         "error": {
0365             "error": "unknown-error"
0366         }
0367     })")));
0368 
0369     QCOMPARE(spyStatusChanged.count(), 2);
0370     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::GenericError);
0371 }
0372 
0373 void DDPAuthenticationManagerTest::testUserNotActivatedError()
0374 {
0375     RocketChatAccount dummyAccount;
0376     DDPAuthenticationManager authManager(dummyAccount.ddp());
0377 
0378     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0379     QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0380     authManager.login(QStringLiteral("someuser"), QStringLiteral("somepassword"));
0381     QCOMPARE(spyStatusChanged.count(), 1);
0382     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0383 
0384     authManager.processMethodResponse(0, Utils::strToJsonObject(QStringLiteral(R"(
0385     {
0386         "msg": "result",
0387         "id": "0",
0388         "error": {
0389             "error": "error-user-is-not-activated"
0390         }
0391     })")));
0392 
0393     QCOMPARE(spyStatusChanged.count(), 2);
0394     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginFailedUserNotActivated);
0395 }
0396 
0397 void DDPAuthenticationManagerTest::testLoginBlockForIpError()
0398 {
0399     RocketChatAccount dummyAccount;
0400     DDPAuthenticationManager authManager(dummyAccount.ddp());
0401 
0402     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0403     QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0404     authManager.login(QStringLiteral("someuser"), QStringLiteral("somepassword"));
0405     QCOMPARE(spyStatusChanged.count(), 1);
0406     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0407 
0408     authManager.processMethodResponse(0, Utils::strToJsonObject(QStringLiteral(R"(
0409     {
0410         "msg": "result",
0411         "id": "0",
0412         "error": {
0413             "error": "error-login-blocked-for-ip"
0414         }
0415     })")));
0416 
0417     QCOMPARE(spyStatusChanged.count(), 2);
0418     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginFailedLoginBlockForIp);
0419 }
0420 
0421 void DDPAuthenticationManagerTest::testLoginBlockedForUser()
0422 {
0423     RocketChatAccount dummyAccount;
0424     DDPAuthenticationManager authManager(dummyAccount.ddp());
0425 
0426     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0427     QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0428     authManager.login(QStringLiteral("someuser"), QStringLiteral("somepassword"));
0429     QCOMPARE(spyStatusChanged.count(), 1);
0430     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0431 
0432     authManager.processMethodResponse(0, Utils::strToJsonObject(QStringLiteral(R"(
0433     {
0434         "msg": "result",
0435         "id": "0",
0436         "error": {
0437             "error": "error-login-blocked-for-user"
0438         }
0439     })")));
0440 
0441     QCOMPARE(spyStatusChanged.count(), 2);
0442     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginFailedLoginBlockedForUser);
0443 }
0444 
0445 void DDPAuthenticationManagerTest::testLoginAppUserAllowToLogin()
0446 {
0447     RocketChatAccount dummyAccount;
0448     DDPAuthenticationManager authManager(dummyAccount.ddp());
0449 
0450     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoggedOut);
0451     QSignalSpy spyStatusChanged(&authManager, &DDPAuthenticationManager::loginStatusChanged);
0452     authManager.login(QStringLiteral("someuser"), QStringLiteral("somepassword"));
0453     QCOMPARE(spyStatusChanged.count(), 1);
0454     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginOngoing);
0455 
0456     authManager.processMethodResponse(0, Utils::strToJsonObject(QStringLiteral(R"(
0457     {
0458         "msg": "result",
0459         "id": "0",
0460         "error": {
0461             "error": "error-app-user-is-not-allowed-to-login"
0462         }
0463     })")));
0464 
0465     QCOMPARE(spyStatusChanged.count(), 2);
0466     QCOMPARE(authManager.loginStatus(), DDPAuthenticationManager::LoginStatus::LoginFailedLoginAppNotAllowedToLogin);
0467 }
0468 
0469 #include "moc_ddpauthenticationmanagertest.cpp"