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

no-deprecated-printf
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;
}
QString ProxyClient::tunnelIdentifier() const
{
return m_token + m_nonce;
}
QString ProxyClient::token() const
{
return m_token;

View File

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

View File

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

View File

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

View File

@ -46,6 +46,22 @@ QString TunnelConnection::token() const
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
{
return m_creationTimeStamp;

View File

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

View File

@ -699,6 +699,36 @@ void RemoteProxyOfflineTests::remoteConnection()
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()
{
// Start the server
@ -708,6 +738,8 @@ void RemoteProxyOfflineTests::trippleConnection()
m_mockAuthenticator->setTimeoutDuration(100);
m_mockAuthenticator->setExpectedAuthenticationError();
QString nonce = QUuid::createUuid().toString();
// Create two connection
RemoteProxyConnection *connectionOne = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", this);
connect(connectionOne, &RemoteProxyConnection::sslErrors, this, &BaseTest::ignoreConnectionSslError);
@ -720,7 +752,6 @@ void RemoteProxyOfflineTests::trippleConnection()
// Connect one
QSignalSpy connectionOneReadySpy(connectionOne, &RemoteProxyConnection::ready);
QSignalSpy connectionOneDisconnectedSpy(connectionOne, &RemoteProxyConnection::disconnected);
QVERIFY(connectionOne->connectServer(m_serverUrl));
connectionOneReadySpy.wait();
QVERIFY(connectionOneReadySpy.count() == 1);
@ -728,7 +759,6 @@ void RemoteProxyOfflineTests::trippleConnection()
// Connect two
QSignalSpy connectionTwoReadySpy(connectionTwo, &RemoteProxyConnection::ready);
QSignalSpy connectionTwoDisconnectedSpy(connectionTwo, &RemoteProxyConnection::disconnected);
QVERIFY(connectionTwo->connectServer(m_serverUrl));
connectionTwoReadySpy.wait();
QVERIFY(connectionTwoReadySpy.count() == 1);
@ -736,7 +766,7 @@ void RemoteProxyOfflineTests::trippleConnection()
// Authenticate one
QSignalSpy connectionOneAuthenticatedSpy(connectionOne, &RemoteProxyConnection::authenticated);
QVERIFY(connectionOne->authenticate(m_testToken));
QVERIFY(connectionOne->authenticate(m_testToken, nonce));
connectionOneAuthenticatedSpy.wait();
QVERIFY(connectionOneAuthenticatedSpy.count() == 1);
QVERIFY(connectionOne->isConnected());
@ -748,7 +778,7 @@ void RemoteProxyOfflineTests::trippleConnection()
// Authenticate two
QSignalSpy remoteConnectionEstablishedTwo(connectionTwo, &RemoteProxyConnection::remoteConnectionEstablished);
QSignalSpy connectionTwoAuthenticatedSpy(connectionTwo, &RemoteProxyConnection::authenticated);
QVERIFY(connectionTwo->authenticate(m_testToken));
QVERIFY(connectionTwo->authenticate(m_testToken, nonce));
connectionTwoAuthenticatedSpy.wait();
QVERIFY(connectionTwoAuthenticatedSpy.count() == 1);
QVERIFY(connectionTwo->isConnected());
@ -757,10 +787,11 @@ void RemoteProxyOfflineTests::trippleConnection()
// Wait for both to be connected
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
QSignalSpy connectionThreeReadySpy(connectionThree, &RemoteProxyConnection::ready);
QSignalSpy connectionThreeDisconnectedSpy(connectionThree, &RemoteProxyConnection::disconnected);
QVERIFY(connectionThree->connectServer(m_serverUrl));
connectionThreeReadySpy.wait();
QVERIFY(connectionThreeReadySpy.count() == 1);
@ -768,17 +799,16 @@ void RemoteProxyOfflineTests::trippleConnection()
// Authenticate three
QSignalSpy connectionThreeAuthenticatedSpy(connectionThree, &RemoteProxyConnection::authenticated);
QVERIFY(connectionThree->authenticate(m_testToken));
QVERIFY(connectionThree->authenticate(m_testToken, nonce));
connectionThreeAuthenticatedSpy.wait();
QVERIFY(connectionOneAuthenticatedSpy.count() == 1);
connectionOneDisconnectedSpy.wait(200);
connectionTwoDisconnectedSpy.wait(200);
connectionThreeDisconnectedSpy.wait(200);
QVERIFY(connectionThreeDisconnectedSpy.count() >= 1);
QVERIFY(connectionOneDisconnectedSpy.count() >= 1);
QVERIFY(connectionTwoDisconnectedSpy.count() >= 1);
QVERIFY(connectionOne->state() == RemoteProxyConnection::StateDisconnected);
QVERIFY(connectionTwo->state() == RemoteProxyConnection::StateDisconnected);
// Make sure the one and two are still connected
QVERIFY(connectionOne->state() == RemoteProxyConnection::StateRemoteConnected);
QVERIFY(connectionTwo->state() == RemoteProxyConnection::StateRemoteConnected);
// Clean up
stopServer();
@ -885,6 +915,9 @@ void RemoteProxyOfflineTests::jsonRpcTimeout()
// Configure result (authentication takes longer than json rpc timeout
m_mockAuthenticator->setExpectedAuthenticationError();
m_mockAuthenticator->setTimeoutDuration(4000);
m_configuration->setAuthenticationTimeout(4000);
m_configuration->setJsonRpcTimeout(1000);
m_configuration->setInactiveTimeout(2000);
// Create request
QVariantMap params;

View File

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

View File

@ -195,6 +195,119 @@ QVariant BaseTest::injectSocketData(const QByteArray &data)
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()
{
qRegisterMetaType<RemoteProxyConnection::State>();

View File

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