From db21e6cdf3f3393e2da99fa52366b79071089a25 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 14 Aug 2018 17:13:17 +0200 Subject: [PATCH] somewhat working --- .gitmodules | 3 + libnymea-app-core/connection/awsclient.cpp | 73 ++++++++++++++++++- libnymea-app-core/connection/awsclient.h | 2 + .../connection/cloudtransport.cpp | 35 ++------- libnymea-app-core/connection/cloudtransport.h | 8 +- libnymea-app-core/jsonrpc/jsonrpcclient.h | 6 +- libnymea-app-core/libnymea-app-core.pro | 6 +- nymea-remoteproxy | 1 + 8 files changed, 96 insertions(+), 38 deletions(-) create mode 160000 nymea-remoteproxy diff --git a/.gitmodules b/.gitmodules index e2919b8b..46c80a6d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "qmqtt"] path = qmqtt url = https://github.com/emqtt/qmqtt.git +[submodule "nymea-remoteproxy"] + path = nymea-remoteproxy + url = git@gitlab.guh.io:cloud/nymea-remoteproxy.git diff --git a/libnymea-app-core/connection/awsclient.cpp b/libnymea-app-core/connection/awsclient.cpp index 5c3b24c9..0d2734db 100644 --- a/libnymea-app-core/connection/awsclient.cpp +++ b/libnymea-app-core/connection/awsclient.cpp @@ -23,6 +23,7 @@ AWSClient::AWSClient(QObject *parent) : QObject(parent) settings.beginGroup("cloud"); m_username = settings.value("username").toString(); m_accessToken = settings.value("accessToken").toByteArray(); + m_accessTokenExpiry = settings.value("accessTokenExpiry").toDateTime(); m_idToken = settings.value("idToken").toByteArray(); m_refreshToken = settings.value("refreshToken").toByteArray(); @@ -30,6 +31,9 @@ AWSClient::AWSClient(QObject *parent) : QObject(parent) m_secretKey = settings.value("secretKey").toByteArray(); m_sessionToken = settings.value("sessionToken").toByteArray(); +// if (m_accessTokenExpiry < QDateTime::currentDateTime() && !m_refreshToken.isEmpty()) { + refreshAccessToken(); +// } } bool AWSClient::isLoggedIn() const @@ -90,12 +94,14 @@ void AWSClient::login(const QString &username, const QString &password) QVariantMap authenticationResult = jsonDoc.toVariant().toMap().value("AuthenticationResult").toMap(); m_accessToken = authenticationResult.value("AccessToken").toByteArray(); + m_accessTokenExpiry = QDateTime::currentDateTime().addSecs(authenticationResult.value("ExpiresIn").toInt()); m_idToken = authenticationResult.value("IdToken").toByteArray(); m_refreshToken = authenticationResult.value("RefreshToken").toByteArray(); QSettings settings; settings.beginGroup("cloud"); settings.setValue("accessToken", m_accessToken); + settings.setValue("accessTokenExpiry", m_accessTokenExpiry); settings.setValue("idToken", m_idToken); settings.setValue("refreshToken", m_refreshToken); @@ -252,7 +258,7 @@ void AWSClient::connectMQTT() void AWSClient::postToMQTT() { QString host = "a2addxakg5juii.iot.eu-west-1.amazonaws.com"; - QString topic = "850593e9-f2ab-4e89-913a-16f848d48867/eu-west-1:88c8b0f1-3f26-46cb-81f3-ccc37dcb543a/"; + QString topic = "850593e9-f2ab-4e89-913a-16f848d48867/eu-west-1:88c8b0f1-3f26-46cb-81f3-ccc37dcb543a/proxy"; // This is somehow broken in AWS... // The Signature needs to be created with having the topic percentage-encoded twice @@ -333,3 +339,68 @@ QByteArray AWSClient::accessToken() const return m_accessToken; } +void AWSClient::refreshAccessToken() +{ + QUrl url("https://cognito-idp.eu-west-1.amazonaws.com/"); + + QUrlQuery query; + query.addQueryItem("Action", "InitiateAuth"); + query.addQueryItem("Version", "2016-04-18"); + url.setQuery(query); + + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-amz-json-1.0"); + request.setRawHeader("Host", "cognito-idp.eu-west-1.amazonaws.com"); + request.setRawHeader("X-Amz-Target", "AWSCognitoIdentityProviderService.InitiateAuth"); + + QVariantMap params; + params.insert("AuthFlow", "REFRESH_TOKEN_AUTH"); +// params.insert("AuthFlow", "USER_PASSWORD_AUTH"); + params.insert("ClientId", clientId); + + QVariantMap authParams; + authParams.insert("REFRESH_TOKEN", m_refreshToken); +// authParams.insert("USERNAME", m_username); +// authParams.insert("PASSWORD", "H22*xgemmmmm"); + + params.insert("AuthParameters", authParams); + + QJsonDocument jsonDoc = QJsonDocument::fromVariant(params); + QByteArray payload = jsonDoc.toJson(QJsonDocument::Compact); + + qDebug() << "Refreshing AWS token for user:" << m_username << qUtf8Printable(payload); + + QNetworkReply *reply = m_nam->post(request, payload); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + reply->deleteLater(); + if (reply->error() != QNetworkReply::NoError) { + qWarning() << "Error logging in to aws:" << reply->error() << reply->errorString(); + return; + } + QByteArray data = reply->readAll(); + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + if (error.error != QJsonParseError::NoError) { + qWarning() << "Failed to parse AWS login response" << error.errorString(); + return; + } + +// QVariantMap authenticationResult = jsonDoc.toVariant().toMap().value("AuthenticationResult").toMap(); +// m_accessToken = authenticationResult.value("AccessToken").toByteArray(); +// m_accessTokenExpiry = QDateTime::currentDateTime().addSecs(authenticationResult.value("ExpiresIn").toInt()); +// m_idToken = authenticationResult.value("IdToken").toByteArray(); +// m_refreshToken = authenticationResult.value("RefreshToken").toByteArray(); + +// QSettings settings; +// settings.beginGroup("cloud"); +// settings.setValue("accessToken", m_accessToken); +// settings.setValue("accessTokenExpiry", m_accessTokenExpiry); +// settings.setValue("idToken", m_idToken); +// settings.setValue("refreshToken", m_refreshToken); + + qDebug() << "AWS login successful" << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented)); + emit isLoggedInChanged(); + + }); +} + diff --git a/libnymea-app-core/connection/awsclient.h b/libnymea-app-core/connection/awsclient.h index 372430bd..044e9ef4 100644 --- a/libnymea-app-core/connection/awsclient.h +++ b/libnymea-app-core/connection/awsclient.h @@ -39,6 +39,7 @@ signals: void devicesFetched(QList devices); private: + void refreshAccessToken(); void getCredentialsForIdentity(const QString &identityId); void connectMQTT(); @@ -47,6 +48,7 @@ private: QString m_username; QByteArray m_accessToken; + QDateTime m_accessTokenExpiry; QByteArray m_idToken; QByteArray m_refreshToken; diff --git a/libnymea-app-core/connection/cloudtransport.cpp b/libnymea-app-core/connection/cloudtransport.cpp index 6301fed5..6933b394 100644 --- a/libnymea-app-core/connection/cloudtransport.cpp +++ b/libnymea-app-core/connection/cloudtransport.cpp @@ -1,12 +1,15 @@ #include "cloudtransport.h" -#include "qmqtt.h" +#include "awsclient.h" +#include "remoteproxyconnection.h" + +using namespace remoteproxyclient; CloudTransport::CloudTransport(AWSClient *awsClient, QObject *parent): NymeaTransportInterface(parent), m_awsClient(awsClient) { - + m_remoteproxyConnection = new RemoteProxyConnection(QUuid::createUuid(), "nymea:app", RemoteProxyConnection::ConnectionTypeWebSocket, this); } QStringList CloudTransport::supportedSchemes() const @@ -17,34 +20,10 @@ QStringList CloudTransport::supportedSchemes() const void CloudTransport::connect(const QUrl &url) { qDebug() << "should connect to" << url; - QString date = QDateTime::currentDateTime().toString("yyyyMMddThhmmssZ"); - QString region = "eu-west-1"; - QString service = "iotdevicegateway"; - QString credentialScope = date + '/' + region + '/' + service + '/' + "aws4_request"; - QString algorithm = "AWS4-HMAC-SHA256"; - QString canonicalQuerystring = "X-Amz-Algorithm=" + algorithm; + m_awsClient->postToMQTT(); -// canonicalQuerystring += "&X-Amz-Credential=" + QByteArray(credentials.accessKeyId + '/' + credentialScope).toPercentageEncoded(); -// '&X-Amz-Security-Token=' + encodeURIComponent(credentials.sessionToken); + m_remoteproxyConnection->connectServer(QHostAddress("127.0.0.1"), 1212); - QString requestUrl = "wss://a2addxakg5juii.iot.eu-west-1.amazonaws.com/mqtt?" + canonicalQuerystring; - m_mqttClient = new QMQTT::Client(requestUrl, 443, QWebSocketProtocol::VersionLatest, true, this); - - QObject::connect(m_mqttClient, &QMQTT::Client::connected, this, [](){ - qDebug() << "MQTT connected"; - }); - QObject::connect(m_mqttClient, &QMQTT::Client::disconnected, this, []() { - qDebug() << "MQTT disconnected"; - }); - QObject::connect(m_mqttClient, &QMQTT::Client::error, this, [](QMQTT::ClientError error) { - qDebug() << "MQTT error" << error << QMQTT::ClientError::SocketHostNotFoundError; - }); - - - m_mqttClient->setUsername("michael.zanetti@guh.io"); - m_mqttClient->setPassword("H22*gemmmmm"); - m_mqttClient->setClientId("8rjhfdlf9jf1suok2jcrltd6v"); - m_mqttClient->connectToHost(); } void CloudTransport::disconnect() diff --git a/libnymea-app-core/connection/cloudtransport.h b/libnymea-app-core/connection/cloudtransport.h index a549503e..d95c3057 100644 --- a/libnymea-app-core/connection/cloudtransport.h +++ b/libnymea-app-core/connection/cloudtransport.h @@ -5,10 +5,10 @@ #include -namespace QMQTT { -class Client; -} class AWSClient; +namespace remoteproxyclient { +class RemoteProxyConnection; +} class CloudTransport : public NymeaTransportInterface { @@ -24,8 +24,8 @@ public: void sendData(const QByteArray &data) override; private: - QMQTT::Client *m_mqttClient = nullptr; AWSClient *m_awsClient = nullptr; + remoteproxyclient::RemoteProxyConnection *m_remoteproxyConnection = nullptr; }; #endif // CLOUDTRANSPORT_H diff --git a/libnymea-app-core/jsonrpc/jsonrpcclient.h b/libnymea-app-core/jsonrpc/jsonrpcclient.h index a81554c3..f41e5cb5 100644 --- a/libnymea-app-core/jsonrpc/jsonrpcclient.h +++ b/libnymea-app-core/jsonrpc/jsonrpcclient.h @@ -18,8 +18,8 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef JSONRPCCLIENT_H -#define JSONRPCCLIENT_H +#ifndef NYMEAJSONRPCCLIENT_H +#define NYMEAJSONRPCCLIENT_H #include #include @@ -157,4 +157,4 @@ private: }; -#endif // JSONRPCCLIENT_H +#endif // NYMEAJSONRPCCLIENT_H diff --git a/libnymea-app-core/libnymea-app-core.pro b/libnymea-app-core/libnymea-app-core.pro index 8c1a1df1..18fde7b5 100644 --- a/libnymea-app-core/libnymea-app-core.pro +++ b/libnymea-app-core/libnymea-app-core.pro @@ -12,8 +12,9 @@ include(../config.pri) } DEFINES += QT_STATIC include(../qmqtt/src/mqtt/mqtt.pri) -HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS \ - connection/sigv4utils.h +HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS + +include(../nymea-remoteproxy/libnymea-remoteproxyclient/libnymea-remoteproxyclient.pri) QT -= gui QT += network websockets bluetooth @@ -89,6 +90,7 @@ HEADERS += \ connection/tcpsockettransport.h \ connection/bluetoothtransport.h \ connection/awsclient.h \ + connection/sigv4utils.h \ devicemanager.h \ jsonrpc/jsontypes.h \ jsonrpc/jsonrpcclient.h \ diff --git a/nymea-remoteproxy b/nymea-remoteproxy new file mode 160000 index 00000000..c2c3304a --- /dev/null +++ b/nymea-remoteproxy @@ -0,0 +1 @@ +Subproject commit c2c3304a558a434115c49e168155da8eba81cd98