Add optional the nonce parameter for authentication and update docs
This commit is contained in:
parent
2dcf219c5a
commit
eacffb695f
150
README.md
150
README.md
@ -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>
|
||||
|
||||
|
||||
@ -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 ¶ms, 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);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -39,6 +39,8 @@ public:
|
||||
ProxyClient *clientOne() const;
|
||||
ProxyClient *clientTwo() const;
|
||||
|
||||
bool hasClient(ProxyClient *proxyClient) const;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
private:
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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}\\\" \
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -58,6 +58,9 @@ private slots:
|
||||
void authenticate_data();
|
||||
void authenticate();
|
||||
|
||||
void authenticateNonce();
|
||||
void authenticateSendData();
|
||||
|
||||
// Client lib
|
||||
void clientConnection();
|
||||
void remoteConnection();
|
||||
|
||||
@ -53,7 +53,7 @@ void MockAuthenticator::replyFinished()
|
||||
|
||||
setReplyError(reply->authenticationReply(), reply->error());
|
||||
setReplyFinished(reply->authenticationReply());
|
||||
delete reply;
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
AuthenticationReply *MockAuthenticator::authenticate(ProxyClient *proxyClient)
|
||||
|
||||
Reference in New Issue
Block a user