First tunnel connection

This commit is contained in:
Simon Stürz 2018-08-10 17:11:03 +02:00
parent ca54d5b44d
commit c40aac6808
23 changed files with 505 additions and 166 deletions

View File

@ -17,7 +17,7 @@ QString AwsAuthenticator::name() const
AuthenticationReply *AwsAuthenticator::authenticate(ProxyClient *proxyClient)
{
qCDebug(dcAuthenticator()) << name() << "Start authenticating" << proxyClient << "using token" << proxyClient->token();
qCDebug(dcAuthenticator()) << name() << "Start authenticating" << proxyClient << "using token" << proxyClient->token();
AuthenticationReply *reply = createAuthenticationReply(proxyClient, this);
return reply;
}

View File

@ -35,26 +35,17 @@ void Engine::start()
if (!m_running)
qCDebug(dcEngine()) << "Start server engine";
// Init proxy server
if (m_proxyServer) {
delete m_proxyServer;
m_proxyServer = nullptr;
}
// Clean up
clean();
m_configuration = new ProxyConfiguration(this);
m_proxyServer = new ProxyServer(this);
// Init WebSocketServer
if (m_webSocketServer) {
delete m_webSocketServer;
m_webSocketServer = nullptr;
}
m_webSocketServer = new WebSocketServer(m_sslConfiguration, this);
QUrl websocketServerUrl;
websocketServerUrl.setScheme("wss");
websocketServerUrl.setHost(m_webSocketServerHostAddress.toString());
websocketServerUrl.setPort(m_webSocketServerPort);
m_webSocketServer = new WebSocketServer(m_sslConfiguration, this);
m_webSocketServer->setServerUrl(websocketServerUrl);
m_proxyServer->registerTransportInterface(m_webSocketServer);
@ -65,7 +56,8 @@ void Engine::start()
qCDebug(dcEngine()) << "Starting proxy server";
m_proxyServer->startServer();
QTimer::singleShot(0, this, &Engine::run);
// Set tunning true in the next event loop
QMetaObject::invokeMethod(this, QString("setRunning").toLatin1().data(), Qt::QueuedConnection, Q_ARG(bool, true));
}
void Engine::stop()
@ -73,17 +65,7 @@ void Engine::stop()
if (m_running)
qCDebug(dcEngine()) << "Stop server engine";
if (m_proxyServer) {
m_proxyServer->stopServer();
delete m_proxyServer;
m_proxyServer = nullptr;
}
if (m_webSocketServer) {
delete m_webSocketServer;
m_webSocketServer = nullptr;
}
clean();
setRunning(false);
}
@ -176,6 +158,26 @@ Engine::~Engine()
stop();
}
void Engine::clean()
{
if (m_proxyServer) {
m_proxyServer->stopServer();
delete m_proxyServer;
m_proxyServer = nullptr;
}
if (m_webSocketServer) {
delete m_webSocketServer;
m_webSocketServer = nullptr;
}
if (m_configuration) {
delete m_configuration;
m_configuration = nullptr;
}
}
void Engine::setRunning(bool running)
{
if (m_running == running)
@ -186,9 +188,5 @@ void Engine::setRunning(bool running)
emit runningChanged(m_running);
}
void Engine::run()
{
setRunning(true);
}
}

View File

@ -8,6 +8,7 @@
#include "proxyserver.h"
#include "websocketserver.h"
#include "proxyconfiguration.h"
#include "authentication/authenticator.h"
namespace remoteproxy {
@ -53,18 +54,18 @@ private:
QSslConfiguration m_sslConfiguration;
QUrl m_authenticationServerUrl;
ProxyConfiguration *m_configuration = nullptr;
Authenticator *m_authenticator = nullptr;
ProxyServer *m_proxyServer = nullptr;
WebSocketServer *m_webSocketServer = nullptr;
void setRunning(bool running);
private slots:
void run();
signals:
void runningChanged(bool running);
private slots:
void clean();
void setRunning(bool running);
};
}

View File

@ -66,15 +66,16 @@ void AuthenticationHandler::onAuthenticationFinished()
if (authenticationReply->error() != Authenticator::AuthenticationErrorNoError) {
qCWarning(dcJsonRpc()) << "Authentication error occured" << authenticationReply->error();
jsonReply->setSuccess(false);
authenticationReply->proxyClient()->setAuthenticated(false);
} else {
// Successfully authenticated
authenticationReply->proxyClient()->setAuthenticated(true);
jsonReply->setSuccess(true);
}
jsonReply->setData(errorToReply(authenticationReply->error()));
jsonReply->finished();
// Set client authenticated
authenticationReply->proxyClient()->setAuthenticated(authenticationReply->error() == Authenticator::AuthenticationErrorNoError);
}
}

View File

@ -100,18 +100,6 @@ JsonReply *JsonRpcServer::Introspect(const QVariantMap &params, ProxyClient *pro
return createReply(data);
}
void JsonRpcServer::sendNotification(const QString &nameSpace, const QString &method, const QVariantMap &params, ProxyClient *proxyClient)
{
QVariantMap notification;
notification.insert("id", m_notificationId++);
notification.insert("notification", nameSpace + "." + method);
notification.insert("params", params);
QByteArray data = QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact);
qCDebug(dcJsonRpcTraffic()) << "Sending notification:" << data;
proxyClient->interface()->sendData(proxyClient->clientId(), data);
}
void JsonRpcServer::sendResponse(ProxyClient *client, int commandId, const QVariantMap &params)
{
QVariantMap response;
@ -290,4 +278,16 @@ void JsonRpcServer::processData(ProxyClient *proxyClient, const QByteArray &data
}
}
void JsonRpcServer::sendNotification(const QString &nameSpace, const QString &method, const QVariantMap &params, ProxyClient *proxyClient)
{
QVariantMap notification;
notification.insert("id", m_notificationId++);
notification.insert("notification", nameSpace + "." + method);
notification.insert("params", params);
QByteArray data = QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact);
qCDebug(dcJsonRpcTraffic()) << "Sending notification:" << data;
proxyClient->interface()->sendData(proxyClient->clientId(), data);
}
}

