add methods to revoke existing tokens again
This commit is contained in:
parent
08727a07ba
commit
53cca56fd3
@ -113,6 +113,19 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject
|
||||
returns.insert("o:token", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||
setReturns("Authenticate", returns);
|
||||
|
||||
params.clear(); returns.clear();
|
||||
setDescription("Tokens", "Return a list of if TokenInfo objects all the tokens for the current user.");
|
||||
setParams("Tokens", params);
|
||||
returns.insert("tokenInfoList", QVariantList() << JsonTypes::tokenInfoRef());
|
||||
setReturns("Tokens", returns);
|
||||
|
||||
params.clear(); returns.clear();
|
||||
setDescription("RemoveToken", "Revoke access for a given token.");
|
||||
params.insert("tokenId", JsonTypes::basicTypeToString(JsonTypes::Uuid));
|
||||
setParams("RemoveToken", params);
|
||||
returns.insert("error", JsonTypes::userErrorRef());
|
||||
setReturns("RemoveToken", returns);
|
||||
|
||||
QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
@ -189,6 +202,35 @@ JsonReply *JsonRPCServer::Authenticate(const QVariantMap ¶ms)
|
||||
return createReply(ret);
|
||||
}
|
||||
|
||||
JsonReply *JsonRPCServer::Tokens(const QVariantMap ¶ms) const
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
QByteArray token = property("token").toByteArray();
|
||||
|
||||
QString username = GuhCore::instance()->userManager()->userForToken(token);
|
||||
if (username.isEmpty()) {
|
||||
// There *really* should be a user for the token in the DB
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
QList<TokenInfo> tokens = GuhCore::instance()->userManager()->tokens(username);
|
||||
QVariantList retList;
|
||||
foreach (const TokenInfo &tokenInfo, tokens) {
|
||||
retList << JsonTypes::packTokenInfo(tokenInfo);
|
||||
}
|
||||
QVariantMap retMap;
|
||||
retMap.insert("tokenInfoList", retList);
|
||||
return createReply(retMap);
|
||||
}
|
||||
|
||||
JsonReply *JsonRPCServer::RemoveToken(const QVariantMap ¶ms)
|
||||
{
|
||||
QUuid tokenId = params.value("tokenId").toUuid();
|
||||
UserManager::UserError error = GuhCore::instance()->userManager()->removeToken(tokenId);
|
||||
QVariantMap ret;
|
||||
ret.insert("error", JsonTypes::userErrorToString(error));
|
||||
return createReply(ret);
|
||||
}
|
||||
|
||||
/*! Returns the list of registred \l{JsonHandler}{JsonHandlers} and their name.*/
|
||||
QHash<QString, JsonHandler *> JsonRPCServer::handlers() const
|
||||
{
|
||||
@ -324,6 +366,7 @@ void JsonRPCServer::processData(const QUuid &clientId, const QByteArray &data)
|
||||
|
||||
// Hack: attach clientId to handler to be able to handle the JSONRPC methods. Do not use this outside of jsonrpcserver
|
||||
handler->setProperty("clientId", clientId);
|
||||
handler->setProperty("token", message.value("token").toByteArray());
|
||||
|
||||
JsonReply *reply;
|
||||
QMetaObject::invokeMethod(handler, method.toLatin1().data(), Q_RETURN_ARG(JsonReply*, reply), Q_ARG(QVariantMap, params));
|
||||
|
||||
@ -53,6 +53,8 @@ public:
|
||||
|
||||
Q_INVOKABLE JsonReply *CreateUser(const QVariantMap ¶ms);
|
||||
Q_INVOKABLE JsonReply *Authenticate(const QVariantMap ¶ms);
|
||||
Q_INVOKABLE JsonReply *Tokens(const QVariantMap ¶ms) const;
|
||||
Q_INVOKABLE JsonReply *RemoveToken(const QVariantMap ¶ms);
|
||||
|
||||
QHash<QString, JsonHandler *> handlers() const;
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
|
||||
* Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> *
|
||||
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
|
||||
* *
|
||||
* This file is part of guh. *
|
||||
* *
|
||||
@ -117,6 +118,7 @@ QVariantMap JsonTypes::s_repeatingOption;
|
||||
QVariantMap JsonTypes::s_wirelessAccessPoint;
|
||||
QVariantMap JsonTypes::s_wiredNetworkDevice;
|
||||
QVariantMap JsonTypes::s_wirelessNetworkDevice;
|
||||
QVariantMap JsonTypes::s_tokenInfo;
|
||||
|
||||
void JsonTypes::init()
|
||||
{
|
||||
@ -351,6 +353,12 @@ void JsonTypes::init()
|
||||
s_wirelessNetworkDevice.insert("bitRate", basicTypeToString(QVariant::String));
|
||||
s_wirelessNetworkDevice.insert("o:currentAccessPoint", wirelessAccessPointRef());
|
||||
|
||||
// TokenInfo
|
||||
s_tokenInfo.insert("id", basicTypeToString(QVariant::Uuid));
|
||||
s_tokenInfo.insert("userName", basicTypeToString(QVariant::String));
|
||||
s_tokenInfo.insert("deviceName", basicTypeToString(QVariant::String));
|
||||
s_tokenInfo.insert("creationTime", basicTypeToString(QVariant::UInt));
|
||||
|
||||
s_initialized = true;
|
||||
}
|
||||
|
||||
@ -428,6 +436,7 @@ QVariantMap JsonTypes::allTypes()
|
||||
allTypes.insert("WirelessAccessPoint", wirelessAccessPointDescription());
|
||||
allTypes.insert("WiredNetworkDevice", wiredNetworkDeviceDescription());
|
||||
allTypes.insert("WirelessNetworkDevice", wirelessNetworkDeviceDescription());
|
||||
allTypes.insert("TokenInfo", tokenInfoDescription());
|
||||
|
||||
return allTypes;
|
||||
}
|
||||
@ -1167,6 +1176,16 @@ QVariantList JsonTypes::packPlugins()
|
||||
return pluginsList;
|
||||
}
|
||||
|
||||
QVariantMap JsonTypes::packTokenInfo(const TokenInfo &tokenInfo)
|
||||
{
|
||||
QVariantMap ret;
|
||||
ret.insert("id", tokenInfo.id().toString());
|
||||
ret.insert("userName", tokenInfo.username());
|
||||
ret.insert("deviceName", tokenInfo.deviceName());
|
||||
ret.insert("creationTime", tokenInfo.creationTime().toTime_t());
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! Returns the type string for the given \a type. */
|
||||
QString JsonTypes::basicTypeToString(const QVariant::Type &type)
|
||||
{
|
||||
@ -1794,6 +1813,12 @@ QPair<bool, QString> JsonTypes::validateVariant(const QVariant &templateVariant,
|
||||
qCWarning(dcJsonRpc) << "WirelessNetworkDevice not matching";
|
||||
return result;
|
||||
}
|
||||
} else if (refName == tokenInfoRef()) {
|
||||
QPair<bool, QString> result = validateMap(tokenInfoDescription(), variant.toMap());
|
||||
if (!result.first) {
|
||||
qCWarning(dcJsonRpc) << "TokenInfo not matching";
|
||||
return result;
|
||||
}
|
||||
} else if (refName == basicTypeRef()) {
|
||||
QPair<bool, QString> result = validateBasicType(variant);
|
||||
if (!result.first) {
|
||||
|
||||
@ -165,6 +165,7 @@ public:
|
||||
DECLARE_OBJECT(wirelessAccessPoint, "WirelessAccessPoint")
|
||||
DECLARE_OBJECT(wiredNetworkDevice, "WiredNetworkDevice")
|
||||
DECLARE_OBJECT(wirelessNetworkDevice, "WirelessNetworkDevice")
|
||||
DECLARE_OBJECT(tokenInfo, "TokenInfo")
|
||||
|
||||
// pack types
|
||||
static QVariantMap packEventType(const EventType &eventType);
|
||||
@ -197,7 +198,6 @@ public:
|
||||
static QVariantMap packWiredNetworkDevice(WiredNetworkDevice *networkDevice);
|
||||
static QVariantMap packWirelessNetworkDevice(WirelessNetworkDevice *networkDevice);
|
||||
|
||||
// pack resources
|
||||
static QVariantList packRules(const QList<Rule> rules);
|
||||
static QVariantList packCreateMethods(DeviceClass::CreateMethods createMethods);
|
||||
static QVariantList packSupportedVendors();
|
||||
@ -219,6 +219,8 @@ public:
|
||||
static QVariantList packEventTypes(const DeviceClass &deviceClass);
|
||||
static QVariantList packPlugins();
|
||||
|
||||
static QVariantMap packTokenInfo(const TokenInfo &tokenInfo);
|
||||
|
||||
static QString basicTypeToString(const QVariant::Type &type);
|
||||
|
||||
// unpack Types
|
||||
|
||||
@ -57,6 +57,7 @@ HEADERS += $$top_srcdir/server/guhcore.h \
|
||||
$$top_srcdir/server/networkmanager/networkconnection.h \
|
||||
$$top_srcdir/server/networkmanager/wirednetworkdevice.h \
|
||||
$$top_srcdir/server/usermanager.h \
|
||||
$$PWD/tokeninfo.h
|
||||
|
||||
|
||||
SOURCES += $$top_srcdir/server/guhcore.cpp \
|
||||
@ -113,4 +114,5 @@ SOURCES += $$top_srcdir/server/guhcore.cpp \
|
||||
$$top_srcdir/server/networkmanager/networkconnection.cpp \
|
||||
$$top_srcdir/server/networkmanager/wirednetworkdevice.cpp \
|
||||
$$top_srcdir/server/usermanager.cpp \
|
||||
$$PWD/tokeninfo.cpp
|
||||
|
||||
|
||||
54
server/tokeninfo.cpp
Normal file
54
server/tokeninfo.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
|
||||
* *
|
||||
* This file is part of guh. *
|
||||
* *
|
||||
* Guh is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, version 2 of the License. *
|
||||
* *
|
||||
* Guh is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "tokeninfo.h"
|
||||
|
||||
namespace guhserver {
|
||||
|
||||
TokenInfo::TokenInfo(const QUuid &id, const QString &username, const QDateTime &creationTime, const QString &deviceName):
|
||||
m_id(id),
|
||||
m_username(username),
|
||||
m_creationTime(creationTime),
|
||||
m_deviceName(deviceName)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QUuid TokenInfo::id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
QString TokenInfo::username() const
|
||||
{
|
||||
return m_username;
|
||||
}
|
||||
|
||||
QDateTime TokenInfo::creationTime() const
|
||||
{
|
||||
return m_creationTime;
|
||||
}
|
||||
|
||||
QString TokenInfo::deviceName() const
|
||||
{
|
||||
return m_deviceName;
|
||||
}
|
||||
|
||||
}
|
||||
48
server/tokeninfo.h
Normal file
48
server/tokeninfo.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
|
||||
* *
|
||||
* This file is part of guh. *
|
||||
* *
|
||||
* Guh is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, version 2 of the License. *
|
||||
* *
|
||||
* Guh is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef TOKENINFO_H
|
||||
#define TOKENINFO_H
|
||||
|
||||
#include <QUuid>
|
||||
#include <QDateTime>
|
||||
|
||||
namespace guhserver {
|
||||
|
||||
class TokenInfo
|
||||
{
|
||||
public:
|
||||
TokenInfo(const QUuid &id, const QString &username, const QDateTime &creationTime, const QString &deviceName);
|
||||
|
||||
QUuid id() const;
|
||||
QString username() const;
|
||||
QDateTime creationTime() const;
|
||||
QString deviceName() const;
|
||||
|
||||
private:
|
||||
QUuid m_id;
|
||||
QString m_username;
|
||||
QDateTime m_creationTime;
|
||||
QString m_deviceName;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TOKENINFO_H
|
||||
@ -126,7 +126,8 @@ QByteArray UserManager::authenticate(const QString &username, const QString &pas
|
||||
}
|
||||
|
||||
QByteArray token = QCryptographicHash::hash(QUuid::createUuid().toByteArray(), QCryptographicHash::Sha256).toBase64();
|
||||
QString storeTokenQuery = QString("INSERT INTO tokens(username, token, creationdate, devicename) VALUES(\"%1\", \"%2\", \"%3\", \"%4\");")
|
||||
QString storeTokenQuery = QString("INSERT INTO tokens(id, username, token, creationdate, devicename) VALUES(\"%1\", \"%2\", \"%3\", \"%4\", \"%5\");")
|
||||
.arg(QUuid::createUuid().toString())
|
||||
.arg(username)
|
||||
.arg(QString::fromUtf8(token))
|
||||
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
|
||||
@ -140,10 +141,70 @@ QByteArray UserManager::authenticate(const QString &username, const QString &pas
|
||||
return token;
|
||||
}
|
||||
|
||||
QString UserManager::userForToken(const QByteArray &token) const
|
||||
{
|
||||
if (!validateToken(token)) {
|
||||
qCWarning(dcUserManager) << "Token failed character validation:" << token;
|
||||
return QString();
|
||||
}
|
||||
QString getUserQuery = QString("SELECT * FROM tokens WHERE token = \"%1\";")
|
||||
.arg(QString::fromUtf8(token));
|
||||
QSqlQuery result = m_db.exec(getUserQuery);
|
||||
if (m_db.lastError().type() != QSqlError::NoError) {
|
||||
qCWarning(dcUserManager) << "Error fetching username for token:" << m_db.lastError().databaseText() << m_db.lastError().driverText() << getUserQuery;
|
||||
return QString();
|
||||
}
|
||||
if (!result.first()) {
|
||||
qCWarning(dcUserManager) << "No such token in DB:" << token;
|
||||
return QString();
|
||||
}
|
||||
|
||||
return result.value("username").toString();
|
||||
}
|
||||
|
||||
QList<TokenInfo> UserManager::tokens(const QString &username) const
|
||||
{
|
||||
QList<TokenInfo> ret;
|
||||
|
||||
if (!validateUsername(username)) {
|
||||
qCWarning(dcUserManager) << "Username did not pass validation:" << username;
|
||||
return ret;
|
||||
}
|
||||
QString getTokensQuery = QString("SELECT id, username, creationdate, deviceName FROM tokens WHERE username = \"%1\";")
|
||||
.arg(username);
|
||||
QSqlQuery result = m_db.exec(getTokensQuery);
|
||||
if (m_db.lastError().type() != QSqlError::NoError) {
|
||||
qCWarning(dcUserManager) << "Query for tokens failed:" << m_db.lastError().databaseText() << m_db.lastError().driverText() << getTokensQuery;
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (result.next()) {
|
||||
ret << TokenInfo(result.value("id").toUuid(), result.value("username").toString(), result.value("creationdate").toDateTime(), result.value("devicename").toString());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
UserManager::UserError UserManager::removeToken(const QUuid &tokenId)
|
||||
{
|
||||
QString removeTokenQuery = QString("DELETE FROM tokens WHERE id = \"%1\";")
|
||||
.arg(tokenId.toString());
|
||||
QSqlQuery result = m_db.exec(removeTokenQuery);
|
||||
if (m_db.lastError().type() != QSqlError::NoError) {
|
||||
qCWarning(dcUserManager) << "Removing token failed:" << m_db.lastError().databaseText() << m_db.lastError().driverText() << removeTokenQuery;
|
||||
return UserErrorBackendError;
|
||||
}
|
||||
if (result.numRowsAffected() != 1) {
|
||||
qCWarning(dcUserManager) << "Token not found in DB";
|
||||
return UserErrorTokenNotFound;
|
||||
}
|
||||
|
||||
qCDebug(dcUserManager) << "Token" << tokenId << "removed from DB";
|
||||
return UserErrorNoError;
|
||||
}
|
||||
|
||||
bool UserManager::verifyToken(const QByteArray &token)
|
||||
{
|
||||
QRegExp validator(QRegExp("(^[a-zA-Z0-9_.+-/=]+$)"));
|
||||
if (!validator.exactMatch(token)) {
|
||||
if (!validateToken(token)) {
|
||||
qCWarning(dcUserManager) << "Token failed character validation" << token;
|
||||
return false;
|
||||
}
|
||||
@ -168,7 +229,7 @@ void UserManager::initDB()
|
||||
m_db.exec("CREATE TABLE users (username VARCHAR(40) UNIQUE, password VARCHAR(100), salt VARCHAR(100));");
|
||||
}
|
||||
if (!m_db.tables().contains("tokens")) {
|
||||
m_db.exec("CREATE TABLE tokens (username VARCHAR(40), token VARCHAR(100) UNIQUE, creationdate DATETIME, devicename VARCHAR(40));");
|
||||
m_db.exec("CREATE TABLE tokens (id VARCHAR(40) UNIQUE, username VARCHAR(40), token VARCHAR(100) UNIQUE, creationdate DATETIME, devicename VARCHAR(40));");
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,4 +239,10 @@ bool UserManager::validateUsername(const QString &username) const
|
||||
return validator.exactMatch(username);
|
||||
}
|
||||
|
||||
bool UserManager::validateToken(const QByteArray &token) const
|
||||
{
|
||||
QRegExp validator(QRegExp("(^[a-zA-Z0-9_.+-/=]+$)"));
|
||||
return validator.exactMatch(token);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -21,6 +21,8 @@
|
||||
#ifndef USERMANAGER_H
|
||||
#define USERMANAGER_H
|
||||
|
||||
#include "tokeninfo.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QSqlDatabase>
|
||||
|
||||
@ -35,7 +37,9 @@ public:
|
||||
UserErrorBackendError,
|
||||
UserErrorInvalidUserId,
|
||||
UserErrorDuplicateUserId,
|
||||
UserErrorBadPassword
|
||||
UserErrorBadPassword,
|
||||
UserErrorTokenNotFound,
|
||||
UserErrorPermissionDenied
|
||||
};
|
||||
Q_ENUM(UserError)
|
||||
|
||||
@ -47,12 +51,16 @@ public:
|
||||
UserError removeUser(const QString &username);
|
||||
|
||||
QByteArray authenticate(const QString &username, const QString &password, const QString &deviceName);
|
||||
QString userForToken(const QByteArray &token) const;
|
||||
QList<TokenInfo> tokens(const QString &username) const;
|
||||
guhserver::UserManager::UserError removeToken(const QUuid &tokenId);
|
||||
|
||||
bool verifyToken(const QByteArray &token);
|
||||
|
||||
private:
|
||||
void initDB();
|
||||
bool validateUsername(const QString &username) const;
|
||||
bool validateToken(const QByteArray &token) const;
|
||||
|
||||
private:
|
||||
QSqlDatabase m_db;
|
||||
|
||||
@ -403,6 +403,15 @@
|
||||
"types": "Object"
|
||||
}
|
||||
},
|
||||
"JSONRPC.RemoveToken": {
|
||||
"description": "Revoke access for a given token.",
|
||||
"params": {
|
||||
"tokenId": "Uuid"
|
||||
},
|
||||
"returns": {
|
||||
"error": "$ref:UserError"
|
||||
}
|
||||
},
|
||||
"JSONRPC.SetNotificationStatus": {
|
||||
"description": "Enable/Disable notifications for this connections.",
|
||||
"params": {
|
||||
@ -412,6 +421,16 @@
|
||||
"enabled": "Bool"
|
||||
}
|
||||
},
|
||||
"JSONRPC.Tokens": {
|
||||
"description": "Return a list of if TokenInfo objects all the tokens for the current user.",
|
||||
"params": {
|
||||
},
|
||||
"returns": {
|
||||
"tokenInfoList": [
|
||||
"$ref:TokenInfo"
|
||||
]
|
||||
}
|
||||
},
|
||||
"JSONRPC.Version": {
|
||||
"description": "Version of this Guh/JSONRPC interface.",
|
||||
"params": {
|
||||
@ -1291,6 +1310,12 @@
|
||||
"o:repeating": "$ref:RepeatingOption",
|
||||
"o:time": "Time"
|
||||
},
|
||||
"TokenInfo": {
|
||||
"creationTime": "Uint",
|
||||
"deviceName": "String",
|
||||
"id": "Uuid",
|
||||
"userName": "String"
|
||||
},
|
||||
"Unit": [
|
||||
"UnitNone",
|
||||
"UnitSeconds",
|
||||
@ -1348,7 +1373,9 @@
|
||||
"UserErrorBackendError",
|
||||
"UserErrorInvalidUserId",
|
||||
"UserErrorDuplicateUserId",
|
||||
"UserErrorBadPassword"
|
||||
"UserErrorBadPassword",
|
||||
"UserErrorTokenNotFound",
|
||||
"UserErrorPermissionDenied"
|
||||
],
|
||||
"ValueOperator": [
|
||||
"ValueOperatorEquals",
|
||||
|
||||
@ -118,6 +118,7 @@ GuhTestBase::GuhTestBase(QObject *parent) :
|
||||
m_mockDevice2Port = 7331 + (qrand() % 1000);
|
||||
QCoreApplication::instance()->setOrganizationName("guh-test");
|
||||
|
||||
GuhCore::instance()->userManager()->removeUser("dummy@guh.io");
|
||||
GuhCore::instance()->userManager()->createUser("dummy@guh.io", "DummyPW1!");
|
||||
m_apiToken = GuhCore::instance()->userManager()->authenticate("dummy@guh.io", "DummyPW1!", "testcase");
|
||||
}
|
||||
|
||||
@ -44,6 +44,8 @@ private slots:
|
||||
|
||||
void testInitialSetup();
|
||||
|
||||
void testRevokeToken();
|
||||
|
||||
void testBasicCall_data();
|
||||
void testBasicCall();
|
||||
|
||||
@ -255,6 +257,101 @@ void TestJSONRPC::testInitialSetup()
|
||||
|
||||
}
|
||||
|
||||
void TestJSONRPC::testRevokeToken()
|
||||
{
|
||||
QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
||||
QVERIFY(spy.isValid());
|
||||
|
||||
// Now get all the tokens
|
||||
spy.clear();
|
||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Tokens\"}");
|
||||
if (spy.count() == 0) {
|
||||
spy.wait();
|
||||
}
|
||||
QVERIFY(spy.count() == 1);
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
||||
QVariantMap response = jsonDoc.toVariant().toMap();
|
||||
qWarning() << "Getting existing Tokens" << response.value("status").toString() << response;
|
||||
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
||||
QVariantList tokenList = response.value("params").toMap().value("tokenInfoList").toList();
|
||||
QCOMPARE(tokenList.count(), 1);
|
||||
QUuid oldTokenId = tokenList.first().toMap().value("id").toUuid();
|
||||
|
||||
// Authenticate and create a new token
|
||||
spy.clear();
|
||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"dummy@guh.io\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}");
|
||||
if (spy.count() == 0) {
|
||||
spy.wait();
|
||||
}
|
||||
QVERIFY(spy.count() == 1);
|
||||
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
||||
response = jsonDoc.toVariant().toMap();
|
||||
qWarning() << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString();
|
||||
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());
|
||||
|
||||
// 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\"}");
|
||||
if (spy.count() == 0) {
|
||||
spy.wait();
|
||||
}
|
||||
QVERIFY(spy.count() == 1);
|
||||
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
||||
response = jsonDoc.toVariant().toMap();
|
||||
qWarning() << "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
|
||||
spy.clear();
|
||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Tokens\"}");
|
||||
if (spy.count() == 0) {
|
||||
spy.wait();
|
||||
}
|
||||
QVERIFY(spy.count() == 1);
|
||||
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
||||
response = jsonDoc.toVariant().toMap();
|
||||
qWarning() << "Calling Tokens" << response.value("status").toString();
|
||||
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
||||
tokenList = response.value("params").toMap().value("tokenInfoList").toList();
|
||||
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
|
||||
spy.clear();
|
||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.RemoveToken\", \"params\": {\"tokenId\": \"" + newTokenId.toByteArray() + "\"}}");
|
||||
if (spy.count() == 0) {
|
||||
spy.wait();
|
||||
}
|
||||
QVERIFY(spy.count() == 1);
|
||||
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
||||
response = jsonDoc.toVariant().toMap();
|
||||
qWarning() << "Calling RemoveToken" << response.value("status").toString() << response;
|
||||
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
||||
|
||||
// Do a call with the now removed token, it should be forbidden
|
||||
spy.clear();
|
||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + newToken + "\", \"method\": \"JSONRPC.Version\"}");
|
||||
if (spy.count() == 0) {
|
||||
spy.wait();
|
||||
}
|
||||
QVERIFY(spy.count() == 1);
|
||||
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
||||
response = jsonDoc.toVariant().toMap();
|
||||
qWarning() << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString();
|
||||
QCOMPARE(response.value("status").toString(), QStringLiteral("unauthorized"));
|
||||
}
|
||||
|
||||
void TestJSONRPC::testBasicCall_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("call");
|
||||
|
||||
Reference in New Issue
Block a user