From 6622fa1265b6f386a2ebb6dc57bfb7e52d763d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 15 Aug 2018 14:56:23 +0200 Subject: [PATCH] Add first online test --- .gitignore | 2 + README.md | 308 ++++++++++++++++++ client/proxyclient.cpp | 2 +- create-coverage-html.sh | 29 +- copyright => debian/copyright | 0 .../authentication/authenticationreply.cpp | 4 +- .../authentication/awsauthenticator.cpp | 2 +- libnymea-remoteproxy/engine.cpp | 37 +-- libnymea-remoteproxy/engine.h | 6 - libnymea-remoteproxy/proxyconfiguration.cpp | 59 ++++ libnymea-remoteproxy/proxyconfiguration.h | 8 + libnymea-remoteproxy/websocketserver.cpp | 2 +- .../proxyjsonrpcclient.cpp | 7 +- .../proxyjsonrpcclient.h | 1 - .../remoteproxyconnection.cpp | 43 ++- .../remoteproxyconnection.h | 4 +- nymea-remoteproxy.conf | 2 +- server/main.cpp | 38 +-- tests/online/online.pro | 2 - .../resources.qrc} | 1 + tests/{ => resources}/test-certificate.crt | 0 tests/{ => resources}/test-certificate.key | 0 tests/resources/test-configuration.conf | 13 + .../nymea-remoteproxy-tests-offline.cpp | 150 ++++----- .../nymea-remoteproxy-tests-offline.h | 11 +- .../test-offline.pro} | 0 .../nymea-remoteproxy-tests-online.cpp | 71 ++++ .../nymea-remoteproxy-tests-online.h | 31 ++ tests/test-online/test-online.pro | 13 + tests/testbase/basetest.cpp | 41 +-- tests/testbase/basetest.h | 12 +- tests/testbase/testbase.pri | 5 +- tests/tests.pro | 2 +- 33 files changed, 679 insertions(+), 227 deletions(-) rename copyright => debian/copyright (100%) delete mode 100644 tests/online/online.pro rename tests/{certificate.qrc => resources/resources.qrc} (77%) rename tests/{ => resources}/test-certificate.crt (100%) rename tests/{ => resources}/test-certificate.key (100%) create mode 100644 tests/resources/test-configuration.conf rename tests/{offline => test-offline}/nymea-remoteproxy-tests-offline.cpp (74%) rename tests/{offline => test-offline}/nymea-remoteproxy-tests-offline.h (75%) rename tests/{offline/offline.pro => test-offline/test-offline.pro} (100%) create mode 100644 tests/test-online/nymea-remoteproxy-tests-online.cpp create mode 100644 tests/test-online/nymea-remoteproxy-tests-online.h create mode 100644 tests/test-online/test-online.pro diff --git a/.gitignore b/.gitignore index 67809ed..80d3151 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,5 @@ Thumbs.db coverage-html client/nymea-remoteproxy-client +tests/test-offline/nymea-remoteproxy-tests-offline +tests/test-online/nymea-remoteproxy-tests-online diff --git a/README.md b/README.md index e69de29..8d851e2 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,308 @@ +# nymea remote proxy server +---------------------------------------------- + +The nymea remote proxy server is the meeting point of nymea servers and nymea clients in order to establishing a secure remote connection. + +# Build + +In order to build the proxy server you need to install the qt default package. + + apt install qt5-default + +Change into the source directory and run following commands + + cd nymea-remoteproxy + mkdir build + cd build + qmake ../ + make -j$(nproc) + +In the build directory you can find the resulting library and binary files. + +If you want to start the proxy server from the build directory, you need to export the library path before starting the application: + + + $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/libnymea-remoteproxy:$(pwd)/libnymea-remoteproxyclient + $ ./server/nymea-remoteproxy -c ../nymea-remoteproxy/tests/test-certificate.crt -k ../nymea-remoteproxy/tests/test-certificate.key + + +## AWS SDK + +Get the latest source code and build dependecies + + $ apt update + $ apt install git build-essential cmake libcurl4-openssl-dev libssl-dev uuid-dev zlib1g-dev libpulse-dev + + $ git clone https://github.com/aws/aws-sdk-cpp.git + +Create the build and install folder + + $ cd aws-sdk-cpp + $ mkdir -p build/install + $ cd build + + $ cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_ONLY="lambda" -DCMAKE_INSTALL_PREFIX=$(pwd)/install ../ + $ make -j$(nproc) + +Install build output into install directory + + $ make install + +#### Building debian package + + $ git clone https://github.com/aws/aws-sdk-cpp.git + $ cd aws-sdk-cpp + + $ git clone git@gitlab.guh.io:cloud/aws-sdk-cpp-debian.git debian + + $ crossbuilder + + +# Install + + + +# Configure + + + +# Test + +In order to run the test, you can call `make check` in the build directory or run the resulting executable: + + $ nymea-remoteproxy-tests + + +## Test coverage report + +If you want to create a line coverage report from the tests simply run following command in the source directory: + + + $ apt install lcov gcovr + $ ./create-coverage-html.sh + +The resulting coverage report will be place in the `coverage-html` directory. + +# Usage + +In order to get information about the server you can start the command with the `--help` parameter. + + $ nymea-remoteproxy --help + + Usage: nymea-remoteproxy [options] + + The nymea remote proxy server. This server allowes nymea-cloud users and registered nymea deamons to establish a tunnel connection. + + Server version: 0.0.1 + API version: 0.1 + + Copyright © 2018 Simon Stürz + + + Options: + -h, --help Displays this help. + -v, --version Displays version information. + -l, --logging Write log file to the given logfile. + -s, --server The server address this proxy will + listen on. Default is 127.0.0.1 + -p, --port The proxy server port. Default is + 1212 + -c, --certificate The path to the SSL certificate used + for this proxy server. + -k, --certificate-key The path to the SSL certificate key + used for this proxy server. + -a, --authentication-server The server url of the AWS + authentication server. + + +# 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. + +## Message format + +#### Request + + { + "id": integer, + "method": "Namespace.Method", + "o:params" { } + } + +#### Response + + { + "id": integer, + "status": "string", + "o:params" { }, + "o:error": "string" + } + +#### Notification + + { + "id": integer, + "notification": "Namespace.Notification", + "o:params" { } + } + +## Say Hello + +#### Request + + { + "id": 0, + "method": "RemoteProxy.Hello" + } + + +#### Response + + { + "id": 0, + "params": { + "apiVersion": "0.1", + "name": "nymea-remoteproxy-testserver", + "server": "nymea-remoteproxy", + "version": "0.0.1" + }, + "status": "success" + } + +## 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. + +#### Request + + { + "id": 1, + "method": "Authentication.Authenticate", + "params": { + "id": "string", + "name": "string", + "token": "tokenstring" + } + } + +#### Response + +* **On Success**: If the token was authenticated successfully, the response will look like this: + + { + "id": 1, + "status": "success" + } + +* **On Failure** If the token was invalid, the response will look like this and the server will close the connection immediatly: + + { + "id": 1, + "status": "error", + "error": "Invalid token. You are not allowed to use this server." + } + +#### Tunnel established + +Once the other client is here and ready, the server will send a notification to the clients indicating that the tunnel has been established successfully. This message is the last data comming from the proxy server. + +> **Important:** Any data traffic following after this notification comes from the tunnel endpoint, __not__ from the __proxy server__ any more. + + { + "id": "0", + "notification": "RemoteProxy.TunnelEstablished", + "params": { + "name": "String", + "uuid": "String" + } + } + + +## Introspect the API + + +#### Request + + { + "id": 0, + "method": "RemoteProxy.Introspect" + } + +#### 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" + } + }, + "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" + } + } + }, + "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", + "AuthenticationErrorAuthenticationServerNotResponding" + ], + "BasicType": [ + "Uuid", + "String", + "Int", + "UInt", + "Double", + "Bool", + "Variant", + "Object" + ] + } + }, + "status": "success" + } + + +# License + +This is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + diff --git a/client/proxyclient.cpp b/client/proxyclient.cpp index 91d1001..0eccf1f 100644 --- a/client/proxyclient.cpp +++ b/client/proxyclient.cpp @@ -4,7 +4,7 @@ ProxyClient::ProxyClient(QObject *parent) : QObject(parent) { - m_connection = new RemoteProxyConnection(QUuid::createUuid(), "nymea-remoteproxy-client", RemoteProxyConnection::ConnectionTypeWebSocket, this); + m_connection = new RemoteProxyConnection(QUuid::createUuid(), "nymea-remoteproxy-client", this); m_connection->setInsecureConnection(true); connect(m_connection, &RemoteProxyConnection::ready, this, &ProxyClient::onClientReady); connect(m_connection, &RemoteProxyConnection::authenticated, this, &ProxyClient::onAuthenticationFinished); diff --git a/create-coverage-html.sh b/create-coverage-html.sh index f1a22ac..7fa639f 100755 --- a/create-coverage-html.sh +++ b/create-coverage-html.sh @@ -8,9 +8,28 @@ qmake CONFIG+=coverage make -j$(nproc) make coverage-html -# Clean up source directory -rm -v libnymea-remoteproxy/libnymea-remoteproxy.so* -rm -v libnymea-remoteproxyclient/libnymea-remoteproxyclient.so* -rm -v server/nymea-remoteproxy -rm -v tests/nymea-remoteproxy-tests +# Clean build make clean + +# Clean source directory +rm -v Makefile + +rm -v libnymea-remoteproxy/libnymea-remoteproxy.so* +rm -v libnymea-remoteproxy/Makefile + +rm -v libnymea-remoteproxyclient/libnymea-remoteproxyclient.so* +rm -v libnymea-remoteproxyclient/Makefile + +rm -v server/nymea-remoteproxy +rm -v server/Makefile + +rm -v tests/Makefile + +rm -v tests/test-offline/nymea-remoteproxy-tests-offline +rm -v tests/test-offline/Makefile + +rm -v tests/test-online/nymea-remoteproxy-tests-online +rm -v tests/test-online/Makefile + +rm -v client/nymea-remoteproxy-client +rm -v client/Makefile diff --git a/copyright b/debian/copyright similarity index 100% rename from copyright rename to debian/copyright diff --git a/libnymea-remoteproxy/authentication/authenticationreply.cpp b/libnymea-remoteproxy/authentication/authenticationreply.cpp index b6bb389..845013a 100644 --- a/libnymea-remoteproxy/authentication/authenticationreply.cpp +++ b/libnymea-remoteproxy/authentication/authenticationreply.cpp @@ -52,7 +52,7 @@ void AuthenticationReply::onTimeout() m_error = Authenticator::AuthenticationErrorTimeout; m_timer->stop(); m_process->kill(); - emit finished(); + QTimer::singleShot(0, this, &AuthenticationReply::finished); } void AuthenticationReply::abort() @@ -60,7 +60,7 @@ void AuthenticationReply::abort() m_error = Authenticator::AuthenticationErrorAborted; m_timer->stop(); m_process->kill(); - emit finished(); + QTimer::singleShot(0, this, &AuthenticationReply::finished); } } diff --git a/libnymea-remoteproxy/authentication/awsauthenticator.cpp b/libnymea-remoteproxy/authentication/awsauthenticator.cpp index d088e40..70f9ace 100644 --- a/libnymea-remoteproxy/authentication/awsauthenticator.cpp +++ b/libnymea-remoteproxy/authentication/awsauthenticator.cpp @@ -30,7 +30,7 @@ void AwsAuthenticator::onAuthenticationProcessFinished(Authenticator::Authentica setReplyError(reply, error); setReplyFinished(reply); - qCDebug(dcAuthentication()) << "" << error; + qCDebug(dcAuthentication()) << name() << "finished with error" << error; } AuthenticationReply *AwsAuthenticator::authenticate(ProxyClient *proxyClient) diff --git a/libnymea-remoteproxy/engine.cpp b/libnymea-remoteproxy/engine.cpp index a6b092f..d19e6af 100644 --- a/libnymea-remoteproxy/engine.cpp +++ b/libnymea-remoteproxy/engine.cpp @@ -32,9 +32,6 @@ bool Engine::exists() void Engine::start(ProxyConfiguration *configuration) { - // Make sure an authenticator was registered - Q_ASSERT_X(m_authenticator != nullptr, "Engine", "There is no authenticator registerd."); - if (!m_running) qCDebug(dcEngine()) << "Start server engine"; @@ -42,10 +39,13 @@ void Engine::start(ProxyConfiguration *configuration) clean(); m_configuration = configuration; - Q_ASSERT_X(configuration != nullptr, "Engine", "There is no configuration set."); + qCDebug(dcApplication()) << "Using configuration" << m_configuration; + + // Make sure an authenticator was registered + Q_ASSERT_X(m_authenticator != nullptr, "Engine", "There is no authenticator registerd."); m_proxyServer = new ProxyServer(this); - m_webSocketServer = new WebSocketServer(m_sslConfiguration, this); + m_webSocketServer = new WebSocketServer(m_configuration->sslConfiguration(), this); QUrl websocketServerUrl; websocketServerUrl.setScheme("wss"); @@ -84,32 +84,7 @@ bool Engine::developerMode() const QString Engine::serverName() const { - return m_serverName; -} - -void Engine::setServerName(const QString &serverName) -{ - m_serverName = serverName; -} - -void Engine::setConfiguration(ProxyConfiguration *configuration) -{ - m_configuration = configuration; - qCDebug(dcApplication()) << "Set configuration" << m_configuration; -} - -void Engine::setSslConfiguration(const QSslConfiguration &configuration) -{ - m_sslConfiguration = configuration; - - qCDebug(dcEngine()) << "SSL certificate information:"; - qCDebug(dcEngine()) << " Common name:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::CommonName); - qCDebug(dcEngine()) << " Organisation:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::Organization); - qCDebug(dcEngine()) << " Organisation unit name:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::OrganizationalUnitName); - qCDebug(dcEngine()) << " Country name:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::CountryName); - qCDebug(dcEngine()) << " Locality name:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::LocalityName); - qCDebug(dcEngine()) << " State/Province:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::StateOrProvinceName); - qCDebug(dcEngine()) << " Email address:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::EmailAddress); + return m_configuration->serverName(); } void Engine::setAuthenticator(Authenticator *authenticator) diff --git a/libnymea-remoteproxy/engine.h b/libnymea-remoteproxy/engine.h index b97e8d0..fed4e98 100644 --- a/libnymea-remoteproxy/engine.h +++ b/libnymea-remoteproxy/engine.h @@ -29,12 +29,8 @@ public: bool developerMode() const; QString serverName() const; - void setServerName(const QString &serverName); - void setConfiguration(ProxyConfiguration *configuration); - void setSslConfiguration(const QSslConfiguration &configuration); void setAuthenticator(Authenticator *authenticator); - void setDeveloperModeEnabled(bool enabled); Authenticator *authenticator() const; @@ -48,9 +44,7 @@ private: bool m_running = false; bool m_developerMode = false; - QString m_serverName; - QSslConfiguration m_sslConfiguration; ProxyConfiguration *m_configuration = nullptr; Authenticator *m_authenticator = nullptr; ProxyServer *m_proxyServer = nullptr; diff --git a/libnymea-remoteproxy/proxyconfiguration.cpp b/libnymea-remoteproxy/proxyconfiguration.cpp index 662bd44..a9c1b69 100644 --- a/libnymea-remoteproxy/proxyconfiguration.cpp +++ b/libnymea-remoteproxy/proxyconfiguration.cpp @@ -1,7 +1,10 @@ #include "loggingcategories.h" #include "proxyconfiguration.h" +#include +#include #include +#include namespace remoteproxy { @@ -26,6 +29,7 @@ bool ProxyConfiguration::loadConfiguration(const QString &fileName) QSettings settings(fileName, QSettings::IniFormat); + setServerName(settings.value("name", "nymea-remoteproxy").toString()); setWriteLogFile(settings.value("writeLogs", false).toBool()); setLogFileName(settings.value("logFile", "/var/log/nymea-remoteproxy.log").toString()); setSslCertificateFileName(settings.value("certificate", "/etc/ssl/certs/ssl-cert-snakeoil.pem").toString()); @@ -41,9 +45,50 @@ bool ProxyConfiguration::loadConfiguration(const QString &fileName) setTcpServerPort(static_cast(settings.value("port", 1213).toInt())); settings.endGroup(); + // Load ssl configuration + QSslConfiguration sslConfiguration; + // Load certificate + QFile certFile(sslCertificateFileName()); + if (!certFile.open(QIODevice::ReadOnly)) { + qCWarning(dcApplication()) << "Could not open certificate file" << sslCertificateFileName() << certFile.errorString(); + return false; + } + + QSslCertificate certificate(&certFile, QSsl::Pem); + qCDebug(dcApplication()) << "Loaded successfully certificate" << sslCertificateFileName(); + certFile.close(); + + // Create SSL configuration + sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); + sslConfiguration.setLocalCertificate(certificate); + sslConfiguration.setProtocol(QSsl::TlsV1_2OrLater); + + // SSL key + QFile certKeyFile(sslCertificateKeyFileName()); + if (!certKeyFile.open(QIODevice::ReadOnly)) { + qCWarning(dcApplication()) << "Could not open certificate key file:" << sslCertificateKeyFileName() << certKeyFile.errorString(); + return false; + } + + QSslKey sslKey(&certKeyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + qCDebug(dcApplication()) << "Loaded successfully certificate key" << sslCertificateKeyFileName(); + certKeyFile.close(); + sslConfiguration.setPrivateKey(sslKey); + m_sslConfiguration = sslConfiguration; + return true; } +QString ProxyConfiguration::serverName() const +{ + return m_serverName; +} + +void ProxyConfiguration::setServerName(const QString &serverName) +{ + m_serverName = serverName; +} + bool ProxyConfiguration::writeLogFile() const { return m_writeLogFile; @@ -84,6 +129,11 @@ void ProxyConfiguration::setSslCertificateKeyFileName(const QString &fileName) m_sslCertificateKeyFileName = fileName; } +QSslConfiguration ProxyConfiguration::sslConfiguration() const +{ + return m_sslConfiguration; +} + QHostAddress ProxyConfiguration::webSocketServerHost() const { return m_webSocketServerHost; @@ -128,10 +178,19 @@ QDebug operator<<(QDebug debug, ProxyConfiguration *configuration) { debug.nospace() << endl << "========== ProxyConfiguration ==========" << endl; debug.nospace() << "General" << endl; + debug.nospace() << " - name:" << configuration->serverName() << endl; debug.nospace() << " - write logfile:" << configuration->writeLogFile() << endl; debug.nospace() << " - logfile:" << configuration->logFileName() << endl; debug.nospace() << " - certificate:" << configuration->sslCertificateFileName() << endl; debug.nospace() << " - certificate key:" << configuration->sslCertificateKeyFileName() << endl; + debug.nospace() << " - SSL certificate information:"; + debug.nospace() << " Common name:" << configuration->sslConfiguration().localCertificate().issuerInfo(QSslCertificate::CommonName); + debug.nospace() << " Organisation:" << configuration->sslConfiguration().localCertificate().issuerInfo(QSslCertificate::Organization); + debug.nospace() << " Organisation unit name:" << configuration->sslConfiguration().localCertificate().issuerInfo(QSslCertificate::OrganizationalUnitName); + debug.nospace() << " Country name:" << configuration->sslConfiguration().localCertificate().issuerInfo(QSslCertificate::CountryName); + debug.nospace() << " Locality name:" << configuration->sslConfiguration().localCertificate().issuerInfo(QSslCertificate::LocalityName); + debug.nospace() << " State/Province:" << configuration->sslConfiguration().localCertificate().issuerInfo(QSslCertificate::StateOrProvinceName); + debug.nospace() << " Email address:" << configuration->sslConfiguration().localCertificate().issuerInfo(QSslCertificate::EmailAddress); debug.nospace() << "WebSocketServer" << endl; debug.nospace() << " - host:" << configuration->webSocketServerHost().toString() << endl; debug.nospace() << " - port:" << configuration->webSocketServerPort() << endl; diff --git a/libnymea-remoteproxy/proxyconfiguration.h b/libnymea-remoteproxy/proxyconfiguration.h index d8cde2f..41b5ebe 100644 --- a/libnymea-remoteproxy/proxyconfiguration.h +++ b/libnymea-remoteproxy/proxyconfiguration.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace remoteproxy { @@ -16,6 +17,9 @@ public: bool loadConfiguration(const QString &fileName); // General + QString serverName() const; + void setServerName(const QString &serverName); + bool writeLogFile() const; void setWriteLogFile(bool enabled); @@ -28,6 +32,8 @@ public: QString sslCertificateKeyFileName() const; void setSslCertificateKeyFileName(const QString &fileName); + QSslConfiguration sslConfiguration() const; + // WebSocketServer QHostAddress webSocketServerHost() const; void setWebSocketServerHost(const QHostAddress &address); @@ -44,10 +50,12 @@ public: private: // General + QString m_serverName; bool m_writeLogFile = false; QString m_logFileName = "/var/log/nymea-remoteproxy.log"; QString m_sslCertificateFileName = "/etc/ssl/certs/ssl-cert-snakeoil.pem"; QString m_sslCertificateKeyFileName = "/etc/ssl/private/ssl-cert-snakeoil.key"; + QSslConfiguration m_sslConfiguration; // WebSocketServer QHostAddress m_webSocketServerHost = QHostAddress::LocalHost; diff --git a/libnymea-remoteproxy/websocketserver.cpp b/libnymea-remoteproxy/websocketserver.cpp index 56a4069..8f25410 100644 --- a/libnymea-remoteproxy/websocketserver.cpp +++ b/libnymea-remoteproxy/websocketserver.cpp @@ -46,7 +46,7 @@ void WebSocketServer::sendData(const QUuid &clientId, const QByteArray &data) client = m_clientList.value(clientId); if (client) { qCDebug(dcWebSocketServerTraffic()) << "--> Sending data to client:" << data; - client->sendTextMessage(data + '\n'); + client->sendTextMessage(data); } else { qCWarning(dcWebSocketServer()) << "Client" << clientId << "unknown to this transport"; } diff --git a/libnymea-remoteproxyclient/proxyjsonrpcclient.cpp b/libnymea-remoteproxyclient/proxyjsonrpcclient.cpp index b421642..ba9ad7c 100644 --- a/libnymea-remoteproxyclient/proxyjsonrpcclient.cpp +++ b/libnymea-remoteproxyclient/proxyjsonrpcclient.cpp @@ -32,7 +32,7 @@ JsonReply *JsonRpcClient::callAuthenticate(const QUuid &clientUuid, const QStrin params.insert("token", token); JsonReply *reply = new JsonReply(m_commandId, "Authentication", "Authenticate", params, this); - qCDebug(dcRemoteProxyClientJsonRpc()) << "Calling" << QString("%1.%2").arg(reply->nameSpace()).arg(reply->method()) << params; + qCDebug(dcRemoteProxyClientJsonRpc()) << "Calling" << QString("%1.%2").arg(reply->nameSpace()).arg(reply->method()) << reply->params(); sendRequest(reply->requestMap()); m_replies.insert(m_commandId, reply); return reply; @@ -45,11 +45,6 @@ void JsonRpcClient::sendRequest(const QVariantMap &request) m_connection->sendData(data); } -void JsonRpcClient::onConnectedChanged(bool connected) -{ - Q_UNUSED(connected) -} - void JsonRpcClient::processData(const QByteArray &data) { qCDebug(dcRemoteProxyClientJsonRpcTraffic()) << "Received data:" << qUtf8Printable(data); diff --git a/libnymea-remoteproxyclient/proxyjsonrpcclient.h b/libnymea-remoteproxyclient/proxyjsonrpcclient.h index 8d31427..fe646b6 100644 --- a/libnymea-remoteproxyclient/proxyjsonrpcclient.h +++ b/libnymea-remoteproxyclient/proxyjsonrpcclient.h @@ -36,7 +36,6 @@ signals: void tunnelEstablished(const QString clientName, const QString &clientUuid); public slots: - void onConnectedChanged(bool connected); void processData(const QByteArray &data); }; diff --git a/libnymea-remoteproxyclient/remoteproxyconnection.cpp b/libnymea-remoteproxyclient/remoteproxyconnection.cpp index b476013..498fc01 100644 --- a/libnymea-remoteproxyclient/remoteproxyconnection.cpp +++ b/libnymea-remoteproxyclient/remoteproxyconnection.cpp @@ -1,16 +1,15 @@ -#include "remoteproxyconnection.h" -#include "websocketconnection.h" -#include "proxyjsonrpcclient.h" #include "proxyconnection.h" +#include "proxyjsonrpcclient.h" +#include "websocketconnection.h" +#include "remoteproxyconnection.h" Q_LOGGING_CATEGORY(dcRemoteProxyClientConnection, "RemoteProxyClientConnection") Q_LOGGING_CATEGORY(dcRemoteProxyClientConnectionTraffic, "RemoteProxyClientConnectionTraffic") namespace remoteproxyclient { -RemoteProxyConnection::RemoteProxyConnection(const QUuid &clientUuid, const QString &clientName, ConnectionType connectionType, QObject *parent) : +RemoteProxyConnection::RemoteProxyConnection(const QUuid &clientUuid, const QString &clientName, QObject *parent) : QObject(parent), - m_connectionType(connectionType), m_clientUuid(clientUuid), m_clientName(clientName) { @@ -82,6 +81,11 @@ RemoteProxyConnection::ConnectionType RemoteProxyConnection::connectionType() co return m_connectionType; } +void RemoteProxyConnection::setConnectionType(RemoteProxyConnection::ConnectionType connectionType) +{ + m_connectionType = connectionType; +} + QHostAddress RemoteProxyConnection::serverAddress() const { return m_serverAddress; @@ -150,7 +154,6 @@ void RemoteProxyConnection::cleanUp() m_connection = nullptr; } - m_error = ErrorNoError; m_serverName = QString(); m_proxyServerName = QString(); m_proxyServerVersion = QString(); @@ -164,8 +167,8 @@ void RemoteProxyConnection::setState(RemoteProxyConnection::State state) if (m_state == state) return; - qCDebug(dcRemoteProxyClientConnection()) << "State changed" << state; m_state = state; + qCDebug(dcRemoteProxyClientConnection()) << "State changed" << m_state; emit stateChanged(m_state); } @@ -174,8 +177,8 @@ void RemoteProxyConnection::setError(RemoteProxyConnection::Error error) if (m_error == error) return; - qCDebug(dcRemoteProxyClientConnection()) << "Error occured" << error; m_error = error; + qCDebug(dcRemoteProxyClientConnection()) << "Error occured" << m_error << errorString(); emit errorOccured(m_error); } @@ -234,7 +237,7 @@ void RemoteProxyConnection::onHelloFinished() reply->deleteLater(); QVariantMap response = reply->response(); - qCDebug(dcRemoteProxyClientConnection()) << "Hello response ready" << response; + qCDebug(dcRemoteProxyClientConnectionTraffic()) << "Hello response ready" << reply->commandId() << response; if (response.value("status").toString() != "success") { qCWarning(dcRemoteProxyClientConnection()) << "Could not get initial information from proxy server."; @@ -258,12 +261,21 @@ void RemoteProxyConnection::onHelloFinished() void RemoteProxyConnection::onAuthenticateFinished() { JsonReply *reply = static_cast(sender()); - QVariantMap response = reply->response(); - qCDebug(dcRemoteProxyClientConnection()) << "Authentication response ready" << response; + reply->deleteLater(); - // TODO: verify success - setState(StateWaitTunnel); - emit authenticated(); + QVariantMap response = reply->response(); + qCDebug(dcRemoteProxyClientConnectionTraffic()) << "Authentication response ready" << reply->commandId() << response; + + QVariantMap responseParams = response.value("params").toMap(); + if (responseParams.value("authenticationError").toString() != "AuthenticationErrorNoError") { + qCWarning(dcRemoteProxyClientConnection()) << "Authentication request finished with error" << responseParams.value("authenticationError").toString(); + setError(RemoteProxyConnection::ErrorProxyAuthenticationFailed); + m_connection->disconnectServer(); + } else { + qCDebug(dcRemoteProxyClientConnection()) << "Successfully authenticated."; + setState(StateWaitTunnel); + emit authenticated(); + } } void RemoteProxyConnection::onTunnelEstablished(const QString &clientName, const QString &clientUuid) @@ -277,6 +289,7 @@ bool RemoteProxyConnection::connectServer(const QHostAddress &serverAddress, qui { m_serverAddress = serverAddress; m_serverPort = port; + m_error = ErrorNoError; cleanUp(); @@ -295,7 +308,7 @@ bool RemoteProxyConnection::connectServer(const QHostAddress &serverAddress, qui m_jsonClient = new JsonRpcClient(m_connection, this); connect(m_jsonClient, &JsonRpcClient::tunnelEstablished, this, &RemoteProxyConnection::onTunnelEstablished); - qCDebug(dcRemoteProxyClientConnection()) << "Connecting to" << m_connection->serverUrl().toString(); + qCDebug(dcRemoteProxyClientConnection()) << "Connecting to" << QString("%1:%2").arg(serverAddress.toString()).arg(port); m_connection->connectServer(serverAddress, port); setState(StateConnecting); diff --git a/libnymea-remoteproxyclient/remoteproxyconnection.h b/libnymea-remoteproxyclient/remoteproxyconnection.h index ed625c8..cbc264a 100644 --- a/libnymea-remoteproxyclient/remoteproxyconnection.h +++ b/libnymea-remoteproxyclient/remoteproxyconnection.h @@ -46,7 +46,7 @@ public: }; Q_ENUM(Error) - explicit RemoteProxyConnection(const QUuid &clientUuid, const QString &clientName, ConnectionType connectionType = RemoteProxyConnection::ConnectionTypeWebSocket, QObject *parent = nullptr); + explicit RemoteProxyConnection(const QUuid &clientUuid, const QString &clientName, QObject *parent = nullptr); ~RemoteProxyConnection(); RemoteProxyConnection::State state() const; @@ -58,6 +58,8 @@ public: bool isRemoteConnected() const; RemoteProxyConnection::ConnectionType connectionType() const; + void setConnectionType(RemoteProxyConnection::ConnectionType connectionType); + QHostAddress serverAddress() const; quint16 serverPort() const; diff --git a/nymea-remoteproxy.conf b/nymea-remoteproxy.conf index 244249f..d5036bc 100644 --- a/nymea-remoteproxy.conf +++ b/nymea-remoteproxy.conf @@ -1,4 +1,4 @@ -[General] +name=nymea-remoteproxy writeLogs=false logFile=/var/log/nymea-remoteproxy.log certificate=/etc/ssl/certs/ssl-cert-snakeoil.pem diff --git a/server/main.cpp b/server/main.cpp index 6eb2b3e..1e869be 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -167,37 +167,8 @@ int main(int argc, char *argv[]) exit(-1); } - // SSL certificate - QSslConfiguration sslConfiguration; - // Load certificate - QFile certFile(configuration->sslCertificateFileName()); - if (!certFile.open(QIODevice::ReadOnly)) { - qCCritical(dcApplication()) << "Could not open certificate file" << configuration->sslCertificateFileName() << certFile.errorString(); - exit(-1); - } - - QSslCertificate certificate(&certFile, QSsl::Pem); - qCDebug(dcApplication()) << "Loaded successfully certificate" << configuration->sslCertificateFileName(); - certFile.close(); - - // Create SSL configuration - sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); - sslConfiguration.setLocalCertificate(certificate); - sslConfiguration.setProtocol(QSsl::TlsV1_2OrLater); - - // SSL key - QFile certKeyFile(configuration->sslCertificateKeyFileName()); - if (!certKeyFile.open(QIODevice::ReadOnly)) { - qCCritical(dcApplication()) << "Could not open certificate key file:" << configuration->sslCertificateKeyFileName() << certKeyFile.errorString(); - exit(-1); - } - - QSslKey sslKey(&certKeyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); - qCDebug(dcApplication()) << "Loaded successfully certificate key" << configuration->sslCertificateKeyFileName(); - certKeyFile.close(); - sslConfiguration.setPrivateKey(sslKey); - - if (sslConfiguration.isNull()) { + // Verify SSL configuration + if (configuration->sslConfiguration().isNull()) { qCCritical(dcApplication()) << "No SSL configuration specified. The server does not suppoert insecure connections."; exit(-1); } @@ -224,11 +195,8 @@ int main(int argc, char *argv[]) } // Configure and start the engines - Engine::instance()->setConfiguration(configuration); - Engine::instance()->setDeveloperModeEnabled(parser.isSet(developmentOption)); - Engine::instance()->setSslConfiguration(sslConfiguration); Engine::instance()->setAuthenticator(authenticator); - + Engine::instance()->setDeveloperModeEnabled(parser.isSet(developmentOption)); Engine::instance()->start(configuration); return application.exec(); diff --git a/tests/online/online.pro b/tests/online/online.pro deleted file mode 100644 index 597d48e..0000000 --- a/tests/online/online.pro +++ /dev/null @@ -1,2 +0,0 @@ -include(../../nymea-remoteproxy.pri) - diff --git a/tests/certificate.qrc b/tests/resources/resources.qrc similarity index 77% rename from tests/certificate.qrc rename to tests/resources/resources.qrc index f92e6f8..3e2f3fd 100644 --- a/tests/certificate.qrc +++ b/tests/resources/resources.qrc @@ -2,5 +2,6 @@ test-certificate.crt test-certificate.key + test-configuration.conf diff --git a/tests/test-certificate.crt b/tests/resources/test-certificate.crt similarity index 100% rename from tests/test-certificate.crt rename to tests/resources/test-certificate.crt diff --git a/tests/test-certificate.key b/tests/resources/test-certificate.key similarity index 100% rename from tests/test-certificate.key rename to tests/resources/test-certificate.key diff --git a/tests/resources/test-configuration.conf b/tests/resources/test-configuration.conf new file mode 100644 index 0000000..d6d4888 --- /dev/null +++ b/tests/resources/test-configuration.conf @@ -0,0 +1,13 @@ +name=test-nymea-remoteproxy +writeLogs=false +logFile=/var/log/nymea-remoteproxy.log +certificate=:/test-certificate.crt +certificateKey=:/test-certificate.key + +[WebSocketServer] +host=127.0.0.1 +port=1212 + +[TcpServer] +host=127.0.0.1 +port=1213 diff --git a/tests/offline/nymea-remoteproxy-tests-offline.cpp b/tests/test-offline/nymea-remoteproxy-tests-offline.cpp similarity index 74% rename from tests/offline/nymea-remoteproxy-tests-offline.cpp rename to tests/test-offline/nymea-remoteproxy-tests-offline.cpp index 99c8fe6..02a2e4c 100644 --- a/tests/offline/nymea-remoteproxy-tests-offline.cpp +++ b/tests/test-offline/nymea-remoteproxy-tests-offline.cpp @@ -10,27 +10,24 @@ #include #include -RemoteProxyTests::RemoteProxyTests(QObject *parent) : +RemoteProxyOfflineTests::RemoteProxyOfflineTests(QObject *parent) : BaseTest(parent) { } -void RemoteProxyTests::startStopServer() +void RemoteProxyOfflineTests::startStopServer() { startServer(); stopServer(); } -void RemoteProxyTests::dummyAuthenticator() +void RemoteProxyOfflineTests::dummyAuthenticator() { cleanUpEngine(); - DummyAuthenticator *dummyAuthenticator = new DummyAuthenticator(this); // Start proxy webserver - Engine::instance()->setAuthenticator(dummyAuthenticator); - Engine::instance()->setSslConfiguration(m_sslConfiguration); - + Engine::instance()->setAuthenticator(m_dummyAuthenticator); QSignalSpy runningSpy(Engine::instance(), &Engine::runningChanged); Engine::instance()->start(m_configuration); runningSpy.wait(); @@ -53,22 +50,18 @@ void RemoteProxyTests::dummyAuthenticator() qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); verifyAuthenticationError(response); - dummyAuthenticator->deleteLater(); cleanUpEngine(); } -void RemoteProxyTests::webserverConnectionBlocked() +void RemoteProxyOfflineTests::webserverConnectionBlocked() { // Create a dummy server which blocks the port QWebSocketServer dummyServer("dummy-server", QWebSocketServer::NonSecureMode); dummyServer.listen(QHostAddress::LocalHost, m_port); // Start proxy webserver - Engine::instance()->setConfiguration(m_configuration); - Engine::instance()->setAuthenticator(m_authenticator); - Engine::instance()->setSslConfiguration(m_sslConfiguration); - QSignalSpy runningSpy(Engine::instance(), &Engine::runningChanged); + Engine::instance()->setAuthenticator(m_authenticator); Engine::instance()->start(m_configuration); runningSpy.wait(); @@ -93,12 +86,7 @@ void RemoteProxyTests::webserverConnectionBlocked() stopServer(); } -void RemoteProxyTests::webserverConnection() -{ - -} - -void RemoteProxyTests::getIntrospect() +void RemoteProxyOfflineTests::getIntrospect() { // Start the server startServer(); @@ -110,7 +98,7 @@ void RemoteProxyTests::getIntrospect() stopServer(); } -void RemoteProxyTests::getHello() +void RemoteProxyOfflineTests::getHello() { // Start the server startServer(); @@ -122,7 +110,7 @@ void RemoteProxyTests::getHello() stopServer(); } -void RemoteProxyTests::apiBasicCalls_data() +void RemoteProxyOfflineTests::apiBasicCalls_data() { QTest::addColumn("data"); QTest::addColumn("valid"); @@ -137,7 +125,7 @@ void RemoteProxyTests::apiBasicCalls_data() QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\", \"params\":{\"törööö\":\"chooo-chooo\"}}") << false; } -void RemoteProxyTests::apiBasicCalls() +void RemoteProxyOfflineTests::apiBasicCalls() { QFETCH(QByteArray, data); QFETCH(bool, valid); @@ -154,7 +142,7 @@ void RemoteProxyTests::apiBasicCalls() stopServer(); } -void RemoteProxyTests::authenticate_data() +void RemoteProxyOfflineTests::authenticate_data() { QTest::addColumn("uuid"); QTest::addColumn("name"); @@ -180,7 +168,7 @@ void RemoteProxyTests::authenticate_data() } -void RemoteProxyTests::authenticate() +void RemoteProxyOfflineTests::authenticate() { QFETCH(QString, uuid); QFETCH(QString, name); @@ -192,8 +180,8 @@ void RemoteProxyTests::authenticate() startServer(); // Configure result - m_authenticator->setExpectedAuthenticationError(expectedError); - m_authenticator->setTimeoutDuration(timeout); + m_mockAuthenticator->setExpectedAuthenticationError(expectedError); + m_mockAuthenticator->setTimeoutDuration(timeout); // Create request QVariantMap params; @@ -209,17 +197,17 @@ void RemoteProxyTests::authenticate() stopServer(); } -void RemoteProxyTests::clientConnection() +void RemoteProxyOfflineTests::clientConnection() { // Start the server startServer(); // Configure moch authenticator - m_authenticator->setTimeoutDuration(100); - m_authenticator->setExpectedAuthenticationError(); + m_mockAuthenticator->setTimeoutDuration(100); + m_mockAuthenticator->setExpectedAuthenticationError(); // Connect to the server (insecure disabled) - RemoteProxyConnection *connection = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", RemoteProxyConnection::ConnectionTypeWebSocket, this); + RemoteProxyConnection *connection = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", this); connection->setInsecureConnection(true); // Connect to server (insecue enabled for testing) @@ -260,20 +248,20 @@ void RemoteProxyTests::clientConnection() stopServer(); } -void RemoteProxyTests::remoteConnection() +void RemoteProxyOfflineTests::remoteConnection() { // Start the server startServer(); // Configure moch authenticator - m_authenticator->setTimeoutDuration(100); - m_authenticator->setExpectedAuthenticationError(); + m_mockAuthenticator->setTimeoutDuration(100); + m_mockAuthenticator->setExpectedAuthenticationError(); // Create two connection - RemoteProxyConnection *connectionOne = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", RemoteProxyConnection::ConnectionTypeWebSocket, this); + RemoteProxyConnection *connectionOne = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", this); connectionOne->setInsecureConnection(true); - RemoteProxyConnection *connectionTwo = new RemoteProxyConnection(QUuid::createUuid(), "Test client two", RemoteProxyConnection::ConnectionTypeWebSocket, this); + RemoteProxyConnection *connectionTwo = new RemoteProxyConnection(QUuid::createUuid(), "Test client two", this); connectionTwo->setInsecureConnection(true); // Connect one @@ -310,8 +298,8 @@ void RemoteProxyTests::remoteConnection() QVERIFY(connectionTwo->isAuthenticated()); // Wait for both to be connected - remoteConnectionEstablishedOne.wait(); - remoteConnectionEstablishedTwo.wait(); + remoteConnectionEstablishedOne.wait(500); + remoteConnectionEstablishedTwo.wait(500); QVERIFY(remoteConnectionEstablishedOne.count() == 1); QVERIFY(remoteConnectionEstablishedTwo.count() == 1); @@ -326,14 +314,16 @@ void RemoteProxyTests::remoteConnection() QByteArray dataTwo = "Hello from client two :-)"; connectionOne->sendData(dataOne); - remoteConnectionDataTwo.wait(); + remoteConnectionDataTwo.wait(500); QVERIFY(remoteConnectionDataTwo.count() == 1); + QCOMPARE(remoteConnectionDataTwo.at(0).at(0).toByteArray(), dataOne); // verify if data is the same connectionTwo->sendData(dataTwo); - remoteConnectionDataOne.wait(); + remoteConnectionDataOne.wait(500); QVERIFY(remoteConnectionDataOne.count() == 1); + QCOMPARE(remoteConnectionDataOne.at(0).at(0).toByteArray(), dataTwo); // verify if data is the same @@ -344,59 +334,59 @@ void RemoteProxyTests::remoteConnection() stopServer(); } -void RemoteProxyTests::sslConfigurations() +void RemoteProxyOfflineTests::sslConfigurations() { - // // Start the server - // startServer(); + // Start the server + startServer(); - // // Connect to the server (insecure disabled) - // RemoteProxyConnection *connector = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", RemoteProxyConnection::ConnectionTypeWebSocket, this); - // QSignalSpy spyError(connector, &RemoteProxyConnection::errorOccured); - // QVERIFY(connector->connectServer(QHostAddress::LocalHost, m_port)); - // spyError.wait(); - // QVERIFY(spyError.count() == 1); - // QVERIFY(connector->error() == RemoteProxyConnection::ErrorSslError); - // QVERIFY(connector->state() == RemoteProxyConnection::StateDisconnected); + // Connect to the server (insecure disabled) + RemoteProxyConnection *connector = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", this); + QSignalSpy spyError(connector, &RemoteProxyConnection::errorOccured); + QVERIFY(connector->connectServer(QHostAddress::LocalHost, m_port)); + spyError.wait(); + QVERIFY(spyError.count() == 2); + QVERIFY(connector->error() == RemoteProxyConnection::ErrorSocketError); + QVERIFY(connector->state() == RemoteProxyConnection::StateDisconnected); - // // Connect to server (insecue enabled) - // QSignalSpy spyConnected(connector, &RemoteProxyConnection::connected); - // connector->setInsecureConnection(true); - // connector->connectServer(QHostAddress::LocalHost, m_port); - // spyConnected.wait(); + // Connect to server (insecue enabled) + QSignalSpy spyConnected(connector, &RemoteProxyConnection::connected); + connector->setInsecureConnection(true); + connector->connectServer(QHostAddress::LocalHost, m_port); + spyConnected.wait(); - // QVERIFY(connector->isConnected()); + QVERIFY(connector->isConnected()); - // // Disconnect and clean up - // connector->disconnectServer(); - // QVERIFY(!connector->isConnected()); + // Disconnect and clean up + connector->disconnectServer(); + QVERIFY(!connector->isConnected()); - // connector->deleteLater(); - // stopServer(); + connector->deleteLater(); + stopServer(); } -void RemoteProxyTests::timeout() +void RemoteProxyOfflineTests::timeout() { - // // Start the server - // startServer(); + // Start the server + startServer(); - // // Configure result - // // Start the server - // startServer(); + // Configure result + // Start the server + startServer(); - // // Configure result - // m_authenticator->setExpectedAuthenticationError(); - // m_authenticator->setTimeoutDuration(6000); + // Configure result + m_mockAuthenticator->setExpectedAuthenticationError(); + m_mockAuthenticator->setTimeoutDuration(6000); - // // Create request - // QVariantMap params; - // params.insert("uuid", "uuid"); - // params.insert("name", "name"); - // params.insert("token", "token"); + // Create request + QVariantMap params; + params.insert("uuid", "uuid"); + params.insert("name", "name"); + params.insert("token", "token"); - // QVariant response = invokeApiCall("Authentication.Authenticate", params); - // qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); - // // Clean up - // stopServer(); + QVariant response = invokeApiCall("Authentication.Authenticate", params); + qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); + // Clean up + stopServer(); } -QTEST_MAIN(RemoteProxyTests) +QTEST_MAIN(RemoteProxyOfflineTests) diff --git a/tests/offline/nymea-remoteproxy-tests-offline.h b/tests/test-offline/nymea-remoteproxy-tests-offline.h similarity index 75% rename from tests/offline/nymea-remoteproxy-tests-offline.h rename to tests/test-offline/nymea-remoteproxy-tests-offline.h index 3406ddc..b2a0c90 100644 --- a/tests/offline/nymea-remoteproxy-tests-offline.h +++ b/tests/test-offline/nymea-remoteproxy-tests-offline.h @@ -1,5 +1,5 @@ -#ifndef NYMEA_REMOTEPROXY_TESTS_H -#define NYMEA_REMOTEPROXY_TESTS_H +#ifndef NYMEA_REMOTEPROXY_TESTS_OFFLINE_H +#define NYMEA_REMOTEPROXY_TESTS_OFFLINE_H #include #include @@ -15,11 +15,11 @@ using namespace remoteproxy; using namespace remoteproxyclient; -class RemoteProxyTests : public BaseTest +class RemoteProxyOfflineTests : public BaseTest { Q_OBJECT public: - explicit RemoteProxyTests(QObject *parent = nullptr); + explicit RemoteProxyOfflineTests(QObject *parent = nullptr); private slots: // Basic stuff @@ -28,7 +28,6 @@ private slots: // WebSocket connection void webserverConnectionBlocked(); - void webserverConnection(); // Api void getIntrospect(); @@ -48,4 +47,4 @@ private slots: }; -#endif // NYMEA_REMOTEPROXY_TESTS_H +#endif // NYMEA_REMOTEPROXY_TESTS_OFFLINE_H diff --git a/tests/offline/offline.pro b/tests/test-offline/test-offline.pro similarity index 100% rename from tests/offline/offline.pro rename to tests/test-offline/test-offline.pro diff --git a/tests/test-online/nymea-remoteproxy-tests-online.cpp b/tests/test-online/nymea-remoteproxy-tests-online.cpp new file mode 100644 index 0000000..452b53e --- /dev/null +++ b/tests/test-online/nymea-remoteproxy-tests-online.cpp @@ -0,0 +1,71 @@ +#include "engine.h" +#include "loggingcategories.h" +#include "remoteproxyconnection.h" +#include "nymea-remoteproxy-tests-online.h" + +#include +#include +#include +#include +#include + +RemoteProxyOnlineTests::RemoteProxyOnlineTests(QObject *parent) : + BaseTest(parent) +{ + m_authenticator = qobject_cast(m_awsAuthenticator); + +} + +void RemoteProxyOnlineTests::awsStaticCredentials() +{ + startServer(); + + // Connect to the server (insecure disabled) + RemoteProxyConnection *connection = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", this); + connection->setInsecureConnection(true); + + // Connect to server (insecue enabled for testing) + QSignalSpy readySpy(connection, &RemoteProxyConnection::ready); + QVERIFY(connection->connectServer(QHostAddress::LocalHost, m_port)); + readySpy.wait(); + QVERIFY(readySpy.count() == 1); + QVERIFY(connection->isConnected()); + QVERIFY(!connection->isRemoteConnected()); + QVERIFY(connection->state() == RemoteProxyConnection::StateReady); + QVERIFY(connection->error() == RemoteProxyConnection::ErrorNoError); + QVERIFY(connection->serverAddress() == QHostAddress::LocalHost); + QVERIFY(connection->serverPort() == m_port); + QVERIFY(connection->connectionType() == RemoteProxyConnection::ConnectionTypeWebSocket); + QVERIFY(connection->insecureConnection() == true); + QVERIFY(connection->serverName() == SERVER_NAME_STRING); + QVERIFY(connection->proxyServerName() == Engine::instance()->serverName()); + QVERIFY(connection->proxyServerVersion() == SERVER_VERSION_STRING); + QVERIFY(connection->proxyServerApiVersion() == API_VERSION_STRING); + + QSignalSpy errorSpy(connection, &RemoteProxyConnection::errorOccured); + QSignalSpy spyDisconnected(connection, &RemoteProxyConnection::disconnected); + + QVERIFY(connection->authenticate("foobar")); + errorSpy.wait(); + QVERIFY(errorSpy.count() == 1); + QVERIFY(connection->error() == RemoteProxyConnection::ErrorProxyAuthenticationFailed); + + // Disconnect and clean up + connection->disconnectServer(); + // FIXME: check why it waits the full time here + spyDisconnected.wait(100); + QVERIFY(spyDisconnected.count() == 1); + QVERIFY(!connection->isConnected()); + + connection->deleteLater(); + + stopServer(); +} + +void RemoteProxyOnlineTests::awsDynamicCredentials() +{ + + +} + +QTEST_MAIN(RemoteProxyOnlineTests) diff --git a/tests/test-online/nymea-remoteproxy-tests-online.h b/tests/test-online/nymea-remoteproxy-tests-online.h new file mode 100644 index 0000000..6feef0c --- /dev/null +++ b/tests/test-online/nymea-remoteproxy-tests-online.h @@ -0,0 +1,31 @@ +#ifndef NYMEA_REMOTEPROXY_TESTS_ONLINE_H +#define NYMEA_REMOTEPROXY_TESTS_ONLINE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basetest.h" + +using namespace remoteproxy; +using namespace remoteproxyclient; + +class RemoteProxyOnlineTests : public BaseTest +{ + Q_OBJECT +public: + explicit RemoteProxyOnlineTests(QObject *parent = nullptr); + +private slots: + void awsStaticCredentials(); + void awsDynamicCredentials(); + + +}; + +#endif // NYMEA_REMOTEPROXY_TESTS_ONLINE_H diff --git a/tests/test-online/test-online.pro b/tests/test-online/test-online.pro new file mode 100644 index 0000000..501f3c0 --- /dev/null +++ b/tests/test-online/test-online.pro @@ -0,0 +1,13 @@ +include(../../nymea-remoteproxy.pri) +include(../testbase/testbase.pri) + + + +TARGET = nymea-remoteproxy-tests-online + +HEADERS += nymea-remoteproxy-tests-online.h + +SOURCES += nymea-remoteproxy-tests-online.cpp + +target.path = /usr/bin +INSTALLS += target diff --git a/tests/testbase/basetest.cpp b/tests/testbase/basetest.cpp index a0d21f4..6db3d13 100644 --- a/tests/testbase/basetest.cpp +++ b/tests/testbase/basetest.cpp @@ -14,30 +14,13 @@ BaseTest::BaseTest(QObject *parent) : QObject(parent) { m_configuration = new ProxyConfiguration(this); + m_configuration->loadConfiguration(":/test-configuration.conf"); - QFile certificateFile(":/test-certificate.crt"); - if (!certificateFile.open(QIODevice::ReadOnly)) { - qWarning() << "Could not open resource file" << certificateFile.fileName(); - exit(1); - } + m_mockAuthenticator = new MockAuthenticator(this); + m_dummyAuthenticator = new DummyAuthenticator(this); + m_awsAuthenticator = new AwsAuthenticator(this); - QByteArray certificateData = certificateFile.readAll(); - //qDebug() << "Certificate:" << endl << qUtf8Printable(certificateData); - - QFile keyFile(":/test-certificate.key"); - if (!keyFile.open(QIODevice::ReadOnly)) { - qWarning() << "Could not open resource file" << keyFile.fileName(); - exit(1); - } - - QByteArray keyData = keyFile.readAll(); - //qDebug() << "Certificate key:" << endl << qUtf8Printable(keyData); - m_authenticator = new MockAuthenticator(this); - - m_sslConfiguration.setPrivateKey(QSslKey(keyData, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey)); - m_sslConfiguration.setLocalCertificate(QSslCertificate(certificateData, QSsl::Pem)); - m_sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); - m_sslConfiguration.setProtocol(QSsl::TlsV1_2OrLater); + m_authenticator = qobject_cast(m_mockAuthenticator); m_testToken = "eyJraWQiOiJXdnFFT3prVVh5VDlINzFyRUpoNWdxRnkxNFhnR2l3SFAzVEIzUFQ1V3ZrPSIsImFsZyI6IlJT" "MjU2In0.eyJzdWIiOiJmZTJmZDNlNC1hMGJhLTQ1OTUtOWRiZS00ZDkxYjRiMjFlMzUiLCJhdWQiOiI4cmpoZ" @@ -54,6 +37,13 @@ BaseTest::BaseTest(QObject *parent) : } +void BaseTest::loadConfiguration(const QString &fileName) +{ + qDebug() << "Load test configurations" << fileName; + m_configuration->loadConfiguration(fileName); + restartEngine(); +} + void BaseTest::cleanUpEngine() { if (Engine::exists()) { @@ -72,15 +62,9 @@ void BaseTest::restartEngine() void BaseTest::startEngine() { if (!Engine::exists()) { - QString serverName = "nymea-remoteproxy-testserver"; Engine::instance()->setAuthenticator(m_authenticator); - Engine::instance()->setServerName(serverName); - Engine::instance()->setConfiguration(m_configuration); - Engine::instance()->setSslConfiguration(m_sslConfiguration); Engine::instance()->setDeveloperModeEnabled(true); - QVERIFY(Engine::exists()); - QVERIFY(Engine::instance()->serverName() == serverName); } } @@ -90,7 +74,6 @@ void BaseTest::startServer() if (!Engine::instance()->running()) { QSignalSpy runningSpy(Engine::instance(), &Engine::runningChanged); - Engine::instance()->setConfiguration(m_configuration); Engine::instance()->setDeveloperModeEnabled(true); Engine::instance()->start(m_configuration); runningSpy.wait(); diff --git a/tests/testbase/basetest.h b/tests/testbase/basetest.h index 999bb4b..260a951 100644 --- a/tests/testbase/basetest.h +++ b/tests/testbase/basetest.h @@ -14,6 +14,7 @@ #include "mockauthenticator.h" #include "proxyconfiguration.h" #include "remoteproxyconnection.h" +#include "authentication/awsauthenticator.h" #include "authentication/dummyauthenticator.h" using namespace remoteproxy; @@ -32,11 +33,19 @@ protected: QHostAddress m_serverAddress = QHostAddress::LocalHost; QSslConfiguration m_sslConfiguration; - MockAuthenticator *m_authenticator = nullptr; + + Authenticator *m_authenticator = nullptr; + MockAuthenticator *m_mockAuthenticator = nullptr; + DummyAuthenticator *m_dummyAuthenticator = nullptr; + AwsAuthenticator *m_awsAuthenticator = nullptr; + QString m_testToken; int m_commandCounter = 0; + void loadConfiguration(const QString &fileName); + void setAuthenticator(Authenticator *authenticator); + void cleanUpEngine(); void restartEngine(); void startEngine(); @@ -50,7 +59,6 @@ protected slots: void initTestCase(); void cleanupTestCase(); - public slots: void sslErrors(const QList &) { QWebSocket *socket = static_cast(sender()); diff --git a/tests/testbase/testbase.pri b/tests/testbase/testbase.pri index c7462c2..a4572b7 100644 --- a/tests/testbase/testbase.pri +++ b/tests/testbase/testbase.pri @@ -1,4 +1,7 @@ -RESOURCES += ../certificate.qrc +RESOURCES += ../resources/resources.qrc + +CONFIG += testcase +QT += testlib INCLUDEPATH += $${PWD} $$top_srcdir/libnymea-remoteproxy $$top_srcdir/libnymea-remoteproxyclient LIBS += -L$$top_builddir/libnymea-remoteproxy/ -lnymea-remoteproxy \ diff --git a/tests/tests.pro b/tests/tests.pro index f46ccaf..9b5b4fe 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -1,4 +1,4 @@ TEMPLATE=subdirs -SUBDIRS += offline +SUBDIRS += test-offline test-online