View File

@ -24,8 +24,6 @@ public:
Q_INVOKABLE JsonReply *Hello(const QVariantMap &params, ProxyClient *proxyClient = nullptr) const;
Q_INVOKABLE JsonReply *Introspect(const QVariantMap &params, ProxyClient *proxyClient = nullptr) const;
void sendNotification(const QString &nameSpace, const QString &method, const QVariantMap &params, ProxyClient *proxyClient = nullptr);
signals:
void TunnelEstablished(const QVariantMap &params);
@ -33,7 +31,7 @@ private:
QHash<QString, JsonHandler *> m_handlers;
QHash<JsonReply *, ProxyClient *> m_asyncReplies;
QList<ProxyClient *> m_clients;
int m_notificationId;
int m_notificationId = 0;
void sendResponse(ProxyClient *client, int commandId, const QVariantMap &params = QVariantMap());
void sendErrorResponse(ProxyClient *client, int commandId, const QString &error);
@ -54,6 +52,7 @@ public slots:
// Process data from client
void processData(ProxyClient *proxyClient, const QByteArray &data);
void sendNotification(const QString &nameSpace, const QString &method, const QVariantMap &params, ProxyClient *proxyClient = nullptr);
};

View File

@ -18,7 +18,8 @@ HEADERS += \
authentication/authenticator.h \
authentication/awsauthenticator.h \
authentication/authenticationreply.h \
proxyconfiguration.h
proxyconfiguration.h \
tunnelconnection.h
SOURCES += \
engine.cpp \
@ -35,7 +36,8 @@ SOURCES += \
authentication/authenticator.cpp \
authentication/awsauthenticator.cpp \
authentication/authenticationreply.cpp \
proxyconfiguration.cpp
proxyconfiguration.cpp \
tunnelconnection.cpp
# install header file with relative subdirectory

View File

@ -8,4 +8,5 @@ Q_LOGGING_CATEGORY(dcWebSocketServer, "WebSocketServer")
Q_LOGGING_CATEGORY(dcWebSocketServerTraffic, "WebSocketServerTraffic")
Q_LOGGING_CATEGORY(dcAuthenticator, "Authenticator")
Q_LOGGING_CATEGORY(dcProxyServer, "ProxyServer")
Q_LOGGING_CATEGORY(dcProxyServerTraffic, "ProxyServerTraffic")

View File

@ -12,5 +12,6 @@ Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServer)
Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServerTraffic)
Q_DECLARE_LOGGING_CATEGORY(dcAuthenticator)
Q_DECLARE_LOGGING_CATEGORY(dcProxyServer)
Q_DECLARE_LOGGING_CATEGORY(dcProxyServerTraffic)
#endif // LOGGINGCATEGORIES_H

View File

@ -81,15 +81,7 @@ void ProxyClient::setToken(const QString &token)
QDebug operator<<(QDebug debug, ProxyClient *proxyClient)
{
debug.nospace() << "ProxyClient(" << proxyClient->interface()->serverName();
debug.nospace() << ", " << proxyClient->clientId().toString() << ") :" << endl;
debug.nospace() << " tunnel: " << proxyClient->isTunnelConnected() << endl;
debug.nospace() << " authenticated: " << proxyClient->isAuthenticated() << endl;
if (!proxyClient->name().isEmpty() && !proxyClient->token().isEmpty() && !proxyClient->uuid().isEmpty()) {
debug.nospace() << " name: " << proxyClient->name() << endl;
debug.nospace() << " uuid: " << proxyClient->uuid() << endl;
debug.nospace() << " token: " << proxyClient->token() << endl;
}
debug.nospace() << ", " << proxyClient->clientId().toString() << ") ";
return debug;
}

View File

