diff --git a/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp b/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp index 2517fa77..06e71459 100644 --- a/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp +++ b/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp @@ -296,6 +296,8 @@ JsonReply *JsonRPCServerImplementation::Hello(const QVariantMap ¶ms, const J handshake.insert("cacheHashes", cacheHashes); } + m_clientTokens[context.clientId()] = context.token(); + bool badToken = false; if (!context.token().isEmpty()) { TokenInfo tokenInfo = NymeaCore::instance()->userManager()->tokenInfo(context.token()); @@ -303,7 +305,7 @@ JsonReply *JsonRPCServerImplementation::Hello(const QVariantMap ¶ms, const J badToken = tokenInfo.id().isNull(); handshake.insert("authenticated", !badToken); handshake.insert("permissionScopes", Types::scopesToStringList(userInfo.scopes())); - handshake.insert("username", userInfo.username()); + handshake.insert("username", userInfo.username()); } // If the connection is locked down already (because of a previous failed attempt) and authentication failed @@ -412,6 +414,8 @@ JsonReply *JsonRPCServerImplementation::Authenticate(const QVariantMap ¶ms, m_connectionLockdownTimer.start(); } + m_clientTokens[context.clientId()] = token; + return createReply(ret); } @@ -596,11 +600,24 @@ void JsonRPCServerImplementation::processJsonPacket(TransportInterface *interfac QString targetNamespace = commandList.first(); QString method = commandList.last(); + // We'll only allow setting a new token in the hello call. All other calls must match the token given in the hello + QByteArray token = m_clientTokens.value(clientId); + if (methodString == "JSONRPC.Hello") { + token = message.value("token").toByteArray(); + } else if (message.value("token").toByteArray() != token) { + qCWarning(dcJsonRpc()) << "Client changed token without redoing the handshake."; + qCDebug(dcJsonRpc) << "Old token:" << token << "new token:" << message.value("token").toByteArray(); + sendUnauthorizedResponse(interface, clientId, commandId, "Changing the user (token) requires a new handshake. Call JSONRPC.Hello."); + interface->terminateClientConnection(clientId); + qCWarning(dcJsonRpc()) << "Staring connection lockdown timer"; + m_connectionLockdownTimer.start(); + return; + } + // check if authentication is required for this transport - if (interface->configuration().authenticationEnabled) { - QByteArray token = message.value("token").toByteArray(); - QStringList authExemptMethodsNoUser = {"JSONRPC.Introspect", "JSONRPC.Hello", "JSONRPC.RequestPushButtonAuth", "JSONRPC.CreateUser"}; - QStringList authExemptMethodsWithUser = {"JSONRPC.Introspect", "JSONRPC.Hello", "JSONRPC.Authenticate", "JSONRPC.RequestPushButtonAuth"}; + if (interface->configuration().authenticationEnabled) { + QStringList authExemptMethodsNoUser = {"JSONRPC.Hello", "JSONRPC.RequestPushButtonAuth", "JSONRPC.CreateUser"}; + QStringList authExemptMethodsWithUser = {"JSONRPC.Hello", "JSONRPC.Authenticate", "JSONRPC.RequestPushButtonAuth"}; // if there is no user in the system yet, let's fail unless this is a special method for authentication itself if (NymeaCore::instance()->userManager()->initRequired()) { if (!authExemptMethodsNoUser.contains(methodString) && (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token))) { @@ -677,7 +694,7 @@ void JsonRPCServerImplementation::processJsonPacket(TransportInterface *interfac } JsonContext callContext(clientId, m_clientLocales.value(clientId)); - callContext.setToken(message.value("token").toByteArray()); + callContext.setToken(token); qCDebug(dcJsonRpc()) << "Invoking method" << targetNamespace + '.' + method << "from client" << clientId; @@ -833,6 +850,7 @@ void JsonRPCServerImplementation::onPushButtonAuthFinished(int transactionId, bo params.insert("success", success); if (success) { params.insert("token", token); + m_clientTokens[clientId] = token; } emit PushButtonAuthFinished(clientId, params); @@ -981,6 +999,7 @@ void JsonRPCServerImplementation::clientDisconnected(const QUuid &clientId) m_clientNotifications.remove(clientId); m_clientBuffers.remove(clientId); m_clientLocales.remove(clientId); + m_clientTokens.remove(clientId); if (m_pushButtonTransactions.values().contains(clientId)) { NymeaCore::instance()->userManager()->cancelPushButtonAuth(m_pushButtonTransactions.key(clientId)); } diff --git a/libnymea-core/jsonrpc/jsonrpcserverimplementation.h b/libnymea-core/jsonrpc/jsonrpcserverimplementation.h index 6d434bb9..b3165982 100644 --- a/libnymea-core/jsonrpc/jsonrpcserverimplementation.h +++ b/libnymea-core/jsonrpc/jsonrpcserverimplementation.h @@ -113,6 +113,7 @@ private: QHash m_clientBuffers; QHash m_clientNotifications; QHash m_clientLocales; + QHash m_clientTokens; QHash m_pushButtonTransactions; QHash m_newConnectionWaitTimers; diff --git a/libnymea-core/usermanager/usermanager.cpp b/libnymea-core/usermanager/usermanager.cpp index 9deaea0f..0859a0e5 100644 --- a/libnymea-core/usermanager/usermanager.cpp +++ b/libnymea-core/usermanager/usermanager.cpp @@ -164,6 +164,7 @@ UserManager::UserError UserManager::createUser(const QString &username, const QS QSqlQuery checkForDuplicateUserQuery(m_db); checkForDuplicateUserQuery.prepare("SELECT * FROM users WHERE lower(username) = ?;"); + // Note: We're using toLower() on the username mainly for the reason that in old versions the username used to be an email address checkForDuplicateUserQuery.addBindValue(username.toLower()); checkForDuplicateUserQuery.exec(); if (checkForDuplicateUserQuery.first()) { diff --git a/tests/auto/jsonrpc/testjsonrpc.cpp b/tests/auto/jsonrpc/testjsonrpc.cpp index 5cd6a0fa..b3bcafaa 100644 --- a/tests/auto/jsonrpc/testjsonrpc.cpp +++ b/tests/auto/jsonrpc/testjsonrpc.cpp @@ -248,20 +248,10 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.isValid()); QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected); - // Introspect call should work in any case - qCDebug(dcTests()) << "Calling Introspect, expecting success"; - m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Introspect\"}\n"); - if (spy.count() == 0) { - spy.wait(); - } - QVERIFY(spy.count() == 1); - QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); - QVariantMap response = jsonDoc.toVariant().toMap(); - qCDebug(dcTests()) << "Result:" << response.value("status").toString() << response.value("error").toString(); - QCOMPARE(response.value("status").toString(), QStringLiteral("success")); + QJsonDocument jsonDoc; + QVariantMap response; - - // Hello call should work in any case too + // Hello call should work in any case spy.clear(); qCDebug(dcTests()) << "Calling Hello, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n"); @@ -335,10 +325,10 @@ void TestJSONRPC::testInitialSetup() QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 0); - // Now lets play by the rules (with an uppercase email) + // Now lets play by the rules (with an uppercase username) spy.clear(); qCDebug(dcTests()) << "Calling CreateUser, expecting success"; - m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"DummyPW1!\"}}\n"); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"Dummy\", \"password\": \"DummyPW1!\"}}\n"); if (spy.count() == 0) { spy.wait(); } @@ -396,7 +386,7 @@ void TestJSONRPC::testInitialSetup() // Now lets authenticate with a wrong user spy.clear(); qCDebug(dcTests()) << "Calling Authenticate, expecting failure (bad user)"; - m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"Dummy@wrong.domain\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n"); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"Dummy-wrong\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n"); if (spy.count() == 0) { spy.wait(); } @@ -413,7 +403,7 @@ void TestJSONRPC::testInitialSetup() spy.clear(); disconnectedSpy.clear(); qCDebug(dcTests()) << "Calling Authenticate, expecting failure (bad password)"; - m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"wrongpw\", \"deviceName\": \"testcase\"}}\n"); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"Dummy\", \"password\": \"wrongpw\", \"deviceName\": \"testcase\"}}\n"); if (spy.count() == 0) { spy.wait(); } @@ -439,10 +429,10 @@ void TestJSONRPC::testInitialSetup() } QVERIFY(spy.count() == 1); - // Now lets authenticate for real (but intentionally use a lowercase email here, should still work) + // Now lets authenticate for real (but intentionally use a lowercase username here, should still work) spy.clear(); qCDebug(dcTests()) << "Calling Authenticate, expecting success"; - m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"dummy@guh.io\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n"); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"dummy\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n"); if (spy.count() == 0) { spy.wait(); } @@ -491,11 +481,12 @@ void TestJSONRPC::testRevokeToken() QVariantList tokenList = response.value("params").toMap().value("tokenInfoList").toList(); QCOMPARE(tokenList.count(), 1); QUuid oldTokenId = tokenList.first().toMap().value("id").toUuid(); + qCDebug(dcTests()) << "have tokens:" << tokenList; // Authenticate and create a new token spy.clear(); qCDebug(dcTests()) << "Calling Authenticate with valid credentials" ; - m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"dummy@guh.io\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n"); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"dummy\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase-revoke-token\"}}\n"); if (spy.count() == 0) { spy.wait(); } @@ -504,12 +495,15 @@ void TestJSONRPC::testRevokeToken() response = jsonDoc.toVariant().toMap(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("success").toBool(), true); - QByteArray newToken = response.value("params").toMap().value("token").toByteArray(); - QVERIFY(!newToken.isEmpty()); + // After an authenticate call, we need to continue with the new token + QByteArray oldToken = m_apiToken; + m_apiToken = response.value("params").toMap().value("token").toByteArray(); + QVERIFY(!m_apiToken.isEmpty()); + // Now do a Version call with the new token and it should work spy.clear(); - m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + newToken + "\", \"method\": \"JSONRPC.Version\"}\n"); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Version\"}\n"); if (spy.count() == 0) { spy.wait(); } @@ -519,7 +513,7 @@ void TestJSONRPC::testRevokeToken() qCDebug(dcTests()) << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); - // Now get all the tokens using the old token + // Now get all the tokens spy.clear(); qCDebug(dcTests()) << "Calling Tokens"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"Users.GetTokens\"}\n"); @@ -531,21 +525,14 @@ void TestJSONRPC::testRevokeToken() response = jsonDoc.toVariant().toMap(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); tokenList = response.value("params").toMap().value("tokenInfoList").toList(); + qCDebug(dcTests()) << "have tokens:" << tokenList; QCOMPARE(tokenList.count(), 2); - // find the new token - QUuid newTokenId; - foreach (const QVariant &tokenInfo, tokenList) { - if (tokenInfo.toMap().value("id").toUuid() != oldTokenId) { - newTokenId = tokenInfo.toMap().value("id").toUuid(); - break; - } - } - // Revoke the new token + // Revoke the old token spy.clear(); qCDebug(dcTests()) << "Calling RemoveToken"; - m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"Users.RemoveToken\", \"params\": {\"tokenId\": \"" + newTokenId.toByteArray() + "\"}}\n"); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"Users.RemoveToken\", \"params\": {\"tokenId\": \"" + oldTokenId.toByteArray() + "\"}}\n"); if (spy.count() == 0) { spy.wait(); } @@ -554,11 +541,40 @@ void TestJSONRPC::testRevokeToken() response = jsonDoc.toVariant().toMap(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); - // Do a call with the now removed token, it should be forbidden + // Get all the tokens and see if it's down by one. + spy.clear(); + qCDebug(dcTests()) << "Calling Tokens"; + m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"Users.GetTokens\"}\n"); + if (spy.count() == 0) { + spy.wait(); + } + QVERIFY(spy.count() == 1); + jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); + response = jsonDoc.toVariant().toMap(); + QCOMPARE(response.value("status").toString(), QStringLiteral("success")); + tokenList = response.value("params").toMap().value("tokenInfoList").toList(); + qCDebug(dcTests()) << "have tokens:" << tokenList; + QCOMPARE(tokenList.count(), 1); + + // Do a handshake with the old removed token, it should work, but telling us the token is invalid + spy.clear(); + disconnectedSpy.clear(); + qCDebug(dcTests()) << "Calling Hello with now removed token"; + m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + oldToken + "\", \"method\": \"JSONRPC.Hello\"}\n"); + if (spy.count() == 0) { + spy.wait(); + } + QVERIFY(spy.count() == 1); + jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); + response = jsonDoc.toVariant().toMap(); + QCOMPARE(response.value("status").toString(), QStringLiteral("success")); + QCOMPARE(response.value("params").toMap().value("authenticated").toBool(), false); + + // Do a version call with the old removed token, it should be forbidden spy.clear(); disconnectedSpy.clear(); qCDebug(dcTests()) << "Calling Version with now removed token"; - m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + newToken + "\", \"method\": \"JSONRPC.Version\"}\n"); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + oldToken + "\", \"method\": \"JSONRPC.Version\"}\n"); if (spy.count() == 0) { spy.wait(); } @@ -584,16 +600,16 @@ void TestJSONRPC::testBasicCall_data() QTest::addColumn("idValid"); QTest::addColumn("valid"); - QTest::newRow("valid call 1") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\"}") << true << true; - QTest::newRow("valid call 2") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\"}\n") << true << true; - QTest::newRow("valid call 3") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\"}\n\n\n\n") << true << true; - QTest::newRow("missing id") << QByteArray("{\"method\":\"JSONRPC.Introspect\"}\n") << false << false; - QTest::newRow("missing method") << QByteArray("{\"id\":42}\n") << true << false; - QTest::newRow("borked") << QByteArray("{\"id\":42, \"method\":\"JSO}\n") << false << false; + QTest::newRow("valid call 1") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\", \"token\": \"" + m_apiToken + "\"}") << true << true; + QTest::newRow("valid call 2") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\", \"token\": \"" + m_apiToken + "\"}\n") << true << true; + QTest::newRow("valid call 3") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\", \"token\": \"" + m_apiToken + "\"}\n\n\n\n") << true << true; + QTest::newRow("missing id") << QByteArray("{\"method\":\"JSONRPC.Introspect\", \"token\": \"" + m_apiToken + "\"}\n") << false << false; + QTest::newRow("missing method") << QByteArray("{\"id\":42, \"token\": \"" + m_apiToken + "\"}\n") << true << false; + QTest::newRow("borked") << QByteArray("{\"id\":42,, \"token\": \"" + m_apiToken + "\" \"method\":\"JSO}\n") << false << false; QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Foobar\", \"token\": \"" + m_apiToken + "\"}\n") << true << false; QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"FOO.Introspect\", \"token\": \"" + m_apiToken + "\"}\n") << true << false; QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"JSONRPCIntrospect\", \"token\": \"" + m_apiToken + "\"}\n") << true << false; - QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\", \"params\":{\"törööö\":\"chooo-chooo\"}}\n") << true << false; + QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\", \"token\": \"" + m_apiToken + "\", \"params\":{\"törööö\":\"chooo-chooo\"}}\n") << true << false; } void TestJSONRPC::testBasicCall() @@ -980,7 +996,7 @@ void TestJSONRPC::testPushButtonAuth() int transactionId = response.toMap().value("params").toMap().value("transactionId").toInt(); // Setup connection to mock client - QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); + QSignalSpy clientSpy(m_mockTcpServer, &MockTcpServer::outgoingData); pushButtonAgent.sendButtonPressed(); @@ -989,8 +1005,14 @@ void TestJSONRPC::testPushButtonAuth() QCOMPARE(rsp.value("params").toMap().value("transactionId").toInt(), transactionId); QVERIFY2(!rsp.value("params").toMap().value("token").toByteArray().isEmpty(), "Token not in push button auth notification"); -} + m_apiToken = rsp.value("params").toMap().value("token").toByteArray(); + + qCDebug(dcTests()) << "Invoking Version"; + // Test a regular call to verify we're actually authenticated + response = injectAndWait("JSONRPC.Version"); + QVERIFY2(response.toMap().value("status").toString() == "success", "JSONRPC.Version call failed after push button auth!"); +} void TestJSONRPC::testPushButtonAuthInterrupt() @@ -1024,7 +1046,7 @@ void TestJSONRPC::testPushButtonAuthInterrupt() clientSpy.clear(); params.clear(); params.insert("deviceName", "mallory"); - response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, malloryId); + response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, malloryId, ""); QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true); int transactionId2 = response.toMap().value("params").toMap().value("transactionId").toInt(); @@ -1097,6 +1119,7 @@ void TestJSONRPC::testPushButtonAuthInterrupt() QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId3); QCOMPARE(notification.value("params").toMap().value("success").toBool(), true); QVERIFY2(!notification.value("params").toMap().value("token").toByteArray().isEmpty(), "Token is empty while it shouldn't be"); + } void TestJSONRPC::testPushButtonAuthConnectionDrop() @@ -1116,7 +1139,7 @@ void TestJSONRPC::testPushButtonAuthConnectionDrop() // request push button auth for client 1 (alice) and check for OK reply QVariantMap params; params.insert("deviceName", "alice"); - QVariant response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, aliceId); + QVariant response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, aliceId, ""); QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true); // Disconnect alice @@ -1133,7 +1156,7 @@ void TestJSONRPC::testPushButtonAuthConnectionDrop() // request push button auth for client 2 (bob) and check for OK reply params.clear(); params.insert("deviceName", "bob"); - response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, bobId); + response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, bobId, ""); QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true); int transactionId = response.toMap().value("params").toMap().value("transactionId").toInt(); @@ -1154,7 +1177,6 @@ void TestJSONRPC::testPushButtonAuthConnectionDrop() QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId); QCOMPARE(notification.value("params").toMap().value("success").toBool(), true); QVERIFY2(!notification.value("params").toMap().value("token").toByteArray().isEmpty(), "Token is empty while it shouldn't be"); - } void TestJSONRPC::testInitialSetupWithPushButtonAuth() @@ -1194,7 +1216,7 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() QVariantMap params; params.insert("deviceName", "alice"); - response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, aliceId); + response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, aliceId, ""); QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true); int transactionId = response.toMap().value("params").toMap().value("transactionId").toInt(); diff --git a/tests/auto/usermanager/testusermanager.cpp b/tests/auto/usermanager/testusermanager.cpp index 5901a222..1dde9812 100644 --- a/tests/auto/usermanager/testusermanager.cpp +++ b/tests/auto/usermanager/testusermanager.cpp @@ -197,6 +197,8 @@ void TestUsermanager::createUser() void TestUsermanager::authenticate() { m_apiToken.clear(); + injectAndWait("JSONRPC.Hello"); + createUser(); QVariantMap params; @@ -237,6 +239,13 @@ void TestUsermanager::authenticatePushButton() QCOMPARE(rsp.value("params").toMap().value("transactionId").toInt(), transactionId); QVERIFY2(!rsp.value("params").toMap().value("token").toByteArray().isEmpty(), "Token not in push button auth notification"); + + m_apiToken = rsp.value("params").toMap().value("token").toByteArray(); + + qCDebug(dcTests()) << "Invoking Version"; + // Test a regular call to verify we're actually authenticated + response = injectAndWait("JSONRPC.Version"); + QVERIFY2(response.toMap().value("status").toString() == "success", "JSONRPC.Version call failed after push button auth!"); } void TestUsermanager::authenticatePushButtonAuthInterrupt() @@ -269,7 +278,7 @@ void TestUsermanager::authenticatePushButtonAuthInterrupt() clientSpy.clear(); params.clear(); params.insert("deviceName", "mallory"); - response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, malloryId); + response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, malloryId, ""); QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true); int transactionId2 = response.toMap().value("params").toMap().value("transactionId").toInt(); @@ -361,7 +370,7 @@ void TestUsermanager::authenticatePushButtonAuthConnectionDrop() // request push button auth for client 1 (alice) and check for OK reply QVariantMap params; params.insert("deviceName", "alice"); - QVariant response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, aliceId); + QVariant response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, aliceId, ""); QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true); // Disconnect alice @@ -378,7 +387,7 @@ void TestUsermanager::authenticatePushButtonAuthConnectionDrop() // request push button auth for client 2 (bob) and check for OK reply params.clear(); params.insert("deviceName", "bob"); - response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, bobId); + response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, bobId, ""); QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true); int transactionId = response.toMap().value("params").toMap().value("transactionId").toInt(); diff --git a/tests/auto/websocketserver/testwebsocketserver.cpp b/tests/auto/websocketserver/testwebsocketserver.cpp index 09f72bcd..6c7ad2eb 100644 --- a/tests/auto/websocketserver/testwebsocketserver.cpp +++ b/tests/auto/websocketserver/testwebsocketserver.cpp @@ -83,7 +83,7 @@ void TestWebSocketServer::initTestCase() config.address = "127.0.0.1"; config.port = 4444; config.sslEnabled = true; - config.authenticationEnabled = true; + config.authenticationEnabled = false; NymeaCore::instance()->configuration()->setWebSocketServerConfiguration(config); } diff --git a/tests/testlib/nymeatestbase.cpp b/tests/testlib/nymeatestbase.cpp index ebbd8acf..0fd87b72 100644 --- a/tests/testlib/nymeatestbase.cpp +++ b/tests/testlib/nymeatestbase.cpp @@ -87,6 +87,11 @@ void NymeaTestBase::initTestCase(const QString &loggingRules) qApp->processEvents(); qCDebug(dcTests()) << "Nymea core instance initialized. Creating dummy user."; + foreach (const UserInfo &userInfo, NymeaCore::instance()->userManager()->users()) { + NymeaCore::instance()->userManager()->removeUser(userInfo.username()); + } + NymeaCore::instance()->userManager()->removeUser(""); + // Yes, we're intentionally mixing upper/lower case email here... username should not be case sensitive NymeaCore::instance()->userManager()->removeUser("dummy"); NymeaCore::instance()->userManager()->createUser("dummy", "DummyPW1!", "dummy@guh.io", "Dummy", Types::PermissionScopeAdmin); @@ -128,13 +133,13 @@ void NymeaTestBase::cleanup() } } -QVariant NymeaTestBase::injectAndWait(const QString &method, const QVariantMap ¶ms, const QUuid &clientId) +QVariant NymeaTestBase::injectAndWait(const QString &method, const QVariantMap ¶ms, const QUuid &clientId, const QByteArray &tokenOverride) { QVariantMap call; call.insert("id", m_commandId); call.insert("method", method); call.insert("params", params); - call.insert("token", m_apiToken); + call.insert("token", tokenOverride == "default" ? m_apiToken : tokenOverride); QJsonDocument jsonDoc = QJsonDocument::fromVariant(call); QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); diff --git a/tests/testlib/nymeatestbase.h b/tests/testlib/nymeatestbase.h index 251c5d5c..80d41441 100644 --- a/tests/testlib/nymeatestbase.h +++ b/tests/testlib/nymeatestbase.h @@ -56,7 +56,7 @@ protected slots: void cleanup(); protected: - QVariant injectAndWait(const QString &method, const QVariantMap ¶ms = QVariantMap(), const QUuid &clientId = QUuid()); + QVariant injectAndWait(const QString &method, const QVariantMap ¶ms = QVariantMap(), const QUuid &clientId = QUuid(), const QByteArray &tokenOverride = "default"); QVariant checkNotification(const QSignalSpy &spy, const QString ¬ification); QVariantList checkNotifications(const QSignalSpy &spy, const QString ¬ification);