Add optional the nonce parameter for authentication and update docs

This commit is contained in:
Simon Stürz 2018-08-31 22:28:03 +02:00
parent 2dcf219c5a
commit eacffb695f
16 changed files with 321 additions and 114 deletions

150
README.md
View File

@ -38,7 +38,7 @@ If you want to start the proxy server from the build directory, you need to expo
## From repository
There is a public version available in the nymea repository.
$ apt install nymea-remoteproxy nymea-remoteproxy-client
$ apt install nymea-remoteproxy nymea-remoteproxy-client nymea-remoteproxy-monitor
This will install a systemd service called `nymea-remoteproxy.service` and the client application for testing.
@ -62,6 +62,11 @@ The package will deliver a default configuration file with following content (`/
inactiveTimeout=8000
aloneTimeout=8000
[AWS]
region=eu-west-1
authorizerLambdaFunction=system-services-authorizer-dev-checkToken
awsCredentialsUrl=http://169.254.169.254/latest/meta-data/iam/security-credentials/EC2-Remote-Connection-Proxy-Role
[SSL]
certificate=/etc/ssl/certs/ssl-cert-snakeoil.pem
certificateKey=/etc/ssl/private/ssl-cert-snakeoil.key
@ -74,7 +79,7 @@ The package will deliver a default configuration file with following content (`/
[TcpServer]
host=127.0.0.1
port=80
# Test
@ -103,8 +108,8 @@ In order to get information about the server you can start the command with the
The nymea remote proxy server. This server allowes nymea-cloud users and registered nymea deamons to establish a tunnel connection.
Version: 0.1.2
API version: 0.2
Version: 0.1.5
API version: 0.3
Copyright © 2018 Simon Stürz <simon.stuerz@guh.io>
@ -124,7 +129,7 @@ In order to get information about the server you can start the command with the
~/.config/nymea/nymea-remoteproxy.conf
--verbose Print more verbose.
# Server API
Once a client connects to the proxy server, he must authenticate him self by passing the token received from the nymea-cloud mqtt connection request.
@ -204,7 +209,7 @@ If anything goes wrong, or the tunnel partner disconnects from the proxy, the se
## Authenticate the connection
The first data a client **must** send to the proxy server is the authentication request. This request contains the token which will be verified agains the nymea-cloud infrastructure.
The first data a client **must** send to the proxy server is the authentication request. This request contains the `token` which will be verified agains the nymea-cloud infrastructure and a `nonce` which has to be uniq for each connection attempt and shared between the 2 clients. The `uuid` should be a persistant uuid for this client and the name should make clear which type of connection this is and which client is connecting. The name and uuid will be sent to the tunnel partner during the tunnel establishmend.
#### Request
@ -215,6 +220,7 @@ The first data a client **must** send to the proxy server is the authentication
"uuid": "string",
"name": "string",
"token": "tokenstring"
"nonce": "nonce"
}
}
@ -263,75 +269,75 @@ Once the other client is here and ready, the server will send a notification to
#### Response
{
"id": 0,
"params": {
"methods": {
"Authentication.Authenticate": {
"description": "Authenticate this connection. The returned AuthenticationError informs about the result. If the authentication was not successfull, the server will close the connection immediatly after sending the error response. The given id should be a unique id the other tunnel client can understand. Once the authentication was successfull, you can wait for the RemoteProxy.TunnelEstablished notification. If you send any data before getting this notification, the server will close the connection. If the tunnel client does not show up within 10 seconds, the server will close the connection.",
"params": {
"name": "String",
"token": "String",
"uuid": "String"
},
"returns": {
"authenticationError": "$ref:AuthenticationError"
}
"id": 1,
"params": {
"methods": {
"Authentication.Authenticate": {
"description": "Authenticate this connection. The returned AuthenticationError informs about the result. If the authentication was not successfull, the server will close the connection immediatly after sending the error response. The given id should be a unique id the other tunnel client can understand. Once the authentication was successfull, you can wait for the RemoteProxy.TunnelEstablished notification. If you send any data before getting this notification, the server will close the connection. If the tunnel client does not show up within 10 seconds, the server will close the connection.",
"params": {
"name": "String",
"o:nonce": "String",
"token": "String",
"uuid": "String"
},
"RemoteProxy.Hello": {
"description": "Once connected to this server, a client can get information about the server by saying Hello. The response informs the client about this proxy server.",
"params": {
},
"returns": {
"apiVersion": "String",
"name": "String",
"server": "String",
"version": "String"
}
},
"RemoteProxy.Introspect": {
"description": "Introspect this API.",
"params": {
},
"returns": {
"methods": "Object",
"notifications": "Object",
"types": "Object"
}
"returns": {
"authenticationError": "$ref:AuthenticationError"
}
},
"notifications": {
"RemoteProxy.TunnelEstablished": {
"description": "Emitted whenever the tunnel has been established successfully. This is the last message from the remote proxy server! Any following data will be from the other tunnel client until the connection will be closed. The parameter contain some information about the other tunnel client.",
"params": {
"name": "String",
"uuid": "String"
}
"RemoteProxy.Hello": {
"description": "Once connected to this server, a client can get information about the server by saying Hello. The response informs the client about this proxy server.",
"params": {
},
"returns": {
"apiVersion": "String",
"name": "String",
"server": "String",
"version": "String"
}
},
"types": {
"AuthenticationError": [
"AuthenticationErrorNoError",
"AuthenticationErrorUnknown",
"AuthenticationErrorTimeout",
"AuthenticationErrorAborted",
"AuthenticationErrorAuthenticationFailed",
"AuthenticationErrorAuthenticationServerNotResponding"
],
"BasicType": [
"Uuid",
"String",
"Int",
"UInt",
"Double",
"Bool",
"Variant",
"Object"
]
"RemoteProxy.Introspect": {
"description": "Introspect this API.",
"params": {
},
"returns": {
"methods": "Object",
"notifications": "Object",
"types": "Object"
}
}
},
"status": "success"
}
"notifications": {
"RemoteProxy.TunnelEstablished": {
"description": "Emitted whenever the tunnel has been established successfully. This is the last message from the remote proxy server! Any following data will be from the other tunnel client until the connection will be closed. The parameter contain some information about the other tunnel client.",
"params": {
"name": "String",
"uuid": "String"
}
}
},
"types": {
"AuthenticationError": [
"AuthenticationErrorNoError",
"AuthenticationErrorUnknown",
"AuthenticationErrorTimeout",
"AuthenticationErrorAborted",
"AuthenticationErrorAuthenticationFailed",
"AuthenticationErrorProxyError"
],
"BasicType": [
"Uuid",
"String",
"Int",
"UInt",
"Double",
"Bool",
"Variant",
"Object"
]
}
},
"status": "success"
}
# Server monitor
@ -348,8 +354,8 @@ There is also the package `nymea-remoteproxy-monitor` package and application wh
The nymea remote proxy monitor allowes to monitor the live server activity on the a local instance.
Server version: 0.1.2
API version: 0.2
Server version: 0.1.5
API version: 0.3
Copyright © 2018 Simon Stürz <simon.stuerz@guh.io>
@ -374,8 +380,8 @@ The client allowes you to test the proxy server and create a dummy client for te
The nymea remote proxy client application. This client allowes to test a server application as client perspective.
Version: 0.1.2
API version: 0.2
Version: 0.1.5
API version: 0.3
Copyright © 2018 Simon Stürz <simon.stuerz@guh.io>

View File

@ -43,6 +43,7 @@ AuthenticationHandler::AuthenticationHandler(QObject *parent) :
params.insert("uuid", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("token", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("o:nonce", JsonTypes::basicTypeToString(JsonTypes::String));
setParams("Authenticate", params);
returns.insert("authenticationError", JsonTypes::authenticationErrorRef());
setReturns("Authenticate", returns);
@ -58,14 +59,16 @@ JsonReply *AuthenticationHandler::Authenticate(const QVariantMap &params, ProxyC
QString uuid = params.value("uuid").toString();
QString name = params.value("name").toString();
QString token = params.value("token").toString();
QString nonce = params.value("nonce").toString();
qCDebug(dcJsonRpc()) << "Authenticate:" << name << uuid << token;
qCDebug(dcJsonRpc()) << "Authenticate:" << name << uuid << token << nonce;
JsonReply *jsonReply = createAsyncReply("Authenticate");
// Set the token for this proxy client
proxyClient->setUuid(uuid);
proxyClient->setName(name);
proxyClient->setToken(token);
proxyClient->setNonce(nonce);
AuthenticationReply *authReply = Engine::instance()->authenticator()->authenticate(proxyClient);
connect(authReply, &AuthenticationReply::finished, this, &AuthenticationHandler::onAuthenticationFinished);

View File

@ -123,6 +123,16 @@ void ProxyClient::setToken(const QString &token)
m_token = token;
}
QString ProxyClient::nonce() const
{
return m_nonce;
}
void ProxyClient::setNonce(const QString &nonce)
{
m_nonce = nonce;
}
void ProxyClient::sendData(const QByteArray &data)
{
if (!m_interface)

View File

@ -63,6 +63,9 @@ public:
QString token() const;
void setToken(const QString &token);
QString nonce() const;
void setNonce(const QString &nonce);
// Actions for this client
void sendData(const QByteArray &data);
void killConnection(const QString &reason);
@ -81,6 +84,7 @@ private:
QString m_uuid;
QString m_name;
QString m_token;
QString m_nonce;
signals:
void authenticated();

View File

@ -119,13 +119,6 @@ ProxyClient *ProxyServer::getRemoteClient(ProxyClient *proxyClient)
return nullptr;
}
void ProxyServer::sendResponse(TransportInterface *interface, const QUuid &clientId, const QVariantMap &response)
{
QByteArray data = QJsonDocument::fromVariant(response).toJson(QJsonDocument::Compact);
qCDebug(dcJsonRpcTraffic()) << "Sending data:" << data;
interface->sendData(clientId, data);
}
void ProxyServer::establishTunnel(ProxyClient *firstClient, ProxyClient *secondClient)
{
qCDebug(dcProxyServer()) << "Create tunnel between authenticated clients " << firstClient << secondClient;
@ -196,11 +189,16 @@ void ProxyServer::onClientDisconnected(const QUuid &clientId)
m_authenticatedClients.remove(proxyClient->token());
}
if (m_authenticatedClientsNonce.values().contains(proxyClient)) {
m_authenticatedClientsNonce.remove(proxyClient->nonce());
}
// Unregister from json rpc server
m_jsonRpcServer->unregisterClient(proxyClient);
// Check if
if (m_tunnels.contains(proxyClient->token())) {
// There is a tunnel connection for this client, remove the tunnel and disconnect also the other client
ProxyClient *remoteClient = getRemoteClient(proxyClient);
m_tunnels.remove(proxyClient->token());
@ -259,8 +257,10 @@ void ProxyServer::onProxyClientAuthenticated()
qCDebug(dcProxyServer()) << "Client authenticated" << proxyClient;
// Check if we already have a tunnel for this token
if (m_tunnels.contains(proxyClient->token())) {
// A new connection attempt with the same token, kill the old tunnel connection and allow the new connection to stablish the tunnel
// A new connection attempt with the same token, kill the old tunnel
// connection and allow the new connection to stablish the tunnel
qCWarning(dcProxyServer()) << "New authenticated client which already has a tunnel connection. Closing and clean up the old tunnel.";
TunnelConnection tunnel = m_tunnels.take(proxyClient->token());
@ -269,24 +269,47 @@ void ProxyServer::onProxyClientAuthenticated()
tunnel.clientTwo()->killConnection("Clean up for new connection.");
}
// 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());
// FIXME: for backwards compatibility
if (proxyClient->nonce().isEmpty()) {
// Check if we have an other authenticated client with this token
if (m_authenticatedClients.keys().contains(proxyClient->token())) {
// Check if the two clients show up with the same uuid
if (tunnelEnd->uuid() == proxyClient->uuid()) {
qCWarning(dcProxyServer()) << "The clients have the same uuid. This is not allowed.";
proxyClient->killConnection("Duplicated client UUID.");
tunnelEnd->killConnection("Duplicated client UUID.");
return;
// Found a client with this token
ProxyClient *tunnelPartner = m_authenticatedClients.take(proxyClient->token());
// Check if the two clients show up with the same uuid to prevent connection loops
if (tunnelPartner->uuid() == proxyClient->uuid()) {
qCWarning(dcProxyServer()) << "The clients have the same uuid. This is not allowed.";
proxyClient->killConnection("Duplicated client UUID.");
tunnelPartner->killConnection("Duplicated client UUID.");
return;
}
// All ok so far. Create the tunnel
establishTunnel(tunnelPartner, proxyClient);
} else {
// Append and wait for the other client
m_authenticatedClients.insert(proxyClient->token(), proxyClient);
}
// All ok so far. Create the tunnel
establishTunnel(tunnelEnd, proxyClient);
} else {
// Append and wait for the other client
m_authenticatedClients.insert(proxyClient->token(), proxyClient);
// The client passed a nonce, let's hash with that to prevent cross connections
if (m_authenticatedClientsNonce.keys().contains(proxyClient->nonce())) {
// Found a client with this nonce
ProxyClient *tunnelPartner = m_authenticatedClientsNonce.take(proxyClient->nonce());
// Check if the two clients show up with the same uuid to prevent connection loops
if (tunnelPartner->uuid() == proxyClient->uuid()) {
qCWarning(dcProxyServer()) << "The clients have the same uuid. This could cause a loop and is not allowed.";
proxyClient->killConnection("Client loop detected.");
tunnelPartner->killConnection("Client loop detected.");
return;
}
// All ok so far. Create the tunnel
establishTunnel(tunnelPartner, proxyClient);
} else {
m_authenticatedClientsNonce.insert(proxyClient->nonce(), proxyClient);
}
}
}

View File

@ -54,9 +54,12 @@ private:
// Transport ClientId, ProxyClient
QHash<QUuid, ProxyClient *> m_proxyClients;
// Token, ProxyClient
// FIXME: Token, ProxyClient
QHash<QString, ProxyClient *> m_authenticatedClients;
// Nonce, ProxyClient
QHash<QString, ProxyClient *> m_authenticatedClientsNonce;
// Token, Tunnel
QHash<QString, TunnelConnection> m_tunnels;
@ -64,8 +67,6 @@ private:
ProxyClient *getRemoteClient(ProxyClient *proxyClient);
void sendResponse(TransportInterface *interface, const QUuid &clientId, const QVariantMap &response = QVariantMap());
void establishTunnel(ProxyClient *firstClient, ProxyClient *secondClient);
signals:

View File

@ -60,6 +60,11 @@ ProxyClient *TunnelConnection::clientTwo() const
return m_clientTwo;
}
bool TunnelConnection::hasClient(ProxyClient *proxyClient) const
{
return m_clientOne == proxyClient || m_clientTwo == proxyClient;
}
bool TunnelConnection::isValid() const
{
// Both clients have to be valid

View File

@ -39,6 +39,8 @@ public:
ProxyClient *clientOne() const;
ProxyClient *clientTwo() const;
bool hasClient(ProxyClient *proxyClient) const;
bool isValid() const;
private:

View File

@ -45,12 +45,13 @@ JsonReply *JsonRpcClient::callHello()
return reply;
}
JsonReply *JsonRpcClient::callAuthenticate(const QUuid &clientUuid, const QString &clientName, const QString &token)
JsonReply *JsonRpcClient::callAuthenticate(const QUuid &clientUuid, const QString &clientName, const QString &token, const QString &nonce)
{
QVariantMap params;
params.insert("name", clientName);
params.insert("uuid", clientUuid.toString());
params.insert("token", token);
if (!nonce.isEmpty()) params.insert("nonce", nonce);
JsonReply *reply = new JsonReply(m_commandId, "Authentication", "Authenticate", params, this);
qCDebug(dcRemoteProxyClientJsonRpc()) << "Calling" << QString("%1.%2").arg(reply->nameSpace()).arg(reply->method());

View File

@ -42,7 +42,7 @@ public:
explicit JsonRpcClient(ProxyConnection *connection, QObject *parent = nullptr);
JsonReply *callHello();
JsonReply *callAuthenticate(const QUuid &clientUuid, const QString &clientName, const QString &token);
JsonReply *callAuthenticate(const QUuid &clientUuid, const QString &clientName, const QString &token, const QString &nonce);
private:
ProxyConnection *m_connection = nullptr;

View File

@ -340,7 +340,7 @@ bool RemoteProxyConnection::connectServer(const QUrl &url)
return true;
}
bool RemoteProxyConnection::authenticate(const QString &token)
bool RemoteProxyConnection::authenticate(const QString &token, const QString &nonce)
{
if (m_state != StateReady) {
qCWarning(dcRemoteProxyClientConnection()) << "Could not authenticate. The connection is not ready";
@ -349,8 +349,8 @@ bool RemoteProxyConnection::authenticate(const QString &token)
setState(StateAuthenticating);
qCDebug(dcRemoteProxyClientConnection()) << "Start authentication using token" << token;
JsonReply *reply = m_jsonClient->callAuthenticate(m_clientUuid, m_clientName, token);
qCDebug(dcRemoteProxyClientConnection()) << "Start authentication using token" << token << nonce;
JsonReply *reply = m_jsonClient->callAuthenticate(m_clientUuid, m_clientName, token, nonce);
connect(reply, &JsonReply::finished, this, &RemoteProxyConnection::onAuthenticateFinished);
return true;
}

View File

@ -143,7 +143,7 @@ private slots:
public slots:
bool connectServer(const QUrl &url);
bool authenticate(const QString &token);
bool authenticate(const QString &token, const QString &nonce = QString());
void disconnectServer();
bool sendData(const QByteArray &data);
};

View File

@ -4,8 +4,8 @@ QT -= gui
# Define versions
SERVER_NAME=nymea-remoteproxy
API_VERSION_MAJOR=0
API_VERSION_MINOR=2
SERVER_VERSION=0.1.4
API_VERSION_MINOR=3
SERVER_VERSION=0.1.5
DEFINES += SERVER_NAME_STRING=\\\"$${SERVER_NAME}\\\" \
SERVER_VERSION_STRING=\\\"$${SERVER_VERSION}\\\" \

View File

@ -279,23 +279,31 @@ void RemoteProxyOfflineTests::authenticate_data()
QTest::addColumn<QString>("uuid");
QTest::addColumn<QString>("name");
QTest::addColumn<QString>("token");
QTest::addColumn<QString>("nonce");
QTest::addColumn<int>("timeout");
QTest::addColumn<Authenticator::AuthenticationError>("expectedError");
QTest::newRow("success") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken
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!" << m_testToken
QTest::newRow("success") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken << "nonce"
<< 100 << Authenticator::AuthenticationErrorNoError;
QTest::newRow("success") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken << "nonce"
<< 100 << Authenticator::AuthenticationErrorAuthenticationFailed;
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!" << m_testToken
QTest::newRow("not responding") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken << ""
<< 200 << Authenticator::AuthenticationErrorProxyError;
QTest::newRow("aborted") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken
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!" << m_testToken
QTest::newRow("unknown") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken << ""
<< 100 << Authenticator::AuthenticationErrorUnknown;
}
@ -305,6 +313,7 @@ void RemoteProxyOfflineTests::authenticate()
QFETCH(QString, uuid);
QFETCH(QString, name);
QFETCH(QString, token);
QFETCH(QString, nonce);
QFETCH(int, timeout);
QFETCH(Authenticator::AuthenticationError, expectedError);
@ -320,6 +329,7 @@ void RemoteProxyOfflineTests::authenticate()
params.insert("uuid", uuid);
params.insert("name", name);
params.insert("token", token);
if (!nonce.isEmpty()) params.insert("nonce", nonce);
QVariant response = invokeApiCall("Authentication.Authenticate", params);
qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented));
@ -329,6 +339,145 @@ void RemoteProxyOfflineTests::authenticate()
stopServer();
}
void RemoteProxyOfflineTests::authenticateNonce()
{
// Start the server
startServer();
QString nonce = "67af856c4e4071833ed01128e50b3ea5";
QString token = "C001L0ckingT0ken";
// Configure moch authenticator
m_mockAuthenticator->setTimeoutDuration(100);
m_mockAuthenticator->setExpectedAuthenticationError();
QString nameConnectionOne = "Test client one";
QUuid uuidConnectionOne = QUuid::createUuid();
QString nameConnectionTwo = "Test client two";
QUuid uuidConnectionTwo = QUuid::createUuid();
QByteArray dataOne = "Hello from client one :-)";
QByteArray dataTwo = "Hello from client two :-)";
// Create two connection
RemoteProxyConnection *connectionOne = new RemoteProxyConnection(uuidConnectionOne, nameConnectionOne, this);
connect(connectionOne, &RemoteProxyConnection::sslErrors, this, &BaseTest::ignoreConnectionSslError);
RemoteProxyConnection *connectionTwo = new RemoteProxyConnection(uuidConnectionTwo, nameConnectionTwo, this);
connect(connectionTwo, &RemoteProxyConnection::sslErrors, this, &BaseTest::ignoreConnectionSslError);
// Connect one
QSignalSpy connectionOneReadySpy(connectionOne, &RemoteProxyConnection::ready);
QVERIFY(connectionOne->connectServer(m_serverUrl));
connectionOneReadySpy.wait();
QVERIFY(connectionOneReadySpy.count() == 1);
QVERIFY(connectionOne->isConnected());
// Connect two
QSignalSpy connectionTwoReadySpy(connectionTwo, &RemoteProxyConnection::ready);
QVERIFY(connectionTwo->connectServer(m_serverUrl));
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, nonce));
connectionOneAuthenticatedSpy.wait();
QVERIFY(connectionOneAuthenticatedSpy.count() == 1);
QVERIFY(connectionOne->isConnected());
QVERIFY(connectionOne->isAuthenticated());
QVERIFY(connectionOne->state() == RemoteProxyConnection::StateAuthenticated);
// Authenticate two
QSignalSpy remoteConnectionEstablishedTwo(connectionTwo, &RemoteProxyConnection::remoteConnectionEstablished);
QSignalSpy connectionTwoAuthenticatedSpy(connectionTwo, &RemoteProxyConnection::authenticated);
QVERIFY(connectionTwo->authenticate(m_testToken, nonce));
connectionTwoAuthenticatedSpy.wait();
QVERIFY(connectionTwoAuthenticatedSpy.count() == 1);
QVERIFY(connectionTwo->isConnected());
QVERIFY(connectionTwo->isAuthenticated());
// Wait for both to be connected
remoteConnectionEstablishedOne.wait(500);
remoteConnectionEstablishedTwo.wait(500);
QVERIFY(remoteConnectionEstablishedOne.count() == 1);
QVERIFY(remoteConnectionEstablishedTwo.count() == 1);
QVERIFY(connectionOne->state() == RemoteProxyConnection::StateRemoteConnected);
QVERIFY(connectionTwo->state() == RemoteProxyConnection::StateRemoteConnected);
QCOMPARE(connectionOne->tunnelPartnerName(), nameConnectionTwo);
QCOMPARE(connectionOne->tunnelPartnerUuid(), uuidConnectionTwo.toString());
QCOMPARE(connectionTwo->tunnelPartnerName(), nameConnectionOne);
QCOMPARE(connectionTwo->tunnelPartnerUuid(), uuidConnectionOne.toString());
// Pipe data trought the tunnel
QSignalSpy remoteConnectionDataOne(connectionOne, &RemoteProxyConnection::dataReady);
QSignalSpy remoteConnectionDataTwo(connectionTwo, &RemoteProxyConnection::dataReady);
connectionOne->sendData(dataOne);
remoteConnectionDataTwo.wait(500);
QVERIFY(remoteConnectionDataTwo.count() == 1);
QCOMPARE(remoteConnectionDataTwo.at(0).at(0).toByteArray().trimmed(), dataOne);
connectionTwo->sendData(dataTwo);
remoteConnectionDataOne.wait(500);
QVERIFY(remoteConnectionDataOne.count() == 1);
QCOMPARE(remoteConnectionDataOne.at(0).at(0).toByteArray().trimmed(), dataTwo);
connectionOne->deleteLater();
connectionTwo->deleteLater();
// Clean up
stopServer();
}
void RemoteProxyOfflineTests::authenticateSendData()
{
// Start the server
startServer();
QVariantMap params;
params.insert("uuid", "uuid");
params.insert("name", "name");
params.insert("token", "token");
params.insert("nonce", "nonce");
QVariantMap request;
request.insert("id", m_commandCounter);
request.insert("method", "Authentication.Authenticate");
request.insert("params", params);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(request);
// Connect socket
QWebSocket *socket = new QWebSocket("proxy-testclient", QWebSocketProtocol::Version13);
connect(socket, &QWebSocket::sslErrors, this, &BaseTest::sslErrors);
QSignalSpy spyConnection(socket, SIGNAL(connected()));
socket->open(Engine::instance()->webSocketServer()->serverUrl());
spyConnection.wait();
QVERIFY(spyConnection.count() == 1);
// Authenticate
QSignalSpy dataSpy(socket, SIGNAL(textMessageReceived(QString)));
socket->sendTextMessage(QString(jsonDoc.toJson(QJsonDocument::Compact)));
dataSpy.wait();
QVERIFY(dataSpy.count() == 1);
// Send data and make sure we get disconnected
QSignalSpy disconnectedSpy(socket, SIGNAL(disconnected()));
socket->sendTextMessage(QString(jsonDoc.toJson(QJsonDocument::Compact)));
disconnectedSpy.wait();
QVERIFY(disconnectedSpy.count() == 1);
socket->deleteLater();
// Clean up
stopServer();
}
void RemoteProxyOfflineTests::clientConnection()
{
// Start the server
@ -384,7 +533,7 @@ void RemoteProxyOfflineTests::remoteConnection()
// Start the server
startServer();
// Configure moch authenticator
// Configure mock authenticator
m_mockAuthenticator->setTimeoutDuration(100);
m_mockAuthenticator->setExpectedAuthenticationError();

View File

@ -58,6 +58,9 @@ private slots:
void authenticate_data();
void authenticate();
void authenticateNonce();
void authenticateSendData();
// Client lib
void clientConnection();
void remoteConnection();

View File

@ -53,7 +53,7 @@ void MockAuthenticator::replyFinished()
setReplyError(reply->authenticationReply(), reply->error());
setReplyFinished(reply->authenticationReply());
delete reply;
reply->deleteLater();
}
AuthenticationReply *MockAuthenticator::authenticate(ProxyClient *proxyClient)