@ -1,14 +1,15 @@
#include "proxyserver.h"
#include "loggingcategories.h"
#include <QMetaObject>
#include <QJsonDocument>
namespace remoteproxy {
ProxyServer::ProxyServer(QObject *parent) : QObject(parent)
{
qRegisterMetaType<ProxyClient *>("ProxyClient *");
m_jsonRpcServer = new JsonRpcServer(this);
}
ProxyServer::~ProxyServer()
@ -16,6 +17,11 @@ ProxyServer::~ProxyServer()
qCDebug(dcProxyServer()) << "Shutting down proxy server";
}
bool ProxyServer::running() const
{
return m_running;
}
void ProxyServer::registerTransportInterface(TransportInterface *interface)
{
qCDebug(dcProxyServer()) << "Register transport interface" << interface->serverName();
@ -32,6 +38,30 @@ void ProxyServer::registerTransportInterface(TransportInterface *interface)
m_transportInterfaces.append(interface);
}
void ProxyServer::setRunning(bool running)
{
if (m_running == running)
return;
qCDebug(dcProxyServer()) << "The proxy server is now up and running";
m_running = running;
emit runningChanged();
}
ProxyClient *ProxyServer::getRemoteClient(ProxyClient *proxyClient)
{
if (!m_tunnels.contains(proxyClient->token()))
return nullptr;
if (proxyClient == m_tunnels.value(proxyClient->token()).clientOne()) {
return m_tunnels.value(proxyClient->token()).clientTwo();
} else if (proxyClient == m_tunnels.value(proxyClient->token()).clientTwo()) {
return m_tunnels.value(proxyClient->token()).clientOne();
}
return nullptr;
}
void ProxyServer::sendResponse(TransportInterface *interface, const QUuid &clientId, const QVariantMap &response)
{
QByteArray data = QJsonDocument::fromVariant(response).toJson(QJsonDocument::Compact);
@ -39,6 +69,48 @@ void ProxyServer::sendResponse(TransportInterface *interface, const QUuid &clien
interface->sendData(clientId, data);
}
void ProxyServer::establishTunnel(ProxyClient *firstClient, ProxyClient *secondClient)
{
qCDebug(dcProxyServer()) << "Create tunnel between authenticated clients:";
qCDebug(dcProxyServer()) << " -->" << firstClient << firstClient->name();
qCDebug(dcProxyServer()) << " -->" << secondClient << secondClient->name();
TunnelConnection tunnel(firstClient, secondClient);
if (!tunnel.isValid()) {
qCWarning(dcProxyServer()) << "Invalid tunnel. Could not establish connection.";
// FIXME
}
m_tunnels.insert(tunnel.token(), tunnel);
// Tell both clients the tunnel has been established
QVariantMap notificationParamsFirst;
notificationParamsFirst.insert("name", tunnel.clientTwo()->name());
notificationParamsFirst.insert("uuid", tunnel.clientTwo()->uuid());
QVariantMap notificationParamsSecond;
notificationParamsSecond.insert("name", tunnel.clientOne()->name());
notificationParamsSecond.insert("uuid", tunnel.clientOne()->uuid());
// Make sure the proxy is the first one who knows that the tunnel is connected
firstClient->setTunnelConnected(true);
secondClient->setTunnelConnected(true);
// Notify the clients in the next event loop
QMetaObject::invokeMethod(m_jsonRpcServer, QString("sendNotification").toLatin1().data(), Qt::QueuedConnection,
Q_ARG(QString, "ProxyServer"),
Q_ARG(QString, "TunnelEstablished"),
Q_ARG(QVariantMap, notificationParamsFirst),
Q_ARG(ProxyClient *, tunnel.clientOne()));
QMetaObject::invokeMethod(m_jsonRpcServer, QString("sendNotification").toLatin1().data(), Qt::QueuedConnection,
Q_ARG(QString, "ProxyServer"),
Q_ARG(QString, "TunnelEstablished"),
Q_ARG(QVariantMap, notificationParamsSecond),
Q_ARG(ProxyClient *, tunnel.clientTwo()));
}
void ProxyServer::onClientConnected(const QUuid &clientId)
{
TransportInterface *interface = static_cast<TransportInterface *>(sender());
@ -86,10 +158,9 @@ void ProxyServer::onClientDataAvailable(const QUuid &clientId, const QByteArray
return;
}
qCDebug(dcProxyServer()) << "Client data available" << proxyClient << qUtf8Printable(data);
// If this client is not authenticated yet, and not tunnel connected, pipe the traffic into the json rpc server
if (!proxyClient->isAuthenticated() && !proxyClient->isTunnelConnected()) {
qCDebug(dcProxyServerTraffic()) << "Client data available" << proxyClient << qUtf8Printable(data);
m_jsonRpcServer->processData(proxyClient, data);
return;
}
@ -105,15 +176,42 @@ void ProxyServer::onClientDataAvailable(const QUuid &clientId, const QByteArray
}
if (proxyClient->isAuthenticated() && proxyClient->isTunnelConnected()) {
// TODO: Pipe the traffic to the tunnel client
// Check if there is realy a tunnel for this client
if (!m_tunnels.contains(proxyClient->token())) {
// FIXME: kill all clients, something went wrong
}
ProxyClient *remoteClient = getRemoteClient(proxyClient);
if (!remoteClient) {
// FIXME: kill all clients, something went wrong
}
qCDebug(dcProxyServerTraffic()) << "Pipe data:";
qCDebug(dcProxyServerTraffic()) << " --> from" << proxyClient;
qCDebug(dcProxyServerTraffic()) << " --> to" << remoteClient;
qCDebug(dcProxyServerTraffic()) << " --> data:" << qUtf8Printable(data);
remoteClient->interface()->sendData(remoteClient->clientId(), data);
}
}
void ProxyServer::onProxyClientAuthenticated()
{
ProxyClient *proxyClient = static_cast<ProxyClient *>(sender());
qCDebug(dcProxyServer()) << "Client authenticated" << proxyClient;
m_authenticatedClients.insert(proxyClient->token(), proxyClient);
qCDebug(dcProxyServer()) << " name:" << proxyClient->name();
qCDebug(dcProxyServer()) << " uuid:" << proxyClient->uuid();
// Check if we have an other authenticated client with this token
if (m_authenticatedClients.keys().contains(proxyClient->token())) {
// Found a client with this token
ProxyClient *tunnelEnd = m_authenticatedClients.take(proxyClient->token());
establishTunnel(tunnelEnd, proxyClient);
} else {
// Append and wait for the other client
m_authenticatedClients.insert(proxyClient->token(), proxyClient);
}
}
void ProxyServer::onProxyClientTunnelConnected()

View File

@ -7,6 +7,7 @@
#include "proxyclient.h"
#include "jsonrpcserver.h"
#include "tunnelconnection.h"
#include "transportinterface.h"
namespace remoteproxy {
@ -18,16 +19,33 @@ public:
explicit ProxyServer(QObject *parent = nullptr);
~ProxyServer();
bool running() const;
void registerTransportInterface(TransportInterface *interface);
private:
JsonRpcServer *m_jsonRpcServer = nullptr;
QList<TransportInterface *> m_transportInterfaces;
bool m_running = false;
// Transport ClientId, ProxyClient
QHash<QUuid, ProxyClient *> m_proxyClients;
// Token, ProxyClient
QHash<QString, ProxyClient *> m_authenticatedClients;
// Token, Tunnel
QHash<QString, TunnelConnection> m_tunnels;
void setRunning(bool running);
ProxyClient *getRemoteClient(ProxyClient *proxyClient);
void sendResponse(TransportInterface *interface, const QUuid &clientId, const QVariantMap &response = QVariantMap());
void establishTunnel(ProxyClient *firstClient, ProxyClient *secondClient);
signals:
void runningChanged();
private slots:
void onClientConnected(const QUuid &clientId);

View File

@ -0,0 +1,47 @@
#include "tunnelconnection.h"
namespace remoteproxy {
TunnelConnection::TunnelConnection(ProxyClient *clientOne, ProxyClient *clientTwo):
m_clientOne(clientOne),
m_clientTwo(clientTwo)
{
}
QString TunnelConnection::token() const
{
if (!isValid())
return QString();
return m_clientOne->token();
}
ProxyClient *TunnelConnection::clientOne() const
{
return m_clientOne;
}
ProxyClient *TunnelConnection::clientTwo() const
{
return m_clientTwo;
}
bool TunnelConnection::isValid() const
{
// Both clients have to be valid
if (!m_clientOne || !m_clientTwo)
return false;
// Both clients need the same token
if (m_clientOne->token() != m_clientTwo->token())
return false;
// The clients need to be different
if (m_clientOne == m_clientTwo)
return false;
return true;
}
}

View File

@ -0,0 +1,28 @@
#ifndef TUNNELCONNECTION_H
#define TUNNELCONNECTION_H
#include "proxyclient.h"
namespace remoteproxy {
class TunnelConnection
{
public:
TunnelConnection(ProxyClient *clientOne = nullptr, ProxyClient *clientTwo = nullptr);
QString token() const;
ProxyClient *clientOne() const;
ProxyClient *clientTwo() const;
bool isValid() const;
private:
ProxyClient *m_clientOne = nullptr;
ProxyClient *m_clientTwo = nullptr;
};
}
#endif // TUNNELCONNECTION_H

View File

@ -82,10 +82,17 @@ void JsonRpcClient::processData(const QByteArray &data)
if (dataMap.contains("notification")) {
QStringList notification = dataMap.value("notification").toString().split(".");
QString nameSpace = notification.first();
QString notificationName = notification.last();
QVariantMap notificationParams = dataMap.value("params").toMap();
qCDebug(dcRemoteProxyClientJsonRpc()) << "Notification received" << nameSpace << notification;
qCDebug(dcRemoteProxyClientJsonRpc()) << "Notification received" << nameSpace << notificationName;
if (nameSpace == "ProxyServer" && notificationName == "TunnelEstablished") {
QString clientName = notificationParams.value("name").toString();
QString clientUuid = notificationParams.value("uuid").toString();
emit tunnelEstablished(clientName, clientUuid);
}
}
}
}

