From 9edc4395d6887d4b6768e2d8d641e0bc2476d04c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 6 Jun 2016 11:00:16 +0200 Subject: [PATCH] remove websocket compiler flag add cloud connection basic structure --- guh.pri | 7 +- guh.pro | 7 - libguh/loggingcategories.cpp | 1 + libguh/loggingcategories.h | 2 + server/cloud/cloudauthenticator.cpp | 270 ++++++++++++++++++ server/cloud/cloudauthenticator.h | 103 +++++++ server/cloud/cloudconnection.cpp | 157 ++++++++++ server/cloud/cloudconnection.h | 92 ++++++ server/guhcore.cpp | 8 + server/guhcore.h | 3 + server/jsonrpc/actionhandler.cpp | 3 - server/jsonrpc/cloudhandler.cpp | 105 +++++++ server/jsonrpc/cloudhandler.h | 53 ++++ server/jsonrpc/jsonrpcserver.cpp | 13 +- server/jsonrpc/jsonrpcserver.h | 5 - server/jsonrpc/jsontypes.cpp | 10 +- server/jsonrpc/jsontypes.h | 4 +- server/main.cpp | 1 + server/server.pri | 21 +- server/server.pro | 2 +- server/transportinterface.cpp | 1 - .../websocketserver/testwebsocketserver.cpp | 8 - .../auto/websocketserver/websocketserver.pro | 4 +- 23 files changed, 820 insertions(+), 60 deletions(-) create mode 100644 server/cloud/cloudauthenticator.cpp create mode 100644 server/cloud/cloudauthenticator.h create mode 100644 server/cloud/cloudconnection.cpp create mode 100644 server/cloud/cloudconnection.h create mode 100644 server/jsonrpc/cloudhandler.cpp create mode 100644 server/jsonrpc/cloudhandler.h diff --git a/guh.pri b/guh.pri index 6d40200d..f91d6d3c 100644 --- a/guh.pri +++ b/guh.pri @@ -9,7 +9,7 @@ DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" \ JSON_PROTOCOL_VERSION=\\\"$${JSON_PROTOCOL_VERSION}\\\" \ REST_API_VERSION=\\\"$${REST_API_VERSION}\\\" -QT+= network +QT+= network websockets QMAKE_CXXFLAGS += -Werror -std=c++11 -g QMAKE_LFLAGS += -std=c++11 @@ -73,14 +73,9 @@ coverage { QMAKE_CLEAN += *.gcda *.gcno coverage.info coverage.xml } - # Enable Radio 433 MHz for GPIO's enable433gpio { DEFINES += GPIO433 } -# check websocket support (Qt >= 5.3) -equals(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 2) { - DEFINES += WEBSOCKET -} diff --git a/guh.pro b/guh.pro index 673b296f..2a9c19d0 100644 --- a/guh.pro +++ b/guh.pro @@ -60,13 +60,6 @@ contains(DEFINES, BLUETOOTH_LE) { message("Bluetooth LE disabled (Qt $${QT_VERSION} < 5.4.0).") } -# Websocket support -contains(DEFINES, WEBSOCKET){ - message("Building guh with websocket.") -} else { - message("Building guh without websocket.") -} - # GPIO RF 433 MHz support contains(DEFINES, GPIO433){ message("Radio 433 for GPIO's enabled") diff --git a/libguh/loggingcategories.cpp b/libguh/loggingcategories.cpp index 8ba77a66..5d3fae65 100644 --- a/libguh/loggingcategories.cpp +++ b/libguh/loggingcategories.cpp @@ -34,3 +34,4 @@ Q_LOGGING_CATEGORY(dcJsonRpc, "JsonRpc") Q_LOGGING_CATEGORY(dcRest, "Rest") Q_LOGGING_CATEGORY(dcOAuth2, "OAuth2") Q_LOGGING_CATEGORY(dcAvahi, "Avahi") +Q_LOGGING_CATEGORY(dcCloud, "Cloud") diff --git a/libguh/loggingcategories.h b/libguh/loggingcategories.h index b75d0051..b940d1c9 100644 --- a/libguh/loggingcategories.h +++ b/libguh/loggingcategories.h @@ -22,6 +22,7 @@ #define LOGGINGCATEGORYS_H #include +#include // Include dcCoap #include "coap/coap.h" @@ -41,5 +42,6 @@ Q_DECLARE_LOGGING_CATEGORY(dcJsonRpc) Q_DECLARE_LOGGING_CATEGORY(dcRest) Q_DECLARE_LOGGING_CATEGORY(dcOAuth2) Q_DECLARE_LOGGING_CATEGORY(dcAvahi) +Q_DECLARE_LOGGING_CATEGORY(dcCloud) #endif // LOGGINGCATEGORYS_H diff --git a/server/cloud/cloudauthenticator.cpp b/server/cloud/cloudauthenticator.cpp new file mode 100644 index 00000000..93b4634c --- /dev/null +++ b/server/cloud/cloudauthenticator.cpp @@ -0,0 +1,270 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of guh. * + * * + * Guh 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 2 of the License. * + * * + * Guh is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with guh. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "cloudauthenticator.h" + +#include +#include + +namespace guhserver { + +CloudAuthenticator::CloudAuthenticator(QString clientId, QString clientSecret, QObject *parent) : + QObject(parent), + m_clientId(clientId), + m_clientSecret(clientSecret), + m_authenticated(false) +{ + m_networkManager = new QNetworkAccessManager(this); + connect(m_networkManager, &QNetworkAccessManager::finished, this, &CloudAuthenticator::replyFinished); + + m_timer = new QTimer(this); + m_timer->setSingleShot(false); + + connect(m_timer, &QTimer::timeout, this, &CloudAuthenticator::refreshTimeout); +} + +QUrl CloudAuthenticator::url() const +{ + return m_url; +} + +void CloudAuthenticator::setUrl(const QUrl &url) +{ + m_url = url; +} + +QUrlQuery CloudAuthenticator::query() const +{ + return m_query; +} + +void CloudAuthenticator::setQuery(const QUrlQuery &query) +{ + m_query = query; +} + +QString CloudAuthenticator::username() const +{ + return m_username; +} + +void CloudAuthenticator::setUsername(const QString &username) +{ + m_username = username; +} + +QString CloudAuthenticator::password() const +{ + return m_password; +} + +void CloudAuthenticator::setPassword(const QString &password) +{ + m_password = password; +} + +QString CloudAuthenticator::clientId() const +{ + return m_clientId; +} + +void CloudAuthenticator::setClientId(const QString &clientId) +{ + m_clientId = clientId; +} + +QString CloudAuthenticator::clientSecret() const +{ + return m_clientSecret; +} + +void CloudAuthenticator::setClientSecret(const QString clientSecret) +{ + m_clientSecret = clientSecret; +} + +QString CloudAuthenticator::scope() const +{ + return m_scope; +} + +void CloudAuthenticator::setScope(const QString &scope) +{ + m_scope = scope; +} + +QString CloudAuthenticator::token() const +{ + return m_token; +} + +bool CloudAuthenticator::authenticated() const +{ + return m_authenticated; +} + +void CloudAuthenticator::startAuthentication() +{ + qCDebug(dcCloud()) << "Start authentication" << m_username; + + QUrlQuery query; + query.addQueryItem("grant_type", "password"); + query.addQueryItem("username", m_username); + query.addQueryItem("password", m_password); + setQuery(query); + + QNetworkRequest request(m_url); + QByteArray data = QString(m_clientId + ":" + m_clientSecret).toUtf8().toBase64(); + QString header = "Basic " + data; + request.setRawHeader("Authorization", header.toLocal8Bit()); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + + m_tokenRequests.append(m_networkManager->post(request, m_query.toString().toUtf8())); +} + +void CloudAuthenticator::setAuthenticated(const bool &authenticated) +{ + if (!authenticated) { + m_timer->stop(); + qCWarning(dcCloud()) << "Authentication failed" << m_username; + } + m_authenticated = authenticated; + emit authenticationChanged(); +} + +void CloudAuthenticator::setToken(const QString &token) +{ + m_token = token; + emit tokenChanged(); +} + +void CloudAuthenticator::replyFinished(QNetworkReply *reply) +{ + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + // token request + if (m_tokenRequests.contains(reply)) { + + QByteArray data = reply->readAll(); + m_tokenRequests.removeAll(reply); + + // check HTTP status code + if (status != 200) { + qCWarning(dcCloud()) << "Request token reply HTTP error:" << status << reply->errorString(); + qCWarning(dcCloud()) << data; + setAuthenticated(false); + reply->deleteLater(); + return; + } + + // check JSON + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcCloud()) << "Request token reply JSON error:" << error.errorString(); + setAuthenticated(false); + reply->deleteLater(); + return; + } + + if (!jsonDoc.toVariant().toMap().contains("access_token")) { + qCWarning(dcCloud()) << "Could not get access token" << jsonDoc.toJson(); + setAuthenticated(false); + reply->deleteLater(); + return; + } + + setToken(jsonDoc.toVariant().toMap().value("access_token").toString()); + setAuthenticated(true); + + if (jsonDoc.toVariant().toMap().contains("expires_in") && jsonDoc.toVariant().toMap().contains("refresh_token")) { + int expireTime = jsonDoc.toVariant().toMap().value("expires_in").toInt(); + m_refreshToken = jsonDoc.toVariant().toMap().value("refresh_token").toString(); + qCDebug(dcCloud()) << "Token will be refreshed in" << expireTime << "[s]"; + m_timer->start((expireTime - 20) * 1000); + } + + } else if (m_refreshTokenRequests.contains(reply)) { + + QByteArray data = reply->readAll(); + m_refreshTokenRequests.removeAll(reply); + + // check HTTP status code + if (status != 200) { + qCWarning(dcCloud()) << "Refresh token reply HTTP error:" << status << reply->errorString(); + qCWarning(dcCloud()) << data; + setAuthenticated(false); + reply->deleteLater(); + return; + } + + // check JSON + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcCloud()) << "Refresh token reply JSON error:" << error.errorString(); + setAuthenticated(false); + reply->deleteLater(); + return; + } + + if (!jsonDoc.toVariant().toMap().contains("access_token")) { + qCWarning(dcCloud()) << "Could not get access token after refresh" << jsonDoc.toJson(); + setAuthenticated(false); + reply->deleteLater(); + return; + } + + setToken(jsonDoc.toVariant().toMap().value("access_token").toString()); + qCDebug(dcCloud()) << "Token refreshed successfully"; + + if (jsonDoc.toVariant().toMap().contains("expires_in") && jsonDoc.toVariant().toMap().contains("refresh_token")) { + int expireTime = jsonDoc.toVariant().toMap().value("expires_in").toInt(); + m_refreshToken = jsonDoc.toVariant().toMap().value("refresh_token").toString(); + qCDebug(dcCloud()) << "Token will be refreshed in" << expireTime << "[s]"; + m_timer->start((expireTime - 20) * 1000); + } + + if (!authenticated()) + setAuthenticated(true); + } + + reply->deleteLater(); +} + +void CloudAuthenticator::refreshTimeout() +{ + qCDebug(dcCloud()) << "Refresh authentication token for" << m_username; + + QUrlQuery query; + query.addQueryItem("grant_type", "refresh_token"); + query.addQueryItem("refresh_token", m_refreshToken); + query.addQueryItem("client_id", m_clientId); + query.addQueryItem("client_secret", m_clientSecret); + + QNetworkRequest request(m_url); + QByteArray data = QString(m_clientId + ":" + m_clientSecret).toUtf8().toBase64(); + QString header = "Basic " + data; + request.setRawHeader("Authorization", header.toLocal8Bit()); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + m_refreshTokenRequests.append(m_networkManager->post(request, query.toString().toUtf8())); +} + +} diff --git a/server/cloud/cloudauthenticator.h b/server/cloud/cloudauthenticator.h new file mode 100644 index 00000000..279a9920 --- /dev/null +++ b/server/cloud/cloudauthenticator.h @@ -0,0 +1,103 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of guh. * + * * + * Guh 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 2 of the License. * + * * + * Guh is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with guh. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef CLOUDAUTHENTICATOR_H +#define CLOUDAUTHENTICATOR_H + +#include +#include +#include +#include +#include +#include +#include + +#include "loggingcategories.h" + +namespace guhserver { + +class CloudAuthenticator : public QObject +{ + Q_OBJECT +public: + explicit CloudAuthenticator(QString clientId, QString clientSecret, QObject *parent = 0); + + QUrl url() const; + void setUrl(const QUrl &url); + + QUrlQuery query() const; + void setQuery(const QUrlQuery &query); + + QString username() const; + void setUsername(const QString &username); + + QString password() const; + void setPassword(const QString &password); + + QString clientId() const; + void setClientId(const QString &clientId); + + QString clientSecret() const; + void setClientSecret(const QString clientSecret); + + QString scope() const; + void setScope(const QString &scope); + + QString token() const; + + bool authenticated() const; + + void startAuthentication(); + +private: + QNetworkAccessManager *m_networkManager; + QTimer *m_timer; + QList m_tokenRequests; + QList m_refreshTokenRequests; + + QUrl m_url; + QUrlQuery m_query; + QString m_username; + QString m_password; + QString m_clientId; + QString m_clientSecret; + QString m_scope; + + QString m_token; + QString m_refreshToken; + + bool m_authenticated; + + void setAuthenticated(const bool &authenticated); + void setToken(const QString &token); + +private slots: + void replyFinished(QNetworkReply *reply); + void refreshTimeout(); + +signals: + void authenticationChanged(); + void tokenChanged(); + +}; + +} + +#endif // CLOUDAUTHENTICATOR_H diff --git a/server/cloud/cloudconnection.cpp b/server/cloud/cloudconnection.cpp new file mode 100644 index 00000000..02752948 --- /dev/null +++ b/server/cloud/cloudconnection.cpp @@ -0,0 +1,157 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of guh. * + * * + * Guh 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 2 of the License. * + * * + * Guh is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with guh. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "cloudconnection.h" + +#include "loggingcategories.h" +#include "guhsettings.h" + +namespace guhserver { + +CloudConnection::CloudConnection(QObject *parent) : + QObject(parent), + m_error(CloudConnectionErrorNoError), + m_enabled(false), + m_connected(false), + m_active(false), + m_authenticated(false) +{ + m_proxyUrl = QUrl("ws://127.0.0.1:1212"); + m_keystoneUrl = QUrl("http://localhost:8000/oauth2/token"); + +// GuhSettings settings(GuhSettings::SettingsRoleGlobal); +// settings.beginGroup("CloudConnection"); + + m_connection = new QWebSocket("guhd", QWebSocketProtocol::Version13, this); + connect(m_connection, SIGNAL(connected()), this, SLOT(onConnected())); + connect(m_connection, SIGNAL(disconnected()), this, SLOT(onDisconnected())); + connect(m_connection, SIGNAL(textMessageReceived(QString)), this, SLOT(onTextMessageReceived(QString))); + connect(m_connection, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError))); + + m_authenticator = new CloudAuthenticator("0631d42ba0464e4ebd4b78b15c53f532", "b7919ebf3bcf48239f348e764744079b", this); + m_authenticator->setUrl(m_keystoneUrl); + + connect(m_authenticator, &CloudAuthenticator::authenticationChanged, this, &CloudConnection::onAuthenticationChanged); +} + +void CloudConnection::connectToCloud(const QString &username, const QString &password) +{ + m_authenticator->setUsername(username); + m_authenticator->setPassword(password); + m_authenticator->startAuthentication(); +} + +CloudConnection::CloudConnectionError CloudConnection::error() const +{ + return m_error; +} + +void CloudConnection::enable() +{ + m_enabled = true; +} + +void CloudConnection::disable() +{ + m_enabled = false; +} + +bool CloudConnection::enabled() const +{ + return m_enabled; +} + +bool CloudConnection::connected() const +{ + return m_connected; +} + +bool CloudConnection::active() const +{ + return m_active; +} + +bool CloudConnection::authenticated() const +{ + return m_authenticated; +} + +void CloudConnection::setEnabled(const bool &enabled) +{ + m_enabled = enabled; + emit enabledChanged(); +} + +void CloudConnection::setConnected(const bool &connected) +{ + m_connected = connected; + emit connectedChanged(); +} + +void CloudConnection::setActive(const bool &active) +{ + m_active = active; + emit activeChanged(); +} + +void CloudConnection::setAuthenticated(const bool &authenticated) +{ + m_authenticated = authenticated; + emit authenticatedChanged(); +} + +void CloudConnection::onAuthenticationChanged() +{ + qCDebug(dcCloud()) << "Authentication changed" << m_authenticator->authenticated(); + setAuthenticated(m_authenticator->authenticated()); + + if (m_authenticated) { + qCDebug(dcCloud()) << "Connecting to" << m_proxyUrl.toString(); + m_connection->open(m_proxyUrl); + } else { + m_error = CloudConnectionErrorAuthenticationFailed; + } +} + +void CloudConnection::onConnected() +{ + qCDebug(dcCloud()) << "Connected to cloud proxy server" << m_proxyUrl.toString(); + setConnected(true); + + // TODO: authenticate cloud connection +} + +void CloudConnection::onDisconnected() +{ + qCDebug(dcCloud()) << "Disconnected from cloud connection:" << m_connection->closeReason(); + setConnected(false); +} + +void CloudConnection::onError(const QAbstractSocket::SocketError &error) +{ + qCWarning(dcCloud()) << "Websocket error:" << error << m_connection->errorString(); +} + +void CloudConnection::onTextMessageReceived(const QString &message) +{ + qCDebug(dcCloud()) << "Cloud message received" << message; +} + +} diff --git a/server/cloud/cloudconnection.h b/server/cloud/cloudconnection.h new file mode 100644 index 00000000..a21c88ce --- /dev/null +++ b/server/cloud/cloudconnection.h @@ -0,0 +1,92 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of guh. * + * * + * Guh 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 2 of the License. * + * * + * Guh is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with guh. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef CLOUDCONNECTION_H +#define CLOUDCONNECTION_H + +#include +#include +#include + +#include "cloudauthenticator.h" + +namespace guhserver { + +class CloudConnection : public QObject +{ + Q_OBJECT + Q_ENUMS(CloudConnectionError) + +public: + enum CloudConnectionError { + CloudConnectionErrorNoError, + CloudConnectionErrorAuthenticationFailed, + CloudConnectionErrorCloudServerNotReachable + }; + + explicit CloudConnection(QObject *parent = 0); + + void connectToCloud(const QString &username, const QString &password); + + CloudConnectionError error() const; + + void enable(); + void disable(); + + bool enabled() const; + bool connected() const; + bool active() const; + bool authenticated() const; + +private: + QWebSocket *m_connection; + CloudAuthenticator *m_authenticator; + CloudConnectionError m_error; + + QUrl m_proxyUrl; + QUrl m_keystoneUrl; + + bool m_enabled; + bool m_connected; + bool m_active; + bool m_authenticated; + + void setEnabled(const bool &enabled); + void setConnected(const bool &connected); + void setActive(const bool &active); + void setAuthenticated(const bool &authenticated); + +signals: + void enabledChanged(); + void connectedChanged(); + void activeChanged(); + void authenticatedChanged(); + +private slots: + void onAuthenticationChanged(); + void onConnected(); + void onDisconnected(); + void onError(const QAbstractSocket::SocketError &error); + void onTextMessageReceived(const QString &message); + +}; + +} +#endif // CLOUDCONNECTION_H diff --git a/server/guhcore.cpp b/server/guhcore.cpp index 220ff476..2404f7d6 100644 --- a/server/guhcore.cpp +++ b/server/guhcore.cpp @@ -362,6 +362,11 @@ TimeManager *GuhCore::timeManager() const return m_timeManager; } +CloudConnection *GuhCore::cloudConnection() const +{ + return m_cloudConnection; +} + /*! Constructs GuhCore with the given \a parent. This is private. Use \l{GuhCore::instance()} to access the single instance.*/ GuhCore::GuhCore(QObject *parent) : @@ -373,6 +378,9 @@ GuhCore::GuhCore(QObject *parent) : qCDebug(dcApplication) << "Creating Log Engine"; m_logger = new LogEngine(this); + qCDebug(dcApplication) << "Creating Cloud Connection"; + m_cloudConnection = new CloudConnection(this); + qCDebug(dcApplication) << "Creating Device Manager"; m_deviceManager = new DeviceManager(this); diff --git a/server/guhcore.h b/server/guhcore.h index a8772189..acfa394a 100644 --- a/server/guhcore.h +++ b/server/guhcore.h @@ -32,6 +32,7 @@ #include "ruleengine.h" #include "servermanager.h" +#include "cloud/cloudconnection.h" #include "time/timemanager.h" #include @@ -70,6 +71,7 @@ public: DeviceManager *deviceManager() const; RuleEngine *ruleEngine() const; TimeManager *timeManager() const; + CloudConnection *cloudConnection() const; signals: void eventTriggered(const Event &event); @@ -98,6 +100,7 @@ private: RuleEngine *m_ruleEngine; LogEngine *m_logger; TimeManager *m_timeManager; + CloudConnection *m_cloudConnection; QHash m_pendingActions; diff --git a/server/jsonrpc/actionhandler.cpp b/server/jsonrpc/actionhandler.cpp index c11bf37c..9f0c5578 100644 --- a/server/jsonrpc/actionhandler.cpp +++ b/server/jsonrpc/actionhandler.cpp @@ -39,8 +39,6 @@ #include "types/action.h" #include "loggingcategories.h" -#include - namespace guhserver { /*! Constructs a new \l ActionHandler with the given \a parent. */ @@ -75,7 +73,6 @@ QString ActionHandler::name() const JsonReply* ActionHandler::ExecuteAction(const QVariantMap ¶ms) { - DeviceId deviceId(params.value("deviceId").toString()); ActionTypeId actionTypeId(params.value("actionTypeId").toString()); ParamList actionParams = JsonTypes::unpackParams(params.value("params").toList()); diff --git a/server/jsonrpc/cloudhandler.cpp b/server/jsonrpc/cloudhandler.cpp new file mode 100644 index 00000000..3c37c4df --- /dev/null +++ b/server/jsonrpc/cloudhandler.cpp @@ -0,0 +1,105 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of guh. * + * * + * Guh 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 2 of the License. * + * * + * Guh is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with guh. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "cloudhandler.h" + +namespace guhserver { + +CloudHandler::CloudHandler(QObject *parent) : + JsonHandler(parent) +{ + QVariantMap returns; + QVariantMap params; + + setDescription("Authenticate", "Connect and authenticate the cloud connection with the given username and password."); + params.insert("username", JsonTypes::basicTypeToString(JsonTypes::String)); + params.insert("password", JsonTypes::basicTypeToString(JsonTypes::String)); + setParams("Authenticate", params); + returns.insert("cloudConnectionError", JsonTypes::cloudConnectionErrorRef()); + setReturns("Authenticate", returns); + + params.clear(); returns.clear(); + setDescription("GetConnectionStatus", "Get the current status of the cloud connection."); + setParams("GetConnectionStatus", params); + returns.insert("enabled", JsonTypes::basicTypeToString(JsonTypes::Bool)); + returns.insert("connected", JsonTypes::basicTypeToString(JsonTypes::Bool)); + returns.insert("active", JsonTypes::basicTypeToString(JsonTypes::Bool)); + returns.insert("authenticated", JsonTypes::basicTypeToString(JsonTypes::Bool)); + setReturns("GetConnectionStatus", returns); + + // Notification + params.clear(); returns.clear(); + setDescription("ConnectionStatusChanged", "Emitted whenever the status of the cloud connection changed."); + params.insert("enabled", JsonTypes::basicTypeToString(JsonTypes::Bool)); + params.insert("connected", JsonTypes::basicTypeToString(JsonTypes::Bool)); + params.insert("active", JsonTypes::basicTypeToString(JsonTypes::Bool)); + params.insert("authenticated", JsonTypes::basicTypeToString(JsonTypes::Bool)); + setParams("ConnectionStatusChanged", params); + + connect(GuhCore::instance()->cloudConnection(), SIGNAL(enabledChanged()), this, SLOT(onConnectionStatusChanged())); + connect(GuhCore::instance()->cloudConnection(), SIGNAL(connectedChanged()), this, SLOT(onConnectionStatusChanged())); + connect(GuhCore::instance()->cloudConnection(), SIGNAL(activeChanged()), this, SLOT(onConnectionStatusChanged())); + connect(GuhCore::instance()->cloudConnection(), SIGNAL(authenticatedChanged()), this, SLOT(onConnectionStatusChanged())); +} + +QString CloudHandler::name() const +{ + return "Cloud"; +} + +JsonReply *CloudHandler::Authenticate(const QVariantMap ¶ms) const +{ + Q_UNUSED(params) + + QString username = params.value("username").toString(); + QString password = params.value("password").toString(); + qCDebug(dcJsonRpc()) << "Authenticate cloud connection for user" << username; + + GuhCore::instance()->cloudConnection()->connectToCloud(username, password); + + QVariantMap returns; + returns.insert("cloudConnectionError", JsonTypes::cloudConnectionErrorToString(CloudConnection::CloudConnectionErrorNoError)); + return createReply(returns); +} + +JsonReply *CloudHandler::GetConnectionStatus(const QVariantMap ¶ms) const +{ + Q_UNUSED(params) + + QVariantMap returns; + returns.insert("enabled", GuhCore::instance()->cloudConnection()->enabled()); + returns.insert("connected", GuhCore::instance()->cloudConnection()->connected()); + returns.insert("active", GuhCore::instance()->cloudConnection()->active()); + returns.insert("authenticated", GuhCore::instance()->cloudConnection()->authenticated()); + return createReply(returns); +} + +void CloudHandler::onConnectionStatusChanged() +{ + QVariantMap params; + params.insert("enabled", GuhCore::instance()->cloudConnection()->enabled()); + params.insert("connected", GuhCore::instance()->cloudConnection()->connected()); + params.insert("active", GuhCore::instance()->cloudConnection()->active()); + params.insert("authenticated", GuhCore::instance()->cloudConnection()->authenticated()); + + emit ConnectionStatusChanged(params); +} + +} diff --git a/server/jsonrpc/cloudhandler.h b/server/jsonrpc/cloudhandler.h new file mode 100644 index 00000000..280de747 --- /dev/null +++ b/server/jsonrpc/cloudhandler.h @@ -0,0 +1,53 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of guh. * + * * + * Guh 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 2 of the License. * + * * + * Guh is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with guh. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef CLOUDHANDLER_H +#define CLOUDHANDLER_H + +#include + +#include "guhcore.h" +#include "jsonhandler.h" +#include "loggingcategories.h" + +namespace guhserver { + +class CloudHandler : public JsonHandler +{ + Q_OBJECT +public: + CloudHandler(QObject *parent = 0); + + QString name() const; + + Q_INVOKABLE JsonReply* Authenticate(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* GetConnectionStatus(const QVariantMap ¶ms) const; + +signals: + void ConnectionStatusChanged(const QVariantMap ¶ms); + +private slots: + void onConnectionStatusChanged(); + +}; + +} + +#endif // CLOUDHANDLER_H diff --git a/server/jsonrpc/jsonrpcserver.cpp b/server/jsonrpc/jsonrpcserver.cpp index b2aa52a6..8ce7e7dc 100644 --- a/server/jsonrpc/jsonrpcserver.cpp +++ b/server/jsonrpc/jsonrpcserver.cpp @@ -54,6 +54,8 @@ #include "eventhandler.h" #include "logginghandler.h" #include "statehandler.h" +#include "websocketserver.h" +#include "cloudhandler.h" #ifndef TESTING_ENABLED #include "tcpserver.h" @@ -61,10 +63,6 @@ #include "mocktcpserver.h" #endif -#ifdef WEBSOCKET -#include "websocketserver.h" -#endif - #include #include #include @@ -79,9 +77,7 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject #else m_tcpServer(new TcpServer(this)), #endif - #ifdef WEBSOCKET m_websocketServer(new WebSocketServer(sslConfiguration, this)), - #endif m_notificationId(0) { // First, define our own JSONRPC methods @@ -117,16 +113,12 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject m_interfaces.append(m_tcpServer); -#ifdef WEBSOCKET connect(m_websocketServer, SIGNAL(clientConnected(const QUuid &)), this, SLOT(clientConnected(const QUuid &))); connect(m_websocketServer, SIGNAL(clientDisconnected(const QUuid &)), this, SLOT(clientDisconnected(const QUuid &))); connect(m_websocketServer, SIGNAL(dataAvailable(QUuid, QString, QString, QVariantMap)), this, SLOT(processData(QUuid, QString, QString, QVariantMap))); m_websocketServer->startServer(); m_interfaces.append(m_websocketServer); -#else - Q_UNUSED(sslConfiguration) -#endif QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); } @@ -192,6 +184,7 @@ void JsonRPCServer::setup() registerHandler(new EventHandler(this)); registerHandler(new LoggingHandler(this)); registerHandler(new StateHandler(this)); + registerHandler(new CloudHandler(this)); } void JsonRPCServer::processData(const QUuid &clientId, const QString &targetNamespace, const QString &method, const QVariantMap &message) diff --git a/server/jsonrpc/jsonrpcserver.h b/server/jsonrpc/jsonrpcserver.h index c1d4babe..71461126 100644 --- a/server/jsonrpc/jsonrpcserver.h +++ b/server/jsonrpc/jsonrpcserver.h @@ -43,9 +43,7 @@ class MockTcpServer; namespace guhserver { -#ifdef WEBSOCKET class WebSocketServer; -#endif #ifndef TESTING_ENABLED class TcpServer; @@ -84,10 +82,7 @@ private: TcpServer *m_tcpServer; #endif -#ifdef WEBSOCKET WebSocketServer *m_websocketServer; -#endif - QList m_interfaces; QHash m_handlers; QHash m_asyncReplies; diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp index 3379be0e..a6de453f 100644 --- a/server/jsonrpc/jsontypes.cpp +++ b/server/jsonrpc/jsontypes.cpp @@ -82,6 +82,7 @@ QVariantList JsonTypes::s_loggingSource; QVariantList JsonTypes::s_loggingLevel; QVariantList JsonTypes::s_loggingEventType; QVariantList JsonTypes::s_repeatingMode; +QVariantList JsonTypes::s_cloudConnectionError; QVariantMap JsonTypes::s_paramType; QVariantMap JsonTypes::s_param; @@ -110,7 +111,6 @@ QVariantMap JsonTypes::s_calendarItem; QVariantMap JsonTypes::s_timeEventItem; QVariantMap JsonTypes::s_repeatingOption; - void JsonTypes::init() { // BasicTypes @@ -131,6 +131,7 @@ void JsonTypes::init() s_loggingLevel = enumToStrings(Logging::staticMetaObject, "LoggingLevel"); s_loggingEventType = enumToStrings(Logging::staticMetaObject, "LoggingEventType"); s_repeatingMode = enumToStrings(RepeatingOption::staticMetaObject, "RepeatingMode"); + s_cloudConnectionError = enumToStrings(CloudConnection::staticMetaObject, "CloudConnectionError"); // ParamType s_paramType.insert("name", basicTypeToString(String)); @@ -359,6 +360,7 @@ QVariantMap JsonTypes::allTypes() allTypes.insert("LoggingSource", loggingSource()); allTypes.insert("LoggingEventType", loggingEventType()); allTypes.insert("RepeatingMode", repeatingMode()); + allTypes.insert("CloudConnectionError", cloudConnectionError()); allTypes.insert("StateType", stateTypeDescription()); allTypes.insert("StateDescriptor", stateDescriptorDescription()); @@ -1748,10 +1750,10 @@ QPair JsonTypes::validateVariant(const QVariant &templateVariant, qCWarning(dcJsonRpc) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(repeatingModeRef()); return result; } - } else if (refName == removePolicyRef()) { - QPair result = validateEnum(s_removePolicy, variant); + } else if (refName == cloudConnectionErrorRef()) { + QPair result = validateEnum(s_cloudConnectionError, variant); if (!result.first) { - qCWarning(dcJsonRpc) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(removePolicyRef()); + qCWarning(dcJsonRpc) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(cloudConnectionErrorRef()); return result; } } else { diff --git a/server/jsonrpc/jsontypes.h b/server/jsonrpc/jsontypes.h index 9b4c3954..ff1ce97c 100644 --- a/server/jsonrpc/jsontypes.h +++ b/server/jsonrpc/jsontypes.h @@ -44,6 +44,8 @@ #include "time/timedescriptor.h" #include "time/timeeventitem.h" +#include "cloud/cloudconnection.h" + #include #include @@ -122,6 +124,7 @@ public: DECLARE_TYPE(loggingLevel, "LoggingLevel", Logging, LoggingLevel) DECLARE_TYPE(loggingEventType, "LoggingEventType", Logging, LoggingEventType) DECLARE_TYPE(repeatingMode, "RepeatingMode", RepeatingOption, RepeatingMode) + DECLARE_TYPE(cloudConnectionError, "CloudConnectionError", CloudConnection, CloudConnectionError) DECLARE_OBJECT(paramType, "ParamType") DECLARE_OBJECT(param, "Param") @@ -178,7 +181,6 @@ public: static QVariantMap packTimeEventItem(const TimeEventItem &timeEventItem); static QVariantMap packTimeDescriptor(const TimeDescriptor &timeDescriptor); - // pack resources static QVariantList packRules(const QList rules); static QVariantList packCreateMethods(DeviceClass::CreateMethods createMethods); diff --git a/server/main.cpp b/server/main.cpp index b6179869..cdbcbd60 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -120,6 +120,7 @@ int main(int argc, char *argv[]) s_loggingFilters.insert("TimeManager", false); s_loggingFilters.insert("Coap", false); s_loggingFilters.insert("Avahi", false); + s_loggingFilters.insert("Cloud", true); QHash loggingFiltersPlugins; foreach (const QJsonObject &pluginMetadata, DeviceManager::pluginsMetadata()) { diff --git a/server/server.pri b/server/server.pri index adbdad5e..4a55c8dc 100644 --- a/server/server.pri +++ b/server/server.pri @@ -1,15 +1,6 @@ -# check websocket support -contains(DEFINES, WEBSOCKET){ - QT += websockets - HEADERS += $$top_srcdir/server/websocketserver.h - SOURCES += $$top_srcdir/server/websocketserver.cpp -} - - # icons for the webserver RESOURCES += $$top_srcdir/icons.qrc - HEADERS += $$top_srcdir/server/guhcore.h \ $$top_srcdir/server/tcpserver.h \ $$top_srcdir/server/ruleengine.h \ @@ -23,6 +14,7 @@ HEADERS += $$top_srcdir/server/guhcore.h \ $$top_srcdir/server/jsonrpc/eventhandler.h \ $$top_srcdir/server/jsonrpc/statehandler.h \ $$top_srcdir/server/jsonrpc/logginghandler.h \ + $$top_srcdir/server/jsonrpc/cloudhandler.h \ $$top_srcdir/server/stateevaluator.h \ $$top_srcdir/server/logging/logging.h \ $$top_srcdir/server/logging/logengine.h \ @@ -32,6 +24,7 @@ HEADERS += $$top_srcdir/server/guhcore.h \ $$top_srcdir/server/transportinterface.h \ $$top_srcdir/server/servermanager.h \ $$top_srcdir/server/httprequest.h \ + $$top_srcdir/server/websocketserver.h \ $$top_srcdir/server/httpreply.h \ $$top_srcdir/server/rest/restserver.h \ $$top_srcdir/server/rest/restresource.h \ @@ -45,7 +38,9 @@ HEADERS += $$top_srcdir/server/guhcore.h \ $$top_srcdir/server/time/calendaritem.h \ $$top_srcdir/server/time/repeatingoption.h \ $$top_srcdir/server/time/timeeventitem.h \ - $$top_srcdir/server/time/timemanager.h + $$top_srcdir/server/time/timemanager.h \ + $$top_srcdir/server/cloud/cloudconnection.h \ + $$top_srcdir/server/cloud/cloudauthenticator.h \ SOURCES += $$top_srcdir/server/guhcore.cpp \ @@ -61,6 +56,7 @@ SOURCES += $$top_srcdir/server/guhcore.cpp \ $$top_srcdir/server/jsonrpc/eventhandler.cpp \ $$top_srcdir/server/jsonrpc/statehandler.cpp \ $$top_srcdir/server/jsonrpc/logginghandler.cpp \ + $$top_srcdir/server/jsonrpc/cloudhandler.cpp \ $$top_srcdir/server/stateevaluator.cpp \ $$top_srcdir/server/logging/logengine.cpp \ $$top_srcdir/server/logging/logfilter.cpp \ @@ -69,6 +65,7 @@ SOURCES += $$top_srcdir/server/guhcore.cpp \ $$top_srcdir/server/transportinterface.cpp \ $$top_srcdir/server/servermanager.cpp \ $$top_srcdir/server/httprequest.cpp \ + $$top_srcdir/server/websocketserver.cpp \ $$top_srcdir/server/httpreply.cpp \ $$top_srcdir/server/rest/restserver.cpp \ $$top_srcdir/server/rest/restresource.cpp \ @@ -82,4 +79,6 @@ SOURCES += $$top_srcdir/server/guhcore.cpp \ $$top_srcdir/server/time/calendaritem.cpp \ $$top_srcdir/server/time/repeatingoption.cpp \ $$top_srcdir/server/time/timeeventitem.cpp \ - $$top_srcdir/server/time/timemanager.cpp + $$top_srcdir/server/time/timemanager.cpp \ + $$top_srcdir/server/cloud/cloudconnection.cpp \ + $$top_srcdir/server/cloud/cloudauthenticator.cpp \ diff --git a/server/server.pro b/server/server.pro index 39ae6c04..1cb79dfe 100644 --- a/server/server.pro +++ b/server/server.pro @@ -8,7 +8,7 @@ INCLUDEPATH += ../libguh jsonrpc target.path = /usr/bin INSTALLS += target -QT += sql xml +QT += sql xml websockets LIBS += -L$$top_builddir/libguh/ -lguh diff --git a/server/transportinterface.cpp b/server/transportinterface.cpp index 8375fd4e..b0db9a22 100644 --- a/server/transportinterface.cpp +++ b/server/transportinterface.cpp @@ -103,7 +103,6 @@ void TransportInterface::sendResponse(const QUuid &clientId, int commandId, cons sendData(clientId, response); } - /*! Send a JSON error response to the client with the given \a clientId, * \a commandId and \a error to the inerted \l{TransportInterface}. */ diff --git a/tests/auto/websocketserver/testwebsocketserver.cpp b/tests/auto/websocketserver/testwebsocketserver.cpp index f42df374..78b2aed3 100644 --- a/tests/auto/websocketserver/testwebsocketserver.cpp +++ b/tests/auto/websocketserver/testwebsocketserver.cpp @@ -31,9 +31,7 @@ #include #include -#ifdef WEBSOCKET #include -#endif using namespace guhserver; @@ -42,7 +40,6 @@ class TestWebSocketServer: public GuhTestBase Q_OBJECT private slots: -#ifdef WEBSOCKET void testHandshake(); void pingTest(); @@ -57,11 +54,8 @@ private: QVariant injectSocketAndWait(const QString &method, const QVariantMap ¶ms = QVariantMap()); QVariant injectSocketData(const QByteArray &data); - -#endif }; -#ifdef WEBSOCKET void TestWebSocketServer::testHandshake() { @@ -216,7 +210,5 @@ QVariant TestWebSocketServer::injectSocketData(const QByteArray &data) return QVariant(); } -#endif - #include "testwebsocketserver.moc" QTEST_MAIN(TestWebSocketServer) diff --git a/tests/auto/websocketserver/websocketserver.pro b/tests/auto/websocketserver/websocketserver.pro index 9fdccde3..281a146d 100644 --- a/tests/auto/websocketserver/websocketserver.pro +++ b/tests/auto/websocketserver/websocketserver.pro @@ -1,9 +1,7 @@ include(../../../guh.pri) include(../autotests.pri) -contains(DEFINES, WEBSOCKET){ - QT += websockets -} +QT += websockets TARGET = websocketserver SOURCES += testwebsocketserver.cpp