Merge PR #16: Allow multiple tunnels for one valid token

This commit is contained in:
Jenkins nymea 2020-06-02 21:01:32 +02:00
commit a8a10f80d5
10 changed files with 204 additions and 31 deletions

View File

@ -129,6 +129,11 @@ void ProxyClient::setName(const QString &name)
m_name = name; m_name = name;
} }
QString ProxyClient::tunnelIdentifier() const
{
return m_token + m_nonce;
}
QString ProxyClient::token() const QString ProxyClient::token() const
{ {
return m_token; return m_token;

View File

@ -69,6 +69,8 @@ public:
QString name() const; QString name() const;
void setName(const QString &name); void setName(const QString &name);
QString tunnelIdentifier() const;
QString token() const; QString token() const;
void setToken(const QString &token); void setToken(const QString &token);

View File

@ -147,13 +147,13 @@ void ProxyServer::saveStatistics()
ProxyClient *ProxyServer::getRemoteClient(ProxyClient *proxyClient) ProxyClient *ProxyServer::getRemoteClient(ProxyClient *proxyClient)
{ {
if (!m_tunnels.contains(proxyClient->token())) if (!m_tunnels.contains(proxyClient->tunnelIdentifier()))
return nullptr; return nullptr;
if (proxyClient == m_tunnels.value(proxyClient->token()).clientOne()) { if (proxyClient == m_tunnels.value(proxyClient->tunnelIdentifier()).clientOne()) {
return m_tunnels.value(proxyClient->token()).clientTwo(); return m_tunnels.value(proxyClient->tunnelIdentifier()).clientTwo();
} else if (proxyClient == m_tunnels.value(proxyClient->token()).clientTwo()) { } else if (proxyClient == m_tunnels.value(proxyClient->tunnelIdentifier()).clientTwo()) {
return m_tunnels.value(proxyClient->token()).clientOne(); return m_tunnels.value(proxyClient->tunnelIdentifier()).clientOne();
} }
return nullptr; return nullptr;
@ -169,7 +169,7 @@ void ProxyServer::establishTunnel(ProxyClient *firstClient, ProxyClient *secondC
//FIXME: //FIXME:
} }
m_tunnels.insert(tunnel.token(), tunnel); m_tunnels.insert(tunnel.tunnelIdentifier(), tunnel);
// Tell both clients the tunnel has been established // Tell both clients the tunnel has been established
QVariantMap notificationParamsFirst; QVariantMap notificationParamsFirst;
@ -241,11 +241,11 @@ void ProxyServer::onClientDisconnected(const QUuid &clientId)
m_jsonRpcServer->unregisterClient(proxyClient); m_jsonRpcServer->unregisterClient(proxyClient);
// Check if // Check if
if (m_tunnels.contains(proxyClient->token())) { if (m_tunnels.contains(proxyClient->tunnelIdentifier())) {
// There is a tunnel connection for this client, remove the tunnel and disconnect also the other client // There is a tunnel connection for this client, remove the tunnel and disconnect also the other client
ProxyClient *remoteClient = getRemoteClient(proxyClient); ProxyClient *remoteClient = getRemoteClient(proxyClient);
TunnelConnection tunnelConnection = m_tunnels.take(proxyClient->token()); TunnelConnection tunnelConnection = m_tunnels.take(proxyClient->tunnelIdentifier());
Engine::instance()->logEngine()->logTunnel(tunnelConnection); Engine::instance()->logEngine()->logTunnel(tunnelConnection);
if (remoteClient) { if (remoteClient) {
remoteClient->killConnection("Tunnel client disconnected"); remoteClient->killConnection("Tunnel client disconnected");
@ -285,7 +285,7 @@ void ProxyServer::onClientDataAvailable(const QUuid &clientId, const QByteArray
if (proxyClient->isAuthenticated() && proxyClient->isTunnelConnected()) { if (proxyClient->isAuthenticated() && proxyClient->isTunnelConnected()) {
ProxyClient *remoteClient = getRemoteClient(proxyClient); ProxyClient *remoteClient = getRemoteClient(proxyClient);
Q_ASSERT_X(remoteClient, "ProxyServer", "Tunnel existing but not tunnel client available"); Q_ASSERT_X(remoteClient, "ProxyServer", "Tunnel existing but not tunnel client available");
Q_ASSERT_X(m_tunnels.contains(proxyClient->token()), "ProxyServer", "Tunnel connect but not existing"); Q_ASSERT_X(m_tunnels.contains(proxyClient->tunnelIdentifier()), "ProxyServer", "Tunnel connect but not existing");
// Calculate server statisitcs // Calculate server statisitcs
m_troughputCounter += data.count(); m_troughputCounter += data.count();
@ -310,18 +310,17 @@ void ProxyServer::onProxyClientAuthenticated()
qCDebug(dcProxyServer()) << "Client authenticated" << proxyClient; qCDebug(dcProxyServer()) << "Client authenticated" << proxyClient;
// Check if we already have a tunnel for this token //FIXME: limit the amount of connection with one 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
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()); // Check if we already have a tunnel with this identifier
qCDebug(dcProxyServer()) << "Killing " << tunnel; if (m_tunnels.contains(proxyClient->tunnelIdentifier())) {
tunnel.clientOne()->killConnection("Clean up for new connection."); qCWarning(dcProxyServer()) << "There is already a tunnel with this token and nonce. The client has to take a new nonce or a new token.";
tunnel.clientTwo()->killConnection("Clean up for new connection."); // There is already a tunnel with this token and nonce. Reject the connection
proxyClient->killConnection("Tunnel already exists for this token nonce combination.");
return;
} }
// FIXME: for backwards compatibility // FIXME: for backwards compatibility
if (proxyClient->nonce().isEmpty()) { if (proxyClient->nonce().isEmpty()) {
// Check if we have an other authenticated client with this token // Check if we have an other authenticated client with this token

View File

@ -63,7 +63,7 @@ private:
// FIXME: Token, ProxyClient // FIXME: Token, ProxyClient
QHash<QString, ProxyClient *> m_authenticatedClients; QHash<QString, ProxyClient *> m_authenticatedClients;
// Nonce, ProxyClient // TunnelIdentifier (token + nonce), ProxyClient
QHash<QString, ProxyClient *> m_authenticatedClientsNonce; QHash<QString, ProxyClient *> m_authenticatedClientsNonce;
// Token, Tunnel // Token, Tunnel

View File

@ -46,6 +46,22 @@ QString TunnelConnection::token() const
return m_clientOne->token(); return m_clientOne->token();
} }
QString TunnelConnection::nonce() const
{
if (!isValid())
return QString();
return m_clientOne->nonce();
}
QString TunnelConnection::tunnelIdentifier() const
{
if (!isValid())
return QString();
return m_clientOne->tunnelIdentifier();
}
uint TunnelConnection::creationTime() const uint TunnelConnection::creationTime() const
{ {
return m_creationTimeStamp; return m_creationTimeStamp;

View File

@ -38,6 +38,8 @@ public:
TunnelConnection(ProxyClient *clientOne = nullptr, ProxyClient *clientTwo = nullptr); TunnelConnection(ProxyClient *clientOne = nullptr, ProxyClient *clientTwo = nullptr);
QString token() const; QString token() const;
QString nonce() const;
QString tunnelIdentifier() const;
uint creationTime() const; uint creationTime() const;
QString creationTimeString() const; QString creationTimeString() const;

View File

@ -699,6 +699,36 @@ void RemoteProxyOfflineTests::remoteConnection()
stopServer(); stopServer();
} }
void RemoteProxyOfflineTests::multipleRemoteConnection()
{
// Start the server
startServer();
// Configure moch authenticator
m_mockAuthenticator->setExpectedAuthenticationError();
m_mockAuthenticator->setTimeoutDuration(1000);
m_configuration->setAuthenticationTimeout(2000);
m_configuration->setJsonRpcTimeout(5000);
m_configuration->setInactiveTimeout(5000);
// Create multiple tunnels with one token, but different nonces for each connection
QObject *parent = new QObject(this);
for (int i = 0; i < 5; i++) {
qDebug() << "============== Create remote connection" << i;
bool connectionResult = createRemoteConnection(m_testToken, QUuid::createUuid().toString(), parent);
if (!connectionResult) {
qWarning() << "Test failed because could not create remote connection";
delete parent;
}
QVERIFY(connectionResult);
}
delete parent;
// Clean up
stopServer();
}
void RemoteProxyOfflineTests::trippleConnection() void RemoteProxyOfflineTests::trippleConnection()
{ {
// Start the server // Start the server
@ -708,6 +738,8 @@ void RemoteProxyOfflineTests::trippleConnection()
m_mockAuthenticator->setTimeoutDuration(100); m_mockAuthenticator->setTimeoutDuration(100);
m_mockAuthenticator->setExpectedAuthenticationError(); m_mockAuthenticator->setExpectedAuthenticationError();
QString nonce = QUuid::createUuid().toString();
// Create two connection // Create two connection
RemoteProxyConnection *connectionOne = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", this); RemoteProxyConnection *connectionOne = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", this);
connect(connectionOne, &RemoteProxyConnection::sslErrors, this, &BaseTest::ignoreConnectionSslError); connect(connectionOne, &RemoteProxyConnection::sslErrors, this, &BaseTest::ignoreConnectionSslError);
@ -720,7 +752,6 @@ void RemoteProxyOfflineTests::trippleConnection()
// Connect one // Connect one
QSignalSpy connectionOneReadySpy(connectionOne, &RemoteProxyConnection::ready); QSignalSpy connectionOneReadySpy(connectionOne, &RemoteProxyConnection::ready);
QSignalSpy connectionOneDisconnectedSpy(connectionOne, &RemoteProxyConnection::disconnected);
QVERIFY(connectionOne->connectServer(m_serverUrl)); QVERIFY(connectionOne->connectServer(m_serverUrl));
connectionOneReadySpy.wait(); connectionOneReadySpy.wait();
QVERIFY(connectionOneReadySpy.count() == 1); QVERIFY(connectionOneReadySpy.count() == 1);
@ -728,7 +759,6 @@ void RemoteProxyOfflineTests::trippleConnection()
// Connect two // Connect two
QSignalSpy connectionTwoReadySpy(connectionTwo, &RemoteProxyConnection::ready); QSignalSpy connectionTwoReadySpy(connectionTwo, &RemoteProxyConnection::ready);
QSignalSpy connectionTwoDisconnectedSpy(connectionTwo, &RemoteProxyConnection::disconnected);
QVERIFY(connectionTwo->connectServer(m_serverUrl)); QVERIFY(connectionTwo->connectServer(m_serverUrl));
connectionTwoReadySpy.wait(); connectionTwoReadySpy.wait();
QVERIFY(connectionTwoReadySpy.count() == 1); QVERIFY(connectionTwoReadySpy.count() == 1);
@ -736,7 +766,7 @@ void RemoteProxyOfflineTests::trippleConnection()
// Authenticate one // Authenticate one
QSignalSpy connectionOneAuthenticatedSpy(connectionOne, &RemoteProxyConnection::authenticated); QSignalSpy connectionOneAuthenticatedSpy(connectionOne, &RemoteProxyConnection::authenticated);
QVERIFY(connectionOne->authenticate(m_testToken)); QVERIFY(connectionOne->authenticate(m_testToken, nonce));
connectionOneAuthenticatedSpy.wait(); connectionOneAuthenticatedSpy.wait();
QVERIFY(connectionOneAuthenticatedSpy.count() == 1); QVERIFY(connectionOneAuthenticatedSpy.count() == 1);
QVERIFY(connectionOne->isConnected()); QVERIFY(connectionOne->isConnected());
@ -748,7 +778,7 @@ void RemoteProxyOfflineTests::trippleConnection()
// Authenticate two // Authenticate two
QSignalSpy remoteConnectionEstablishedTwo(connectionTwo, &RemoteProxyConnection::remoteConnectionEstablished); QSignalSpy remoteConnectionEstablishedTwo(connectionTwo, &RemoteProxyConnection::remoteConnectionEstablished);
QSignalSpy connectionTwoAuthenticatedSpy(connectionTwo, &RemoteProxyConnection::authenticated); QSignalSpy connectionTwoAuthenticatedSpy(connectionTwo, &RemoteProxyConnection::authenticated);
QVERIFY(connectionTwo->authenticate(m_testToken)); QVERIFY(connectionTwo->authenticate(m_testToken, nonce));
connectionTwoAuthenticatedSpy.wait(); connectionTwoAuthenticatedSpy.wait();
QVERIFY(connectionTwoAuthenticatedSpy.count() == 1); QVERIFY(connectionTwoAuthenticatedSpy.count() == 1);
QVERIFY(connectionTwo->isConnected()); QVERIFY(connectionTwo->isConnected());
@ -757,10 +787,11 @@ void RemoteProxyOfflineTests::trippleConnection()
// Wait for both to be connected // Wait for both to be connected
remoteConnectionEstablishedOne.wait(500); remoteConnectionEstablishedOne.wait(500);
// Now connect a third connection and make sure the tunnel gets closed // Now connect a third connection and make sure the client will be closed
// Connect three // Connect three
QSignalSpy connectionThreeReadySpy(connectionThree, &RemoteProxyConnection::ready); QSignalSpy connectionThreeReadySpy(connectionThree, &RemoteProxyConnection::ready);
QSignalSpy connectionThreeDisconnectedSpy(connectionThree, &RemoteProxyConnection::disconnected);
QVERIFY(connectionThree->connectServer(m_serverUrl)); QVERIFY(connectionThree->connectServer(m_serverUrl));
connectionThreeReadySpy.wait(); connectionThreeReadySpy.wait();
QVERIFY(connectionThreeReadySpy.count() == 1); QVERIFY(connectionThreeReadySpy.count() == 1);
@ -768,17 +799,16 @@ void RemoteProxyOfflineTests::trippleConnection()
// Authenticate three // Authenticate three
QSignalSpy connectionThreeAuthenticatedSpy(connectionThree, &RemoteProxyConnection::authenticated); QSignalSpy connectionThreeAuthenticatedSpy(connectionThree, &RemoteProxyConnection::authenticated);
QVERIFY(connectionThree->authenticate(m_testToken)); QVERIFY(connectionThree->authenticate(m_testToken, nonce));
connectionThreeAuthenticatedSpy.wait(); connectionThreeAuthenticatedSpy.wait();
QVERIFY(connectionOneAuthenticatedSpy.count() == 1); QVERIFY(connectionOneAuthenticatedSpy.count() == 1);
connectionOneDisconnectedSpy.wait(200); connectionThreeDisconnectedSpy.wait(200);
connectionTwoDisconnectedSpy.wait(200); QVERIFY(connectionThreeDisconnectedSpy.count() >= 1);
QVERIFY(connectionOneDisconnectedSpy.count() >= 1); // Make sure the one and two are still connected
QVERIFY(connectionTwoDisconnectedSpy.count() >= 1); QVERIFY(connectionOne->state() == RemoteProxyConnection::StateRemoteConnected);
QVERIFY(connectionOne->state() == RemoteProxyConnection::StateDisconnected); QVERIFY(connectionTwo->state() == RemoteProxyConnection::StateRemoteConnected);
QVERIFY(connectionTwo->state() == RemoteProxyConnection::StateDisconnected);
// Clean up // Clean up
stopServer(); stopServer();
@ -885,6 +915,9 @@ void RemoteProxyOfflineTests::jsonRpcTimeout()
// Configure result (authentication takes longer than json rpc timeout // Configure result (authentication takes longer than json rpc timeout
m_mockAuthenticator->setExpectedAuthenticationError(); m_mockAuthenticator->setExpectedAuthenticationError();
m_mockAuthenticator->setTimeoutDuration(4000); m_mockAuthenticator->setTimeoutDuration(4000);
m_configuration->setAuthenticationTimeout(4000);
m_configuration->setJsonRpcTimeout(1000);
m_configuration->setInactiveTimeout(2000);
// Create request // Create request
QVariantMap params; QVariantMap params;

View File

@ -70,6 +70,7 @@ private slots:
// Client lib // Client lib
void clientConnection(); void clientConnection();
void remoteConnection(); void remoteConnection();
void multipleRemoteConnection();
void trippleConnection(); void trippleConnection();
void duplicateUuid(); void duplicateUuid();
void sslConfigurations(); void sslConfigurations();

View File

@ -195,6 +195,119 @@ QVariant BaseTest::injectSocketData(const QByteArray &data)
return QVariant(); return QVariant();
} }
bool BaseTest::createRemoteConnection(const QString &token, const QString &nonce, QObject *parent)
{
// Configure mock 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, parent);
connect(connectionOne, &RemoteProxyConnection::sslErrors, this, &BaseTest::ignoreConnectionSslError);
RemoteProxyConnection *connectionTwo = new RemoteProxyConnection(uuidConnectionTwo, nameConnectionTwo, parent);
connect(connectionTwo, &RemoteProxyConnection::sslErrors, this, &BaseTest::ignoreConnectionSslError);
// Connect one
QSignalSpy connectionOneReadySpy(connectionOne, &RemoteProxyConnection::ready);
if (!connectionOne->connectServer(m_serverUrl)) {
qWarning() << "Could not connect client one";
return false;
}
connectionOneReadySpy.wait();
if (connectionOneReadySpy.count() != 1) {
qWarning() << "Could not connect client one";
return false;
}
if (!connectionOne->isConnected()) {
qWarning() << "Could not connect client one";
return false;
}
// Connect two
QSignalSpy connectionTwoReadySpy(connectionTwo, &RemoteProxyConnection::ready);
if (!connectionTwo->connectServer(m_serverUrl)) {
qWarning() << "Could not connect client two";
return false;
}
connectionTwoReadySpy.wait();
if (connectionTwoReadySpy.count() != 1) {
qWarning() << "Could not connect client two";
return false;
}
if (!connectionTwo->isConnected()) {
qWarning() << "Could not connect client two";
return false;
}
// Authenticate one
QSignalSpy remoteConnectionEstablishedOne(connectionOne, &RemoteProxyConnection::remoteConnectionEstablished);
QSignalSpy connectionOneAuthenticatedSpy(connectionOne, &RemoteProxyConnection::authenticated);
if (!connectionOne->authenticate(token, nonce)) {
qWarning() << "Could not authenticate client one";
return false;
}
connectionOneAuthenticatedSpy.wait(500);
if (connectionOneAuthenticatedSpy.count() != 1) {
qWarning() << "Could not authenticate client one";
return false;
}
if (connectionOne->state() != RemoteProxyConnection::StateAuthenticated) {
qWarning() << "Could not authenticate client one";
return false;
}
// Authenticate two
QSignalSpy remoteConnectionEstablishedTwo(connectionTwo, &RemoteProxyConnection::remoteConnectionEstablished);
QSignalSpy connectionTwoAuthenticatedSpy(connectionTwo, &RemoteProxyConnection::authenticated);
if (!connectionTwo->authenticate(token, nonce)) {
qWarning() << "Could not authenticate client two";
return false;
}
connectionTwoAuthenticatedSpy.wait(500);
if (connectionTwoAuthenticatedSpy.count() != 1) {
qWarning() << "Could not authenticate client two";
return false;
}
if (connectionTwo->state() != RemoteProxyConnection::StateAuthenticated && connectionTwo->state() != RemoteProxyConnection::StateRemoteConnected) {
qWarning() << "Could not authenticate client two";
return false;
}
// Wait for both to be connected
remoteConnectionEstablishedOne.wait(500);
remoteConnectionEstablishedTwo.wait(500);
if (remoteConnectionEstablishedOne.count() != 1 || remoteConnectionEstablishedTwo.count() != 1) {
qWarning() << "Could not establish remote connection";
return false;
}
if (connectionOne->state() != RemoteProxyConnection::StateRemoteConnected || connectionTwo->state() != RemoteProxyConnection::StateRemoteConnected) {
qWarning() << "Could not establish remote connection";
return false;
}
return true;
}
void BaseTest::initTestCase() void BaseTest::initTestCase()
{ {
qRegisterMetaType<RemoteProxyConnection::State>(); qRegisterMetaType<RemoteProxyConnection::State>();

View File

@ -81,6 +81,8 @@ protected:
QVariant invokeApiCall(const QString &method, const QVariantMap params = QVariantMap(), bool remainsConnected = true); QVariant invokeApiCall(const QString &method, const QVariantMap params = QVariantMap(), bool remainsConnected = true);
QVariant injectSocketData(const QByteArray &data); QVariant injectSocketData(const QByteArray &data);
bool createRemoteConnection(const QString &token, const QString &nonce, QObject *parent);
protected slots: protected slots:
void initTestCase(); void initTestCase();
void cleanupTestCase(); void cleanupTestCase();