View File

@ -33,6 +33,7 @@ private:
void sendRequest(const QVariantMap &request);
signals:
void tunnelEstablished(const QString clientName, const QString &clientUuid);
public slots:
void onConnectedChanged(bool connected);

View File

@ -22,5 +22,4 @@ void ProxyConnection::setAllowSslErrors(bool allowSslErrors)
m_allowSslErrors = allowSslErrors;
}
}

View File

@ -17,7 +17,7 @@ RemoteProxyConnection::RemoteProxyConnection(const QUuid &clientUuid, const QStr
RemoteProxyConnection::~RemoteProxyConnection()
{
cleanUp();
}
RemoteProxyConnection::State RemoteProxyConnection::state() const
@ -64,6 +64,12 @@ bool RemoteProxyConnection::isConnected() const
|| m_state == StateRemoteConnected;
}
bool RemoteProxyConnection::isAuthenticated() const
{
return m_state == StateWaitTunnel
|| m_state == StateRemoteConnected;
}
bool RemoteProxyConnection::isRemoteConnected() const
{
return m_state == StateRemoteConnected;
@ -133,15 +139,21 @@ bool RemoteProxyConnection::sendData(const QByteArray &data)
void RemoteProxyConnection::cleanUp()
{
if (m_jsonClient) {
delete m_jsonClient;
m_jsonClient->deleteLater();
m_jsonClient = nullptr;
}
if (m_connection) {
delete m_connection;
m_connection->deleteLater();
m_connection = nullptr;
}
m_error = ErrorNoError;
m_serverName = QString();
m_proxyServerName = QString();
m_proxyServerVersion = QString();
m_proxyServerApiVersion = QString();
setState(StateDisconnected);
}
@ -167,16 +179,19 @@ void RemoteProxyConnection::setError(RemoteProxyConnection::Error error)
void RemoteProxyConnection::onConnectionChanged(bool isConnected)
{
if (!isConnected) {
emit disconnected();
cleanUp();
} else {
if (isConnected) {
qCDebug(dcRemoteProxyClientConnection()) << "Connected from proxy server.";
setState(StateConnected);
emit connected();
setState(StateInitializing);
JsonReply *reply = m_jsonClient->callHello();
connect(reply, &JsonReply::finished, this, &RemoteProxyConnection::onHelloFinished);
} else {
qCDebug(dcRemoteProxyClientConnection()) << "Disconnected from proxy server.";
emit disconnected();
setState(StateDisconnected);
cleanUp();
}
}
@ -188,6 +203,7 @@ void RemoteProxyConnection::onConnectionDataAvailable(const QByteArray &data)
case StateInitializing:
case StateReady:
case StateAuthenticating:
case StateWaitTunnel:
m_jsonClient->processData(data);
break;
case StateRemoteConnected:
@ -228,10 +244,10 @@ void RemoteProxyConnection::onHelloFinished()
// TODO: verify success
m_serverName = response.value("server").toString();
m_proxyServerName = response.value("name").toString();
m_proxyServerVersion = response.value("version").toString();
m_proxyServerApiVersion = response.value("apiVersion").toString();
m_serverName = responseParams.value("server").toString();
m_proxyServerName = responseParams.value("name").toString();
m_proxyServerVersion = responseParams.value("version").toString();
m_proxyServerApiVersion = responseParams.value("apiVersion").toString();
setState(StateReady);
emit ready();
@ -242,6 +258,17 @@ void RemoteProxyConnection::onAuthenticateFinished()
JsonReply *reply = static_cast<JsonReply *>(sender());
QVariantMap response = reply->response();
qCDebug(dcRemoteProxyClientConnection()) << "Authentication response ready" << response;
// TODO: verify success
setState(StateWaitTunnel);
emit authenticated();
}
void RemoteProxyConnection::onTunnelEstablished(const QString &clientName, const QString &clientUuid)
{
qCDebug(dcRemoteProxyClientConnection()) << "####### Remote connection established with" << clientName << clientUuid;
setState(StateRemoteConnected);
emit remoteConnectionEstablished();
}
bool RemoteProxyConnection::connectServer(const QHostAddress &serverAddress, quint16 port)
@ -249,15 +276,7 @@ bool RemoteProxyConnection::connectServer(const QHostAddress &serverAddress, qui
m_serverAddress = serverAddress;
m_serverPort = port;
if (m_connection) {
delete m_connection;
m_connection = nullptr;
}
if (m_jsonClient) {
delete m_jsonClient;
m_jsonClient = nullptr;
}
cleanUp();
switch (m_connectionType) {
case ConnectionTypeWebSocket:
@ -272,6 +291,7 @@ bool RemoteProxyConnection::connectServer(const QHostAddress &serverAddress, qui
connect(m_connection, &ProxyConnection::sslErrorOccured, this, &RemoteProxyConnection::onConnectionSslError);
m_jsonClient = new JsonRpcClient(m_connection, this);
connect(m_jsonClient, &JsonRpcClient::tunnelEstablished, this, &RemoteProxyConnection::onTunnelEstablished);
qCDebug(dcRemoteProxyClientConnection()) << "Connecting to" << m_connection->serverUrl().toString();
m_connection->connectServer(serverAddress, port);
@ -288,6 +308,8 @@ bool RemoteProxyConnection::authenticate(const QString &token)
return false;
}
setState(StateAuthenticating);
qCDebug(dcRemoteProxyClientConnection()) << "Start authentication using token" << token;
JsonReply *reply = m_jsonClient->callAuthenticate(m_clientUuid, m_clientName, token);
connect(reply, &JsonReply::finished, this, &RemoteProxyConnection::onAuthenticateFinished);
@ -296,10 +318,10 @@ bool RemoteProxyConnection::authenticate(const QString &token)
void RemoteProxyConnection::disconnectServer()
{
if (m_connection)
qCDebug(dcRemoteProxyClientConnection()) << "Disconnect from" << m_connection->serverUrl().toString();
cleanUp();
if (m_connection) {
qCDebug(dcRemoteProxyClientConnection()) << "Disconnecting from" << m_connection->serverUrl().toString();
m_connection->disconnectServer();
}
}
}

View File

@ -46,7 +46,7 @@ public:
};
Q_ENUM(Error)
explicit RemoteProxyConnection(const QUuid &clientUuid, const QString &clientName, ConnectionType connectionType = ConnectionTypeWebSocket, QObject *parent = nullptr);
explicit RemoteProxyConnection(const QUuid &clientUuid, const QString &clientName, ConnectionType connectionType = RemoteProxyConnection::ConnectionTypeWebSocket, QObject *parent = nullptr);
~RemoteProxyConnection();
RemoteProxyConnection::State state() const;
@ -54,6 +54,7 @@ public:
QString errorString() const;
bool isConnected() const;
bool isAuthenticated() const;
bool isRemoteConnected() const;
RemoteProxyConnection::ConnectionType connectionType() const;
@ -100,9 +101,11 @@ private:
signals:
void connected();
void disconnected();
void ready();
void remoteConnectedChanged(bool remoteConnected);
void authenticated();
void remoteConnectionEstablished();
void disconnected();
void stateChanged(RemoteProxyConnection::State state);
void errorOccured(RemoteProxyConnection::Error error);
@ -116,6 +119,7 @@ private slots:
void onHelloFinished();
void onAuthenticateFinished();
void onTunnelEstablished(const QString &clientName, const QString &clientUuid);
public slots:
bool connectServer(const QHostAddress &serverAddress, quint16 port);
@ -126,8 +130,8 @@ public slots:
}
Q_DECLARE_METATYPE(remoteproxyclient::RemoteProxyConnection::State);
Q_DECLARE_METATYPE(remoteproxyclient::RemoteProxyConnection::Error);
Q_DECLARE_METATYPE(remoteproxyclient::RemoteProxyConnection::State);
Q_DECLARE_METATYPE(remoteproxyclient::RemoteProxyConnection::ConnectionType);
#endif // REMOTEPROXYCONNECTOR_H

