Bind a client connection to the token given in the hello call.
This is a prerequisite for enabling dispatching notifications based on user permissions.
This commit is contained in:
parent
d43b9dc737
commit
2be6032718
@ -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));
|
||||
}
|
||||
|
||||
@ -113,6 +113,7 @@ private:
|
||||
QHash<QUuid, QByteArray> m_clientBuffers;
|
||||
QHash<QUuid, QStringList> m_clientNotifications;
|
||||
QHash<QUuid, QLocale> m_clientLocales;
|
||||
QHash<QUuid, QByteArray> m_clientTokens;
|
||||
QHash<int, QUuid> m_pushButtonTransactions;
|
||||
QHash<QUuid, QTimer*> m_newConnectionWaitTimers;
|
||||
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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<bool>("idValid");
|
||||
QTest::addColumn<bool>("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();
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
@ -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)));
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user