View File

@ -54,6 +54,7 @@ void WebSocketConnection::onDisconnected()
void WebSocketConnection::onError(QAbstractSocket::SocketError error)
{
qCDebug(dcRemoteProxyClientWebSocket()) << "Socket error occured" << error << m_webSocket->errorString();
emit errorOccured();
}
void WebSocketConnection::onSslError(const QList<QSslError> &errors)
@ -104,6 +105,9 @@ void WebSocketConnection::connectServer(const QHostAddress &address, quint16 por
void WebSocketConnection::disconnectServer()
{
if (!isConnected())
return;
qCDebug(dcRemoteProxyClientWebSocket()) << "Disconnecting from" << serverUrl().toString();
m_webSocket->close();
}

View File

@ -35,7 +35,6 @@ void MockAuthenticator::replyFinished()
setReplyFinished(reply->authenticationReply());
}
AuthenticationReply *MockAuthenticator::authenticate(ProxyClient *proxyClient)
{
qCDebug(dcAuthenticator()) << name() << "Start authentication for" << proxyClient << "using token" << proxyClient->token();

View File

@ -36,6 +36,20 @@ RemoteProxyTests::RemoteProxyTests(QObject *parent) :
m_sslConfiguration.setLocalCertificate(QSslCertificate(certificateData, QSsl::Pem));
m_sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
m_sslConfiguration.setProtocol(QSsl::TlsV1_2OrLater);
m_testToken = "eyJraWQiOiJXdnFFT3prVVh5VDlINzFyRUpoNWdxRnkxNFhnR2l3SFAzVEIzUFQ1V3ZrPSIsImFsZyI6IlJT"
"MjU2In0.eyJzdWIiOiJmZTJmZDNlNC1hMGJhLTQ1OTUtOWRiZS00ZDkxYjRiMjFlMzUiLCJhdWQiOiI4cmpoZ"
"mRsZjlqZjFzdW9rMmpjcmx0ZDZ2IiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV2ZW50X2lkIjoiN2Y5NTRiNm"
"ItOTYyZi0xMWU4LWI0ZjItMzU5NWRiZmRiODVmIiwidG9rZW5fdXNlIjoiaWQiLCJhdXRoX3RpbWUiOjE1MzM"
"xOTkxMzgsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5ldS13ZXN0LTEuYW1hem9uYXdzLmNvbVwvZXUt"
"d2VzdC0xXzZlWDZZam1YciIsImNvZ25pdG86dXNlcm5hbWUiOiIyYzE3YzUwZC0xZDFlLTRhM2UtYmFjOS0zZ"
"DI0YjQ1MTFiYWEiLCJleHAiOjE1MzMyMDI3MzgsImlhdCI6MTUzMzE5OTEzOCwiZW1haWwiOiJqZW5raW5zQG"
"d1aC5pbyJ9.hMMSvZMx7pMvV70PaUmTZOZgdez5WGX5yagRFPZojBm8jNWZND1lUmi0RFkybeD4HonDiKHxTF"
"_psyJoBVndgHbxYBBl3Np4gn0MxECWjvLxYzGxVBBkN24SqNUyAGkr0uFcZKkBecdtJlqNQnZN8Uk49twmODf"
"raRaRmGmKmRBAK1qDITpUgP6AWqH9xoJWaoDzt0kwJ3EtPxS7vL1PHqOaN8ggXA8Eq4iTCSfXU1HAXhIWJH9Y"
"pQbj58v1vktaAEATdmKmlzmcix-HJK9wWHRSuv3TsNa8DGxvcPOoeTu8Vql7krZ-y7Zu-s2WsgeP4VxyT80VE"
"T_xh6pMkOhE6g";
}
void RemoteProxyTests::cleanUpEngine()
@ -177,6 +191,8 @@ QVariant RemoteProxyTests::injectSocketData(const QByteArray &data)
void RemoteProxyTests::initTestCase()
{
qRegisterMetaType<RemoteProxyConnection::Error>();
qRegisterMetaType<RemoteProxyConnection::State>();
qRegisterMetaType<RemoteProxyConnection::ConnectionType>();
qCDebug(dcApplication()) << "Init test case.";
restartEngine();
@ -301,19 +317,19 @@ void RemoteProxyTests::authenticate_data()
QTest::addColumn<int>("timeout");
QTest::addColumn<Authenticator::AuthenticationError>("expectedError");
QTest::newRow("success") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << "Hh5JrkdFVstVVyCnfE3vVT3zG_JTacXEhPLZhCei"
QTest::newRow("success") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken
<< 100 << Authenticator::AuthenticationErrorNoError;
QTest::newRow("failed") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << "invalid_token_42"
QTest::newRow("failed") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken
<< 100 << Authenticator::AuthenticationErrorAuthenticationFailed;
QTest::newRow("not responding") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << "invalid_token_42"
QTest::newRow("not responding") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken
<< 200 << Authenticator::AuthenticationErrorAuthenticationServerNotResponding;
QTest::newRow("aborted") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << "invalid_token_42"
QTest::newRow("aborted") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken
<< 100 << Authenticator::AuthenticationErrorAborted;
QTest::newRow("unknown") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << "invalid_token_42"
QTest::newRow("unknown") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken
<< 100 << Authenticator::AuthenticationErrorUnknown;
}
@ -352,90 +368,189 @@ void RemoteProxyTests::clientConnection()
// Start the server
startServer();
// Connect to the server (insecure disabled)
RemoteProxyConnection *connectorOne = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", RemoteProxyConnection::ConnectionTypeWebSocket, this);
connectorOne->setInsecureConnection(true);
// Connect to server (insecue enabled for testing)
QSignalSpy readySpy(connectorOne, &RemoteProxyConnection::ready);
QVERIFY(connectorOne->connectServer(QHostAddress::LocalHost, m_port));
readySpy.wait();
QVERIFY(readySpy.count() == 1);
QVERIFY(connectorOne->isConnected());
QVERIFY(connectorOne->state() == RemoteProxyConnection::StateReady);
// Authenticate
// Configure moch authenticator
m_authenticator->setTimeoutDuration(100);
m_authenticator->setExpectedAuthenticationError();
connectorOne->authenticate("foobar");
QTest::qWait(1000);
// Connect to the server (insecure disabled)
RemoteProxyConnection *connection = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", RemoteProxyConnection::ConnectionTypeWebSocket, this);
connection->setInsecureConnection(true);
// Connect to server (insecue enabled for testing)
QSignalSpy readySpy(connection, &RemoteProxyConnection::ready);
QVERIFY(connection->connectServer(QHostAddress::LocalHost, m_port));
readySpy.wait();
QVERIFY(readySpy.count() == 1);
QVERIFY(connection->isConnected());
QVERIFY(!connection->isRemoteConnected());
QVERIFY(connection->state() == RemoteProxyConnection::StateReady);
QVERIFY(connection->error() == RemoteProxyConnection::ErrorNoError);
QVERIFY(connection->serverAddress() == QHostAddress::LocalHost);
QVERIFY(connection->serverPort() == m_port);
QVERIFY(connection->connectionType() == RemoteProxyConnection::ConnectionTypeWebSocket);
QVERIFY(connection->insecureConnection() == true);
QVERIFY(connection->serverName() == SERVER_NAME_STRING);
QVERIFY(connection->proxyServerName() == Engine::instance()->serverName());
QVERIFY(connection->proxyServerVersion() == SERVER_VERSION_STRING);
QVERIFY(connection->proxyServerApiVersion() == API_VERSION_STRING);
QSignalSpy authenticatedSpy(connection, &RemoteProxyConnection::authenticated);
QVERIFY(connection->authenticate("foobar"));
authenticatedSpy.wait();
QVERIFY(authenticatedSpy.count() == 1);
QVERIFY(connection->isConnected());
QVERIFY(connection->isAuthenticated());
QVERIFY(connection->state() == RemoteProxyConnection::StateWaitTunnel);
// Disconnect and clean up
QSignalSpy spyDisconnected(connectorOne, &RemoteProxyConnection::disconnected);
connectorOne->disconnectServer();
spyDisconnected.wait();
QVERIFY(!connectorOne->isConnected());
QSignalSpy spyDisconnected(connection, &RemoteProxyConnection::disconnected);
connection->disconnectServer();
// FIXME: check why it waits the full time here
spyDisconnected.wait(100);
QVERIFY(spyDisconnected.count() == 1);
QVERIFY(!connection->isConnected());
connectorOne->deleteLater();
connection->deleteLater();
stopServer();
}
void RemoteProxyTests::remoteConnection()
{
// Start the server
startServer();
// Configure moch authenticator
m_authenticator->setTimeoutDuration(100);
m_authenticator->setExpectedAuthenticationError();
// Create two connection
RemoteProxyConnection *connectionOne = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", RemoteProxyConnection::ConnectionTypeWebSocket, this);
connectionOne->setInsecureConnection(true);
RemoteProxyConnection *connectionTwo = new RemoteProxyConnection(QUuid::createUuid(), "Test client two", RemoteProxyConnection::ConnectionTypeWebSocket, this);
connectionTwo->setInsecureConnection(true);
// Connect one
QSignalSpy connectionOneReadySpy(connectionOne, &RemoteProxyConnection::ready);
QVERIFY(connectionOne->connectServer(QHostAddress::LocalHost, m_port));
connectionOneReadySpy.wait();
QVERIFY(connectionOneReadySpy.count() == 1);
QVERIFY(connectionOne->isConnected());
// Connect two
QSignalSpy connectionTwoReadySpy(connectionTwo, &RemoteProxyConnection::ready);
QVERIFY(connectionTwo->connectServer(QHostAddress::LocalHost, m_port));
connectionTwoReadySpy.wait();
QVERIFY(connectionTwoReadySpy.count() == 1);
QVERIFY(connectionTwo->isConnected());
// Authenticate one
QSignalSpy remoteConnectionEstablishedOne(connectionOne, &RemoteProxyConnection::remoteConnectionEstablished);
QSignalSpy connectionOneAuthenticatedSpy(connectionOne, &RemoteProxyConnection::authenticated);
QVERIFY(connectionOne->authenticate(m_testToken));
connectionOneAuthenticatedSpy.wait();
QVERIFY(connectionOneAuthenticatedSpy.count() == 1);
QVERIFY(connectionOne->isConnected());
QVERIFY(connectionOne->isAuthenticated());
QVERIFY(connectionOne->state() == RemoteProxyConnection::StateWaitTunnel);
// Authenticate two
QSignalSpy remoteConnectionEstablishedTwo(connectionTwo, &RemoteProxyConnection::remoteConnectionEstablished);
QSignalSpy connectionTwoAuthenticatedSpy(connectionTwo, &RemoteProxyConnection::authenticated);
QVERIFY(connectionTwo->authenticate(m_testToken));
connectionTwoAuthenticatedSpy.wait();
QVERIFY(connectionTwoAuthenticatedSpy.count() == 1);
QVERIFY(connectionTwo->isConnected());
QVERIFY(connectionTwo->isAuthenticated());
// Wait for both to be connected
remoteConnectionEstablishedOne.wait();
remoteConnectionEstablishedTwo.wait();
QVERIFY(remoteConnectionEstablishedOne.count() == 1);
QVERIFY(remoteConnectionEstablishedTwo.count() == 1);
QVERIFY(connectionOne->state() == RemoteProxyConnection::StateRemoteConnected);
QVERIFY(connectionTwo->state() == RemoteProxyConnection::StateRemoteConnected);
// Pipe data trought the tunnel
QSignalSpy remoteConnectionDataOne(connectionOne, &RemoteProxyConnection::dataReady);
QSignalSpy remoteConnectionDataTwo(connectionTwo, &RemoteProxyConnection::dataReady);
QByteArray dataOne = "Hello from client one :-)";
QByteArray dataTwo = "Hello from client two :-)";
connectionOne->sendData(dataOne);
remoteConnectionDataTwo.wait();
QVERIFY(remoteConnectionDataTwo.count() == 1);
// verify if data is the same
connectionTwo->sendData(dataTwo);
remoteConnectionDataOne.wait();
QVERIFY(remoteConnectionDataOne.count() == 1);
// verify if data is the same
connectionOne->deleteLater();
connectionTwo->deleteLater();
// Clean up
stopServer();
}
void RemoteProxyTests::sslConfigurations()
{
// // Start the server
// startServer();
// // Start the server
// startServer();
// // Connect to the server (insecure disabled)
// RemoteProxyConnection *connector = new RemoteProxyConnection(RemoteProxyConnection::ConnectionTypeWebSocket, this);
// connector->setInsecureConnection(false);
// // Connect to the server (insecure disabled)
// RemoteProxyConnection *connector = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", RemoteProxyConnection::ConnectionTypeWebSocket, this);
// QSignalSpy spyError(connector, &RemoteProxyConnection::errorOccured);
// QVERIFY(connector->connectServer(QHostAddress::LocalHost, m_port));
// spyError.wait();
// QVERIFY(spyError.count() == 1);
// QVERIFY(connector->error() == RemoteProxyConnection::ErrorSslError);
// QVERIFY(connector->state() == RemoteProxyConnection::StateDisconnected);
// QSignalSpy spyError(connector, &RemoteProxyConnection::errorOccured);
// connector->connectServer(QHostAddress::LocalHost, m_port);
// spyError.wait();
// // Connect to server (insecue enabled)
// QSignalSpy spyConnected(connector, &RemoteProxyConnection::connected);
// connector->setInsecureConnection(true);
// connector->connectServer(QHostAddress::LocalHost, m_port);
// spyConnected.wait();
// QCOMPARE(connector->error(), RemoteProxyConnection::ErrorSslError);
// QCOMPARE(connector->state(), RemoteProxyConnection::StateDisconnected);
// QVERIFY(connector->isConnected());
// // Connect to server (insecue enabled)
// QSignalSpy spyConnected(connector, &RemoteProxyConnection::connected);
// connector->setInsecureConnection(true);
// connector->connectServer(QHostAddress::LocalHost, m_port);
// spyConnected.wait();
// // Disconnect and clean up
// connector->disconnectServer();
// QVERIFY(!connector->isConnected());
// QVERIFY(connector->isConnected());
// // Disconnect and clean up
// connector->disconnectServer();
// QVERIFY(!connector->isConnected());
// connector->deleteLater();
// stopServer();
// connector->deleteLater();
// stopServer();
}
void RemoteProxyTests::timeout()
{
// Start the server
startServer();
// // Start the server
// startServer();
// Configure result
// Start the server
startServer();
// // Configure result
// // Start the server
// startServer();
// Configure result
m_authenticator->setExpectedAuthenticationError();
m_authenticator->setTimeoutDuration(6000);
// // Configure result
// m_authenticator->setExpectedAuthenticationError();
// m_authenticator->setTimeoutDuration(6000);
// Create request
QVariantMap params;
params.insert("uuid", "uuid");
params.insert("name", "name");
params.insert("token", "token");
// // Create request
// QVariantMap params;
// params.insert("uuid", "uuid");
// params.insert("name", "name");
// params.insert("token", "token");
QVariant response = invokeApiCall("Authentication.Authenticate", params);
qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented));
// Clean up
stopServer();
// QVariant response = invokeApiCall("Authentication.Authenticate", params);
// qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented));
// // Clean up
// stopServer();
}
QTEST_MAIN(RemoteProxyTests)

View File

@ -28,6 +28,7 @@ private:
QHostAddress m_serverAddress = QHostAddress::LocalHost;
QSslConfiguration m_sslConfiguration;
MockAuthenticator *m_authenticator = nullptr;
QString m_testToken;
int m_commandCounter = 0;
@ -63,10 +64,11 @@ private slots:
void authenticate();
// Client lib
void clientConnection();
void remoteConnection();
void sslConfigurations();
void timeout();
void clientConnection();
public slots: