Merge PR #368: Improve certificate pinning mechanism
This commit is contained in:
commit
6f3e4fae32
@ -69,32 +69,6 @@ NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent)
|
||||
updateActiveBearers();
|
||||
}
|
||||
|
||||
void NymeaConnection::acceptCertificate(const QString &url, const QByteArray &pem)
|
||||
{
|
||||
storePem(url, pem);
|
||||
if (m_currentHost) {
|
||||
connectInternal(m_currentHost);
|
||||
}
|
||||
}
|
||||
|
||||
bool NymeaConnection::isTrusted(const QString &url)
|
||||
{
|
||||
// Do we have a legacy fingerprint
|
||||
QSettings settings;
|
||||
settings.beginGroup("acceptedCertificates");
|
||||
if (settings.contains(QUrl(url).host())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do we have a PEM file?
|
||||
QByteArray pem;
|
||||
if (loadPem(url, pem)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NymeaConnection::BearerTypes NymeaConnection::availableBearerTypes() const
|
||||
{
|
||||
return m_availableBearerTypes;
|
||||
@ -161,7 +135,6 @@ Connection *NymeaConnection::currentConnection() const
|
||||
if (!m_currentHost || !m_currentTransport) {
|
||||
return nullptr;
|
||||
}
|
||||
qDebug() << "secure:" << m_transportCandidates.value(m_currentTransport)->secure();
|
||||
return m_transportCandidates.value(m_currentTransport);
|
||||
}
|
||||
|
||||
@ -187,63 +160,8 @@ void NymeaConnection::onSslErrors(const QList<QSslError> &errors)
|
||||
qDebug() << "Ignoring host mismatch on certificate.";
|
||||
ignoredErrors.append(error);
|
||||
} else if (error.error() == QSslError::SelfSignedCertificate || error.error() == QSslError::CertificateUntrusted) {
|
||||
// Check our cert DB
|
||||
QByteArray pem;
|
||||
|
||||
|
||||
// Keep this for compatibility with old versions for a bit...
|
||||
// New code will look up the PEM instead and set it before the connection attempt
|
||||
// However, we want to emit verifyConnectionCertificate in any case here.
|
||||
QSettings settings;
|
||||
settings.beginGroup("acceptedCertificates");
|
||||
QByteArray storedFingerPrint = settings.value(transport->url().host()).toByteArray();
|
||||
settings.endGroup();
|
||||
|
||||
QByteArray certificateFingerprint;
|
||||
QByteArray digest = error.certificate().digest(QCryptographicHash::Sha256);
|
||||
for (int i = 0; i < digest.length(); i++) {
|
||||
if (certificateFingerprint.length() > 0) {
|
||||
certificateFingerprint.append(":");
|
||||
}
|
||||
certificateFingerprint.append(digest.mid(i,1).toHex().toUpper());
|
||||
}
|
||||
|
||||
// Ignore self signed certs for connections to localhost
|
||||
if (QHostAddress(transport->url().host()) == QHostAddress::LocalHost || QHostAddress(transport->url().host()) == QHostAddress::LocalHostIPv6) {
|
||||
ignoredErrors.append(error);
|
||||
|
||||
// Check old style fingerprint storage
|
||||
} else if (storedFingerPrint == certificateFingerprint) {
|
||||
qDebug() << "This fingerprint is known to us.";
|
||||
ignoredErrors.append(error);
|
||||
|
||||
// Update the config to use the new system:
|
||||
storePem(transport->url(), error.certificate().toPem());
|
||||
|
||||
// Check new style PEM storage
|
||||
} else if (loadPem(transport->url(), pem) && pem == error.certificate().toPem()) {
|
||||
qDebug() << "Found a SSL certificate for this host. Ignoring error.";
|
||||
ignoredErrors.append(error);
|
||||
|
||||
// Ok... nothing found... Pop up the message
|
||||
} else {
|
||||
qDebug() << "Host presents an unknown self signed certificate:" << error.certificate();
|
||||
qDebug() << "Asking user for confirmation.";
|
||||
|
||||
QStringList info;
|
||||
info << tr("Common Name:") << error.certificate().issuerInfo(QSslCertificate::CommonName);
|
||||
info << tr("Oragnisation:") <<error.certificate().issuerInfo(QSslCertificate::Organization);
|
||||
info << tr("Locality:") << error.certificate().issuerInfo(QSslCertificate::LocalityName);
|
||||
info << tr("Oragnisational Unit:")<< error.certificate().issuerInfo(QSslCertificate::OrganizationalUnitName);
|
||||
info << tr("Country:")<< error.certificate().issuerInfo(QSslCertificate::CountryName);
|
||||
// info << tr("State:")<< error.certificate().issuerInfo(QSslCertificate::StateOrProvinceName);
|
||||
// info << tr("Name Qualifier:")<< error.certificate().issuerInfo(QSslCertificate::DistinguishedNameQualifier);
|
||||
// info << tr("Email:")<< error.certificate().issuerInfo(QSslCertificate::EmailAddress);
|
||||
|
||||
m_connectionStatus = ConnectionStatusSslUntrusted;
|
||||
emit connectionStatusChanged();
|
||||
emit verifyConnectionCertificate(transport->url().toString(), info, certificateFingerprint, error.certificate().toPem());
|
||||
}
|
||||
qDebug() << "Ignoring self signed certificate.";
|
||||
ignoredErrors.append(error);
|
||||
} else {
|
||||
// Reject the connection on all other errors...
|
||||
qDebug() << "SSL Error:" << error.errorString() << error.certificate();
|
||||
@ -475,35 +393,6 @@ void NymeaConnection::updateActiveBearers()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool NymeaConnection::storePem(const QUrl &host, const QByteArray &pem)
|
||||
{
|
||||
QDir dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/sslcerts/");
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/sslcerts/");
|
||||
}
|
||||
QFile certFile(dir.absoluteFilePath(host.host() + ".pem"));
|
||||
if (!certFile.open(QFile::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
certFile.write(pem);
|
||||
certFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NymeaConnection::loadPem(const QUrl &host, QByteArray &pem)
|
||||
{
|
||||
QDir dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/sslcerts/");
|
||||
// qDebug() << "Loading certificates from:" << dir.absoluteFilePath(host.host() + ".pem");
|
||||
QFile certFile(dir.absoluteFilePath(host.host() + ".pem"));
|
||||
if (!certFile.open(QFile::ReadOnly)) {
|
||||
return false;
|
||||
}
|
||||
pem.clear();
|
||||
pem.append(certFile.readAll());
|
||||
return true;
|
||||
}
|
||||
|
||||
void NymeaConnection::registerTransport(NymeaTransportInterfaceFactory *transportFactory)
|
||||
{
|
||||
foreach (const QString &scheme, transportFactory->supportedSchemes()) {
|
||||
@ -511,7 +400,7 @@ void NymeaConnection::registerTransport(NymeaTransportInterfaceFactory *transpor
|
||||
}
|
||||
}
|
||||
|
||||
void NymeaConnection::connect(NymeaHost *nymeaHost, Connection *connection)
|
||||
void NymeaConnection::connectToHost(NymeaHost *nymeaHost, Connection *connection)
|
||||
{
|
||||
if (!nymeaHost) {
|
||||
return;
|
||||
@ -602,15 +491,15 @@ bool NymeaConnection::connectInternal(Connection *connection)
|
||||
QObject::connect(newTransport, &NymeaTransportInterface::disconnected, this, &NymeaConnection::onDisconnected);
|
||||
QObject::connect(newTransport, &NymeaTransportInterface::dataReady, this, &NymeaConnection::dataAvailable);
|
||||
|
||||
// Load any certificate we might have for this url
|
||||
QByteArray pem;
|
||||
if (loadPem(connection->url(), pem)) {
|
||||
qDebug() << "Loaded SSL certificate for" << connection->url().host();
|
||||
QList<QSslError> expectedSslErrors;
|
||||
expectedSslErrors.append(QSslError::HostNameMismatch);
|
||||
expectedSslErrors.append(QSslError(QSslError::SelfSignedCertificate, QSslCertificate(pem)));
|
||||
newTransport->ignoreSslErrors(expectedSslErrors);
|
||||
}
|
||||
// // Load any certificate we might have for this url
|
||||
// QByteArray pem;
|
||||
// if (loadPem(connection->url(), pem)) {
|
||||
// qDebug() << "Loaded SSL certificate for" << connection->url().host();
|
||||
// QList<QSslError> expectedSslErrors;
|
||||
// expectedSslErrors.append(QSslError::HostNameMismatch);
|
||||
// expectedSslErrors.append(QSslError(QSslError::SelfSignedCertificate, QSslCertificate(pem)));
|
||||
// newTransport->ignoreSslErrors(expectedSslErrors);
|
||||
// }
|
||||
|
||||
m_transportCandidates.insert(newTransport, connection);
|
||||
qDebug() << "Connecting to:" << connection->url() << newTransport << m_transportCandidates.value(newTransport);
|
||||
@ -666,7 +555,20 @@ bool NymeaConnection::isConnectionBearerAvailable(Connection::BearerType connect
|
||||
return false;
|
||||
}
|
||||
|
||||
void NymeaConnection::disconnect()
|
||||
void NymeaConnection::disconnectFromHost()
|
||||
{
|
||||
setCurrentHost(nullptr);
|
||||
}
|
||||
|
||||
bool NymeaConnection::isEncrypted() const
|
||||
{
|
||||
return m_currentTransport && m_currentTransport->isEncrypted();
|
||||
}
|
||||
|
||||
QSslCertificate NymeaConnection::sslCertificate() const
|
||||
{
|
||||
if (!m_currentTransport) {
|
||||
return QSslCertificate();
|
||||
}
|
||||
return m_currentTransport->serverCertificate();
|
||||
}
|
||||
|
||||
@ -85,10 +85,11 @@ public:
|
||||
|
||||
void registerTransport(NymeaTransportInterfaceFactory *transportFactory);
|
||||
|
||||
Q_INVOKABLE void connect(NymeaHost* nymeaHost, Connection *connection = nullptr);
|
||||
Q_INVOKABLE void disconnect();
|
||||
Q_INVOKABLE void acceptCertificate(const QString &url, const QByteArray &pem);
|
||||
Q_INVOKABLE bool isTrusted(const QString &url);
|
||||
Q_INVOKABLE void connectToHost(NymeaHost* nymeaHost, Connection *connection = nullptr);
|
||||
Q_INVOKABLE void disconnectFromHost();
|
||||
|
||||
bool isEncrypted() const;
|
||||
QSslCertificate sslCertificate() const;
|
||||
|
||||
NymeaConnection::BearerTypes availableBearerTypes() const;
|
||||
|
||||
@ -120,9 +121,6 @@ private slots:
|
||||
|
||||
void updateActiveBearers();
|
||||
private:
|
||||
bool storePem(const QUrl &host, const QByteArray &pem);
|
||||
bool loadPem(const QUrl &host, QByteArray &pem);
|
||||
|
||||
void connectInternal(NymeaHost *host);
|
||||
bool connectInternal(Connection *connection);
|
||||
|
||||
|
||||
@ -243,6 +243,11 @@ QString Connection::hostAddress() const
|
||||
return m_url.host();
|
||||
}
|
||||
|
||||
int Connection::port() const
|
||||
{
|
||||
return m_url.port();
|
||||
}
|
||||
|
||||
Connection::BearerType Connection::bearerType() const
|
||||
{
|
||||
return m_bearerType;
|
||||
|
||||
@ -43,6 +43,7 @@ class Connection: public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QUrl url READ url CONSTANT)
|
||||
Q_PROPERTY(QString hostAddress READ hostAddress CONSTANT)
|
||||
Q_PROPERTY(int port READ port CONSTANT)
|
||||
Q_PROPERTY(BearerType bearerType READ bearerType CONSTANT)
|
||||
Q_PROPERTY(bool secure READ secure CONSTANT)
|
||||
Q_PROPERTY(QString displayName READ displayName CONSTANT)
|
||||
@ -68,6 +69,7 @@ public:
|
||||
|
||||
QUrl url() const;
|
||||
QString hostAddress() const;
|
||||
int port() const;
|
||||
BearerType bearerType() const;
|
||||
bool secure() const;
|
||||
QString displayName() const;
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
#include "nymeahosts.h"
|
||||
#include "connection/discovery/nymeadiscovery.h"
|
||||
#include "nymeahost.h"
|
||||
#include "connection/nymeaconnection.h"
|
||||
#include "jsonrpc/jsonrpcclient.h"
|
||||
#include <QUuid>
|
||||
|
||||
NymeaHosts::NymeaHosts(QObject *parent) :
|
||||
@ -186,18 +186,18 @@ void NymeaHostsFilterModel::setDiscovery(NymeaDiscovery *discovery)
|
||||
}
|
||||
}
|
||||
|
||||
NymeaConnection *NymeaHostsFilterModel::nymeaConnection() const
|
||||
JsonRpcClient *NymeaHostsFilterModel::jsonRpcClient() const
|
||||
{
|
||||
return m_nymeaConnection;
|
||||
return m_jsonRpcClient;
|
||||
}
|
||||
|
||||
void NymeaHostsFilterModel::setNymeaConnection(NymeaConnection *nymeaConnection)
|
||||
void NymeaHostsFilterModel::setJsonRpcClient(JsonRpcClient *jsonRpcClient)
|
||||
{
|
||||
if (m_nymeaConnection != nymeaConnection) {
|
||||
m_nymeaConnection = nymeaConnection;
|
||||
emit nymeaConnectionChanged();
|
||||
if (m_jsonRpcClient != jsonRpcClient) {
|
||||
m_jsonRpcClient = jsonRpcClient;
|
||||
emit jsonRpcClientChanged();
|
||||
|
||||
connect(m_nymeaConnection, &NymeaConnection::availableBearerTypesChanged, this, [this](){
|
||||
connect(m_jsonRpcClient, &JsonRpcClient::availableBearerTypesChanged, this, [this](){
|
||||
// qDebug() << "Bearer Types Changed!";
|
||||
invalidateFilter();
|
||||
emit countChanged();
|
||||
@ -247,24 +247,24 @@ bool NymeaHostsFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &s
|
||||
{
|
||||
Q_UNUSED(sourceParent)
|
||||
NymeaHost *host = m_nymeaDiscovery->nymeaHosts()->get(sourceRow);
|
||||
if (m_nymeaConnection && !m_showUneachableBearers) {
|
||||
if (m_jsonRpcClient && !m_showUneachableBearers) {
|
||||
bool hasReachableConnection = false;
|
||||
for (int i = 0; i < host->connections()->rowCount(); i++) {
|
||||
// qDebug() << "checking host for available bearer" << host->name() << host->connections()->get(i)->url() << "available bearer types:" << m_nymeaConnection->availableBearerTypes() << "hosts bearer types" << host->connections()->get(i)->bearerType();
|
||||
// Either enable a connection when the Bearer type is directly available
|
||||
switch (host->connections()->get(i)->bearerType()) {
|
||||
case Connection::BearerTypeLan:
|
||||
hasReachableConnection |= m_nymeaConnection->availableBearerTypes().testFlag(NymeaConnection::BearerTypeEthernet);
|
||||
hasReachableConnection |= m_nymeaConnection->availableBearerTypes().testFlag(NymeaConnection::BearerTypeWiFi);
|
||||
hasReachableConnection |= m_jsonRpcClient->availableBearerTypes().testFlag(NymeaConnection::BearerTypeEthernet);
|
||||
hasReachableConnection |= m_jsonRpcClient->availableBearerTypes().testFlag(NymeaConnection::BearerTypeWiFi);
|
||||
break;
|
||||
case Connection::BearerTypeWan:
|
||||
case Connection::BearerTypeCloud:
|
||||
hasReachableConnection |= m_nymeaConnection->availableBearerTypes().testFlag(NymeaConnection::BearerTypeEthernet);
|
||||
hasReachableConnection |= m_nymeaConnection->availableBearerTypes().testFlag(NymeaConnection::BearerTypeWiFi);
|
||||
hasReachableConnection |= m_nymeaConnection->availableBearerTypes().testFlag(NymeaConnection::BearerTypeMobileData);
|
||||
hasReachableConnection |= m_jsonRpcClient->availableBearerTypes().testFlag(NymeaConnection::BearerTypeEthernet);
|
||||
hasReachableConnection |= m_jsonRpcClient->availableBearerTypes().testFlag(NymeaConnection::BearerTypeWiFi);
|
||||
hasReachableConnection |= m_jsonRpcClient->availableBearerTypes().testFlag(NymeaConnection::BearerTypeMobileData);
|
||||
break;
|
||||
case Connection::BearerTypeBluetooth:
|
||||
hasReachableConnection |= m_nymeaConnection->availableBearerTypes().testFlag(NymeaConnection::BearerTypeBluetooth);
|
||||
hasReachableConnection |= m_jsonRpcClient->availableBearerTypes().testFlag(NymeaConnection::BearerTypeBluetooth);
|
||||
break;
|
||||
case Connection::BearerTypeUnknown:
|
||||
hasReachableConnection = true;
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
#include "nymeahost.h"
|
||||
|
||||
class NymeaDiscovery;
|
||||
class NymeaConnection;
|
||||
class JsonRpcClient;
|
||||
|
||||
class NymeaHosts : public QAbstractListModel
|
||||
{
|
||||
@ -87,7 +87,7 @@ class NymeaHostsFilterModel: public QSortFilterProxyModel
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
Q_PROPERTY(NymeaDiscovery* discovery READ discovery WRITE setDiscovery NOTIFY discoveryChanged)
|
||||
Q_PROPERTY(NymeaConnection* nymeaConnection READ nymeaConnection WRITE setNymeaConnection NOTIFY nymeaConnectionChanged)
|
||||
Q_PROPERTY(JsonRpcClient* jsonRpcClient READ jsonRpcClient WRITE setJsonRpcClient NOTIFY jsonRpcClientChanged)
|
||||
Q_PROPERTY(bool showUnreachableBearers READ showUnreachableBearers WRITE setShowUnreachableBearers NOTIFY showUnreachableBearersChanged)
|
||||
Q_PROPERTY(bool showUnreachableHosts READ showUnreachableHosts WRITE setShowUnreachableHosts NOTIFY showUnreachableHostsChanged)
|
||||
|
||||
@ -97,8 +97,8 @@ public:
|
||||
NymeaDiscovery* discovery() const;
|
||||
void setDiscovery(NymeaDiscovery *discovery);
|
||||
|
||||
NymeaConnection* nymeaConnection() const;
|
||||
void setNymeaConnection(NymeaConnection* nymeaConnection);
|
||||
JsonRpcClient* jsonRpcClient() const;
|
||||
void setJsonRpcClient(JsonRpcClient* jsonRpcClient);
|
||||
|
||||
bool showUnreachableBearers() const;
|
||||
void setShowUnreachableBearers(bool showUnreachableBearers);
|
||||
@ -111,7 +111,7 @@ public:
|
||||
signals:
|
||||
void countChanged();
|
||||
void discoveryChanged();
|
||||
void nymeaConnectionChanged();
|
||||
void jsonRpcClientChanged();
|
||||
void showUnreachableBearersChanged();
|
||||
void showUnreachableHostsChanged();
|
||||
|
||||
@ -120,7 +120,7 @@ protected:
|
||||
|
||||
private:
|
||||
NymeaDiscovery *m_nymeaDiscovery = nullptr;
|
||||
NymeaConnection *m_nymeaConnection = nullptr;
|
||||
JsonRpcClient *m_jsonRpcClient = nullptr;
|
||||
|
||||
bool m_showUneachableBearers = false;
|
||||
bool m_showUneachableHosts = false;
|
||||
|
||||
@ -66,6 +66,8 @@ public:
|
||||
virtual ConnectionState connectionState() const = 0;
|
||||
virtual void sendData(const QByteArray &data) = 0;
|
||||
virtual void ignoreSslErrors(const QList<QSslError> &errors) { Q_UNUSED(errors) }
|
||||
virtual bool isEncrypted() const { return false; }
|
||||
virtual QSslCertificate serverCertificate() const { return QSslCertificate(); }
|
||||
|
||||
signals:
|
||||
void connected();
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#include "tcpsockettransport.h"
|
||||
|
||||
#include <QUrl>
|
||||
#include <QSslConfiguration>
|
||||
|
||||
TcpSocketTransport::TcpSocketTransport(QObject *parent) : NymeaTransportInterface(parent)
|
||||
{
|
||||
@ -58,6 +59,16 @@ void TcpSocketTransport::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
m_socket.ignoreSslErrors(errors);
|
||||
}
|
||||
|
||||
bool TcpSocketTransport::isEncrypted() const
|
||||
{
|
||||
return m_socket.isEncrypted();
|
||||
}
|
||||
|
||||
QSslCertificate TcpSocketTransport::serverCertificate() const
|
||||
{
|
||||
return m_socket.peerCertificate();
|
||||
}
|
||||
|
||||
void TcpSocketTransport::onConnected()
|
||||
{
|
||||
if (m_url.scheme() == "nymea") {
|
||||
|
||||
@ -56,6 +56,8 @@ public:
|
||||
void disconnect() override;
|
||||
void sendData(const QByteArray &data) override;
|
||||
void ignoreSslErrors(const QList<QSslError> &errors) override;
|
||||
bool isEncrypted() const override;
|
||||
QSslCertificate serverCertificate() const override;
|
||||
|
||||
private slots:
|
||||
void onConnected();
|
||||
|
||||
@ -99,6 +99,16 @@ void WebsocketTransport::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
m_socket->ignoreSslErrors();
|
||||
}
|
||||
|
||||
bool WebsocketTransport::isEncrypted() const
|
||||
{
|
||||
return !m_socket->sslConfiguration().isNull();
|
||||
}
|
||||
|
||||
QSslCertificate WebsocketTransport::serverCertificate() const
|
||||
{
|
||||
return m_socket->sslConfiguration().peerCertificate();
|
||||
}
|
||||
|
||||
void WebsocketTransport::onTextMessageReceived(const QString &data)
|
||||
{
|
||||
emit dataReady(data.toUtf8());
|
||||
|
||||
@ -56,6 +56,9 @@ public:
|
||||
void sendData(const QByteArray &data) override;
|
||||
void ignoreSslErrors(const QList<QSslError> &errors) override;
|
||||
|
||||
bool isEncrypted() const override;
|
||||
QSslCertificate serverCertificate() const override;
|
||||
|
||||
private:
|
||||
QUrl m_url;
|
||||
QWebSocket *m_socket;
|
||||
|
||||
@ -108,7 +108,6 @@ void DeviceClasses::addDeviceClass(DeviceClass *deviceClass)
|
||||
void DeviceClasses::clearModel()
|
||||
{
|
||||
beginResetModel();
|
||||
qDebug() << "Devices: delete all deviceClasses";
|
||||
qDeleteAll(m_deviceClasses);
|
||||
m_deviceClasses.clear();
|
||||
endResetModel();
|
||||
|
||||
@ -134,7 +134,6 @@ void Devices::removeDevice(Device *device)
|
||||
void Devices::clearModel()
|
||||
{
|
||||
beginResetModel();
|
||||
qDebug() << "Devices: delete all devices";
|
||||
qDeleteAll(m_devices);
|
||||
m_devices.clear();
|
||||
endResetModel();
|
||||
|
||||
@ -39,15 +39,9 @@
|
||||
#include "system/systemcontroller.h"
|
||||
#include "configuration/networkmanager.h"
|
||||
|
||||
#include "connection/tcpsockettransport.h"
|
||||
#include "connection/websockettransport.h"
|
||||
#include "connection/bluetoothtransport.h"
|
||||
#include "connection/cloudtransport.h"
|
||||
|
||||
Engine::Engine(QObject *parent) :
|
||||
QObject(parent),
|
||||
m_connection(new NymeaConnection(this)),
|
||||
m_jsonRpcClient(new JsonRpcClient(m_connection, this)),
|
||||
m_jsonRpcClient(new JsonRpcClient(this)),
|
||||
m_deviceManager(new DeviceManager(m_jsonRpcClient, this)),
|
||||
m_ruleManager(new RuleManager(m_jsonRpcClient, this)),
|
||||
m_scriptManager(new ScriptManager(m_jsonRpcClient, this)),
|
||||
@ -56,10 +50,6 @@ Engine::Engine(QObject *parent) :
|
||||
m_nymeaConfiguration(new NymeaConfiguration(m_jsonRpcClient, this)),
|
||||
m_systemController(new SystemController(m_jsonRpcClient, this))
|
||||
{
|
||||
m_connection->registerTransport(new TcpSocketTransportFactory());
|
||||
m_connection->registerTransport(new WebsocketTransportFactory());
|
||||
m_connection->registerTransport(new BluetoothTransportFactoy());
|
||||
m_connection->registerTransport(new CloudTransportFactory());
|
||||
|
||||
connect(m_jsonRpcClient, &JsonRpcClient::connectedChanged, this, &Engine::onConnectedChanged);
|
||||
|
||||
@ -138,11 +128,6 @@ void Engine::deployCertificate()
|
||||
});
|
||||
}
|
||||
|
||||
NymeaConnection *Engine::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void Engine::onConnectedChanged()
|
||||
{
|
||||
qDebug() << "Engine: connected changed:" << m_jsonRpcClient->connected();
|
||||
|
||||
@ -49,7 +49,6 @@ class NetworkManager;
|
||||
class Engine : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(NymeaConnection* connection READ connection CONSTANT)
|
||||
Q_PROPERTY(DeviceManager* deviceManager READ deviceManager CONSTANT)
|
||||
Q_PROPERTY(RuleManager* ruleManager READ ruleManager CONSTANT)
|
||||
Q_PROPERTY(ScriptManager* scriptManager READ scriptManager CONSTANT)
|
||||
@ -64,7 +63,6 @@ public:
|
||||
bool connected() const;
|
||||
QString connectedHost() const;
|
||||
|
||||
NymeaConnection *connection() const;
|
||||
DeviceManager *deviceManager() const;
|
||||
RuleManager *ruleManager() const;
|
||||
ScriptManager *scriptManager() const;
|
||||
@ -77,7 +75,6 @@ public:
|
||||
Q_INVOKABLE void deployCertificate();
|
||||
|
||||
private:
|
||||
NymeaConnection *m_connection;
|
||||
JsonRpcClient *m_jsonRpcClient;
|
||||
DeviceManager *m_deviceManager;
|
||||
RuleManager *m_ruleManager;
|
||||
|
||||
@ -33,6 +33,11 @@
|
||||
#include "types/param.h"
|
||||
#include "types/params.h"
|
||||
|
||||
#include "connection/tcpsockettransport.h"
|
||||
#include "connection/websockettransport.h"
|
||||
#include "connection/bluetoothtransport.h"
|
||||
#include "connection/cloudtransport.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QVariantMap>
|
||||
#include <QDebug>
|
||||
@ -41,13 +46,24 @@
|
||||
#include <QVersionNumber>
|
||||
#include <QMetaEnum>
|
||||
#include <QLocale>
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
|
||||
JsonRpcClient::JsonRpcClient(NymeaConnection *connection, QObject *parent) :
|
||||
JsonRpcClient::JsonRpcClient(QObject *parent) :
|
||||
JsonHandler(parent),
|
||||
m_id(0),
|
||||
m_connection(connection)
|
||||
m_id(0)
|
||||
{
|
||||
m_connection = new NymeaConnection(this);
|
||||
m_connection->registerTransport(new TcpSocketTransportFactory());
|
||||
m_connection->registerTransport(new WebsocketTransportFactory());
|
||||
m_connection->registerTransport(new BluetoothTransportFactoy());
|
||||
m_connection->registerTransport(new CloudTransportFactory());
|
||||
|
||||
connect(m_connection, &NymeaConnection::availableBearerTypesChanged, this, &JsonRpcClient::availableBearerTypesChanged);
|
||||
connect(m_connection, &NymeaConnection::connectionStatusChanged, this, &JsonRpcClient::connectionStatusChanged);
|
||||
connect(m_connection, &NymeaConnection::connectedChanged, this, &JsonRpcClient::onInterfaceConnectedChanged);
|
||||
connect(m_connection, &NymeaConnection::currentHostChanged, this, &JsonRpcClient:: currentHostChanged);
|
||||
connect(m_connection, &NymeaConnection::currentConnectionChanged, this, &JsonRpcClient:: currentConnectionChanged);
|
||||
connect(m_connection, &NymeaConnection::dataAvailable, this, &JsonRpcClient::dataReceived);
|
||||
|
||||
registerNotificationHandler(this, "notificationReceived");
|
||||
@ -89,6 +105,32 @@ int JsonRpcClient::sendCommand(const QString &method, QObject *caller, const QSt
|
||||
return sendCommand(method, QVariantMap(), caller, callbackMethod);
|
||||
}
|
||||
|
||||
NymeaConnection::BearerTypes JsonRpcClient::availableBearerTypes() const
|
||||
{
|
||||
return m_connection->availableBearerTypes();
|
||||
}
|
||||
|
||||
NymeaConnection::ConnectionStatus JsonRpcClient::connectionStatus() const
|
||||
{
|
||||
return m_connection->connectionStatus();
|
||||
}
|
||||
|
||||
void JsonRpcClient::connectToHost(NymeaHost *host, Connection *connection)
|
||||
{
|
||||
m_connection->connectToHost(host, connection);
|
||||
}
|
||||
|
||||
void JsonRpcClient::disconnectFromHost()
|
||||
{
|
||||
m_connection->disconnectFromHost();
|
||||
}
|
||||
|
||||
void JsonRpcClient::acceptCertificate(const QString &serverUuid, const QByteArray &pem)
|
||||
{
|
||||
qDebug() << "Pinning new certificate for" << serverUuid;
|
||||
storePem(serverUuid, pem);
|
||||
}
|
||||
|
||||
void JsonRpcClient::getCloudConnectionStatus()
|
||||
{
|
||||
JsonRpcReply *reply = createReply("JSONRPC.IsCloudConnected", QVariantMap(), this, "isCloudConnectedReply");
|
||||
@ -174,6 +216,37 @@ bool JsonRpcClient::connected() const
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
NymeaHost *JsonRpcClient::currentHost() const
|
||||
{
|
||||
return m_connection->currentHost();
|
||||
}
|
||||
|
||||
Connection *JsonRpcClient::currentConnection() const
|
||||
{
|
||||
return m_connection->currentConnection();
|
||||
}
|
||||
|
||||
QVariantMap JsonRpcClient::certificateIssuerInfo() const
|
||||
{
|
||||
QVariantMap issuerInfo;
|
||||
foreach (const QByteArray &attr, m_connection->sslCertificate().issuerInfoAttributes()) {
|
||||
issuerInfo.insert(attr, m_connection->sslCertificate().issuerInfo(attr));
|
||||
}
|
||||
|
||||
QByteArray certificateFingerprint;
|
||||
QByteArray digest = m_connection->sslCertificate().digest(QCryptographicHash::Sha256);
|
||||
for (int i = 0; i < digest.length(); i++) {
|
||||
if (certificateFingerprint.length() > 0) {
|
||||
certificateFingerprint.append(":");
|
||||
}
|
||||
certificateFingerprint.append(digest.mid(i,1).toHex().toUpper());
|
||||
}
|
||||
|
||||
issuerInfo.insert("fingerprint", certificateFingerprint);
|
||||
|
||||
return issuerInfo;
|
||||
}
|
||||
|
||||
bool JsonRpcClient::initialSetupRequired() const
|
||||
{
|
||||
return m_initialSetupRequired;
|
||||
@ -377,6 +450,33 @@ void JsonRpcClient::sendRequest(const QVariantMap &request)
|
||||
m_connection->sendData(QJsonDocument::fromVariant(newRequest).toJson(QJsonDocument::Compact) + "\n");
|
||||
}
|
||||
|
||||
bool JsonRpcClient::loadPem(const QUuid &serverUud, QByteArray &pem)
|
||||
{
|
||||
QDir dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/sslcerts/");
|
||||
QFile certFile(dir.absoluteFilePath(serverUud.toString().remove(QRegExp("[{}]")) + ".pem"));
|
||||
if (!certFile.open(QFile::ReadOnly)) {
|
||||
return false;
|
||||
}
|
||||
pem.clear();
|
||||
pem.append(certFile.readAll());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonRpcClient::storePem(const QUuid &serverUuid, const QByteArray &pem)
|
||||
{
|
||||
QDir dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/sslcerts/");
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/sslcerts/");
|
||||
}
|
||||
QFile certFile(dir.absoluteFilePath(serverUuid.toString().remove(QRegExp("[{}]")) + ".pem"));
|
||||
if (!certFile.open(QFile::WriteOnly | QFile::Truncate)) {
|
||||
return false;
|
||||
}
|
||||
certFile.write(pem);
|
||||
certFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void JsonRpcClient::onInterfaceConnectedChanged(bool connected)
|
||||
{
|
||||
|
||||
@ -497,6 +597,34 @@ void JsonRpcClient::helloReply(const QVariantMap ¶ms)
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify SSL certificate
|
||||
if (m_connection->isEncrypted()) {
|
||||
QByteArray pem;
|
||||
if (!loadPem(m_serverUuid, pem)) {
|
||||
qDebug() << "No SSL certificate for this host stored. Accepting and pinning new certificate.";
|
||||
// No certificate yet! Inform ui about it.
|
||||
emit newSslCertificate();
|
||||
storePem(m_serverUuid, m_connection->sslCertificate().toPem());
|
||||
} else {
|
||||
// We have a certificate pinned already. Check if it's the same
|
||||
if (m_connection->sslCertificate().toPem() != pem) {
|
||||
// Uh oh, the certificate has changed
|
||||
qWarning() << "This connections certificate has changed!";
|
||||
|
||||
QSslCertificate certificate = m_connection->sslCertificate();
|
||||
QVariantMap issuerInfo = certificateIssuerInfo();
|
||||
emit verifyConnectionCertificate(m_serverUuid, issuerInfo, certificate.toPem());
|
||||
|
||||
// Reject the connection until the UI explicitly accepts this...
|
||||
m_connection->disconnectFromHost();
|
||||
|
||||
return;
|
||||
}
|
||||
qDebug() << "This connections certificate is trusted.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
emit handshakeReceived();
|
||||
|
||||
if (m_connection->currentHost()->uuid().isNull()) {
|
||||
|
||||
@ -46,7 +46,11 @@ class Params;
|
||||
class JsonRpcClient : public JsonHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(NymeaConnection::BearerTypes availableBearerTypes READ availableBearerTypes NOTIFY availableBearerTypesChanged)
|
||||
Q_PROPERTY(NymeaConnection::ConnectionStatus connectionStatus READ connectionStatus NOTIFY connectionStatusChanged)
|
||||
Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
|
||||
Q_PROPERTY(NymeaHost* currentHost READ currentHost NOTIFY currentHostChanged)
|
||||
Q_PROPERTY(Connection* currentConnection READ currentConnection NOTIFY currentConnectionChanged)
|
||||
Q_PROPERTY(bool initialSetupRequired READ initialSetupRequired NOTIFY initialSetupRequiredChanged)
|
||||
Q_PROPERTY(bool authenticationRequired READ authenticationRequired NOTIFY authenticationRequiredChanged)
|
||||
Q_PROPERTY(bool pushButtonAuthAvailable READ pushButtonAuthAvailable NOTIFY pushButtonAuthAvailableChanged)
|
||||
@ -57,6 +61,7 @@ class JsonRpcClient : public JsonHandler
|
||||
Q_PROPERTY(QString serverUuid READ serverUuid NOTIFY handshakeReceived)
|
||||
Q_PROPERTY(QString serverQtVersion READ serverQtVersion NOTIFY serverQtVersionChanged)
|
||||
Q_PROPERTY(QString serverQtBuildVersion READ serverQtBuildVersion NOTIFY serverQtVersionChanged)
|
||||
Q_PROPERTY(QVariantMap certificateIssuerInfo READ certificateIssuerInfo NOTIFY currentConnectionChanged)
|
||||
|
||||
public:
|
||||
enum CloudConnectionState {
|
||||
@ -67,7 +72,7 @@ public:
|
||||
};
|
||||
Q_ENUM(CloudConnectionState)
|
||||
|
||||
explicit JsonRpcClient(NymeaConnection *connection, QObject *parent = nullptr);
|
||||
explicit JsonRpcClient(QObject *parent = nullptr);
|
||||
|
||||
QString nameSpace() const override;
|
||||
|
||||
@ -77,8 +82,12 @@ public:
|
||||
int sendCommand(const QString &method, const QVariantMap ¶ms, QObject *caller = nullptr, const QString &callbackMethod = QString());
|
||||
int sendCommand(const QString &method, QObject *caller = nullptr, const QString &callbackMethod = QString());
|
||||
|
||||
void setConnection(NymeaConnection *connection);
|
||||
NymeaConnection::BearerTypes availableBearerTypes() const;
|
||||
NymeaConnection::ConnectionStatus connectionStatus() const;
|
||||
bool connected() const;
|
||||
NymeaHost* currentHost() const;
|
||||
Connection* currentConnection() const;
|
||||
QVariantMap certificateIssuerInfo() const;
|
||||
bool initialSetupRequired() const;
|
||||
bool authenticationRequired() const;
|
||||
bool pushButtonAuthAvailable() const;
|
||||
@ -93,20 +102,31 @@ public:
|
||||
QString serverQtBuildVersion();
|
||||
|
||||
// ui methods
|
||||
Q_INVOKABLE void connectToHost(NymeaHost *host, Connection *connection = nullptr);
|
||||
Q_INVOKABLE void disconnectFromHost();
|
||||
Q_INVOKABLE void acceptCertificate(const QString &serverUuid, const QByteArray &pem);
|
||||
|
||||
Q_INVOKABLE bool ensureServerVersion(const QString &jsonRpcVersion);
|
||||
|
||||
Q_INVOKABLE int createUser(const QString &username, const QString &password);
|
||||
Q_INVOKABLE int authenticate(const QString &username, const QString &password, const QString &deviceName);
|
||||
Q_INVOKABLE int requestPushButtonAuth(const QString &deviceName);
|
||||
Q_INVOKABLE void setupRemoteAccess(const QString &idToken, const QString &userId);
|
||||
|
||||
Q_INVOKABLE bool ensureServerVersion(const QString &jsonRpcVersion);
|
||||
|
||||
signals:
|
||||
void availableBearerTypesChanged();
|
||||
void connectionStatusChanged();
|
||||
void connectedChanged(bool connected);
|
||||
void currentHostChanged();
|
||||
void currentConnectionChanged();
|
||||
void handshakeReceived();
|
||||
void newSslCertificate();
|
||||
void verifyConnectionCertificate(const QString &serverUuid, const QVariantMap &issuerInfo, const QByteArray &pem);
|
||||
void initialSetupRequiredChanged();
|
||||
void authenticationRequiredChanged();
|
||||
void pushButtonAuthAvailableChanged();
|
||||
void authenticatedChanged();
|
||||
void connectedChanged(bool connected);
|
||||
void tokenChanged();
|
||||
void invalidProtocolVersion(const QString &actualVersion, const QString &minimumVersion);
|
||||
void authenticationFailed();
|
||||
@ -166,6 +186,9 @@ private:
|
||||
|
||||
void sendRequest(const QVariantMap &request);
|
||||
|
||||
bool loadPem(const QUuid &serverUud, QByteArray &pem);
|
||||
bool storePem(const QUuid &serverUuid, const QByteArray &pem);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -124,7 +124,6 @@ void TagListModel::update()
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Model populated" << m_list.count() << this;
|
||||
endResetModel();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
@ -200,7 +200,6 @@ void SystemController::setAutomaticTime(bool automaticTime)
|
||||
|
||||
void SystemController::getCapabilitiesResponse(const QVariantMap &data)
|
||||
{
|
||||
qDebug() << "capabilities received" << data;
|
||||
m_powerManagementAvailable = data.value("params").toMap().value("powerManagement").toBool();
|
||||
emit powerManagementAvailableChanged();
|
||||
|
||||
@ -219,6 +218,8 @@ void SystemController::getCapabilitiesResponse(const QVariantMap &data)
|
||||
if (m_jsonRpcClient->ensureServerVersion("4.1")) {
|
||||
m_jsonRpcClient->sendCommand("System.GetTime", this, "getServerTimeResponse");
|
||||
}
|
||||
|
||||
qDebug() << "nymea:core capabilities: Power management:" << m_powerManagementAvailable << "Update management:" << m_updateManagementAvailable << "Time management:" << m_timeManagementAvailable;
|
||||
}
|
||||
|
||||
void SystemController::getUpdateStatusResponse(const QVariantMap &data)
|
||||
|
||||
@ -97,7 +97,6 @@ void Plugins::addPlugin(Plugin *plugin)
|
||||
void Plugins::clearModel()
|
||||
{
|
||||
beginResetModel();
|
||||
qDebug() << "Plugins: delete all plugins";
|
||||
qDeleteAll(m_plugins);
|
||||
m_plugins.clear();
|
||||
endResetModel();
|
||||
|
||||
@ -212,5 +212,6 @@
|
||||
<file>ui/components/SettingsPageBase.qml</file>
|
||||
<file>ui/components/SettingsPageSectionHeader.qml</file>
|
||||
<file>ui/grouping/GroupInterfacesPage.qml</file>
|
||||
<file>ui/connection/CertificateErrorDialog.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -45,10 +45,10 @@ Page {
|
||||
title: swipeView.currentItem.title
|
||||
leftButtonVisible: true
|
||||
leftButtonImageSource: {
|
||||
switch (engine.connection.currentConnection.bearerType) {
|
||||
switch (engine.jsonRpcClient.currentConnection.bearerType) {
|
||||
case Connection.BearerTypeLan:
|
||||
case Connection.BearerTypeWan:
|
||||
if (engine.connection.availableBearerTypes & NymeaConnection.BearerTypeEthernet != NymeaConnection.BearerTypeNone) {
|
||||
if (engine.jsonRpcClient.availableBearerTypes & NymeaConnection.BearerTypeEthernet != NymeaConnection.BearerTypeNone) {
|
||||
return "../images/network-wired.svg"
|
||||
}
|
||||
return "../images/network-wifi.svg";
|
||||
@ -62,7 +62,7 @@ Page {
|
||||
return ""
|
||||
}
|
||||
onLeftButtonClicked: {
|
||||
var dialog = connectionDialogComponent.createObject(root, {headerIcon: leftButtonImageSource})
|
||||
var dialog = connectionDialogComponent.createObject(root)
|
||||
dialog.open();
|
||||
}
|
||||
|
||||
@ -404,8 +404,25 @@ Page {
|
||||
id: connectionDialogComponent
|
||||
MeaDialog {
|
||||
id: connectionDialog
|
||||
title: engine.connection.currentHost.name
|
||||
title: engine.jsonRpcClient.currentHost.name
|
||||
standardButtons: Dialog.NoButton
|
||||
headerIcon: {
|
||||
switch (engine.jsonRpcClient.currentConnection.bearerType) {
|
||||
case Connection.BearerTypeLan:
|
||||
case Connection.BearerTypeWan:
|
||||
if (engine.jsonRpcClient.availableBearerTypes & NymeaConnection.BearerTypeEthernet != NymeaConnection.BearerTypeNone) {
|
||||
return "../images/network-wired.svg"
|
||||
}
|
||||
return "../images/network-wifi.svg";
|
||||
case Connection.BearerTypeBluetooth:
|
||||
return "../images/network-wifi.svg";
|
||||
case Connection.BearerTypeCloud:
|
||||
return "../images/cloud.svg"
|
||||
case Connection.BearerTypeLoopback:
|
||||
return "../images/network-wired.svg"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
@ -417,27 +434,11 @@ Page {
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: engine.connection.currentHost.name
|
||||
text: engine.jsonRpcClient.currentHost.name
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: engine.connection.currentHost.uuid
|
||||
font.pixelSize: app.smallFont
|
||||
elide: Text.ElideRight
|
||||
color: Material.color(Material.Grey)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: engine.connection.currentConnection.url
|
||||
font.pixelSize: app.smallFont
|
||||
elide: Text.ElideRight
|
||||
color: Material.color(Material.Grey)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
@ -445,17 +446,46 @@ Page {
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Button {
|
||||
id: cancelButton
|
||||
text: qsTr("OK")
|
||||
Layout.preferredWidth: Math.max(cancelButton.implicitWidth, disconnectButton.implicitWidth)
|
||||
onClicked: connectionDialog.close()
|
||||
ColumnLayout {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: engine.jsonRpcClient.currentHost.uuid
|
||||
font.pixelSize: app.smallFont
|
||||
elide: Text.ElideRight
|
||||
color: Material.color(Material.Grey)
|
||||
// horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: engine.jsonRpcClient.currentConnection.url
|
||||
font.pixelSize: app.smallFont
|
||||
elide: Text.ElideRight
|
||||
color: Material.color(Material.Grey)
|
||||
// horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: app.iconSize
|
||||
name: engine.jsonRpcClient.currentConnection.secure ? "../images/lock-closed.svg" : "../images/lock-open.svg"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("connection/CertificateDialog.qml"));
|
||||
var popup = component.createObject(app, {serverUuid: engine.jsonRpcClient.serverUuid, issuerInfo: engine.jsonRpcClient.certificateIssuerInfo});
|
||||
popup.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: app.margins
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Button {
|
||||
id: disconnectButton
|
||||
@ -463,9 +493,18 @@ Page {
|
||||
Layout.preferredWidth: Math.max(cancelButton.implicitWidth, disconnectButton.implicitWidth)
|
||||
onClicked: {
|
||||
tabSettings.lastConnectedHost = "";
|
||||
engine.connection.disconnect();
|
||||
engine.jsonRpcClient.disconnectFromHost();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Button {
|
||||
id: cancelButton
|
||||
text: qsTr("OK")
|
||||
Layout.preferredWidth: Math.max(cancelButton.implicitWidth, disconnectButton.implicitWidth)
|
||||
onClicked: connectionDialog.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ Item {
|
||||
readonly property Engine engine: engineObject
|
||||
readonly property Engine _engine: engineObject // In case a child cannot use "engine"
|
||||
property int connectionTabIndex: index
|
||||
// onConnectionTabIndexChanged: tabSettings.lastConnectedHost = engine.connection.url
|
||||
// onConnectionTabIndexChanged: tabSettings.lastConnectedHost = engine.jsonRpcClient.url
|
||||
|
||||
Binding {
|
||||
target: AWSClient
|
||||
@ -122,7 +122,7 @@ Item {
|
||||
Binding {
|
||||
target: _discovery
|
||||
property: "discovering"
|
||||
value: engine.connection.currentHost === null
|
||||
value: engine.jsonRpcClient.currentHost === null
|
||||
}
|
||||
|
||||
StackView {
|
||||
@ -136,12 +136,12 @@ Item {
|
||||
setupPushNotifications();
|
||||
if (autoConnectHost.length > 0) {
|
||||
var host = discovery.nymeaHosts.createLanHost("Manual connection", autoConnectHost);
|
||||
engine.connection.connect(host)
|
||||
engine.jsonRpcClient.connectToHost(host)
|
||||
} else if (tabSettings.lastConnectedHost.length > 0) {
|
||||
print("Last connected host was", tabSettings.lastConnectedHost)
|
||||
var cachedHost = discovery.nymeaHosts.find(tabSettings.lastConnectedHost);
|
||||
if (cachedHost) {
|
||||
engine.connection.connect(cachedHost)
|
||||
engine.jsonRpcClient.connectToHost(cachedHost)
|
||||
return;
|
||||
}
|
||||
print("Warning: There is a last connected host but UUID is unknown to discovery...")
|
||||
@ -153,9 +153,9 @@ Item {
|
||||
Timer { running: true; repeat: false; interval: 3000; onTriggered: PlatformHelper.hideSplashScreen(); }
|
||||
|
||||
function init() {
|
||||
print("calling init. Auth required:", engine.jsonRpcClient.authenticationRequired, "initial setup required:", engine.jsonRpcClient.initialSetupRequired, "jsonrpc connected:", engine.jsonRpcClient.connected, "Current host:", engine.connection.currentHost)
|
||||
print("calling init. Auth required:", engine.jsonRpcClient.authenticationRequired, "initial setup required:", engine.jsonRpcClient.initialSetupRequired, "jsonrpc connected:", engine.jsonRpcClient.connected, "Current host:", engine.jsonRpcClient.currentHost)
|
||||
pageStack.clear()
|
||||
if (!engine.connection.currentHost) {
|
||||
if (!engine.jsonRpcClient.currentHost) {
|
||||
print("pushing ConnectPage")
|
||||
pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml"))
|
||||
PlatformHelper.hideSplashScreen();
|
||||
@ -169,7 +169,7 @@ Item {
|
||||
var page = pageStack.push(Qt.resolvedUrl("PushButtonAuthPage.qml"))
|
||||
page.backPressed.connect(function() {
|
||||
tabSettings.lastConnectedHost = "";
|
||||
engine.connection.disconnect();
|
||||
engine.jsonRpcClient.disconnectFromHost();
|
||||
init();
|
||||
})
|
||||
return;
|
||||
@ -178,7 +178,7 @@ Item {
|
||||
var page = pageStack.push(Qt.resolvedUrl("connection/SetupWizard.qml"));
|
||||
page.backPressed.connect(function() {
|
||||
tabSettings.lastConnectedHost = "";
|
||||
engine.connection.disconnect()
|
||||
engine.jsonRpcClient.disconnectFromHost()
|
||||
init();
|
||||
})
|
||||
return;
|
||||
@ -187,7 +187,7 @@ Item {
|
||||
var page = pageStack.push(Qt.resolvedUrl("connection/LoginPage.qml"));
|
||||
page.backPressed.connect(function() {
|
||||
tabSettings.lastConnectedHost = "";
|
||||
engine.connection.disconnect()
|
||||
engine.jsonRpcClient.disconnectFromHost()
|
||||
init();
|
||||
})
|
||||
return;
|
||||
@ -200,10 +200,9 @@ Item {
|
||||
return;
|
||||
}
|
||||
|
||||
print("pushing ConnectingPage")
|
||||
var page = pageStack.push(Qt.resolvedUrl("connection/ConnectingPage.qml"));
|
||||
page.cancel.connect(function(){
|
||||
engine.connection.disconnect();
|
||||
engine.jsonRpcClient.disconnectFromHost();
|
||||
})
|
||||
}
|
||||
|
||||
@ -243,25 +242,24 @@ Item {
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: engine.connection
|
||||
target: engine.jsonRpcClient
|
||||
onCurrentHostChanged: {
|
||||
init();
|
||||
}
|
||||
onVerifyConnectionCertificate: {
|
||||
print("verify cert!")
|
||||
var certDialogComponent = Qt.createComponent(Qt.resolvedUrl("connection/CertificateDialog.qml"));
|
||||
var popup = certDialogComponent.createObject(root, {url: url, issuerInfo: issuerInfo, fingerprint: fingerprint, pem: pem});
|
||||
print("Asking user to verify certificate:", serverUuid, issuerInfo, pem)
|
||||
var certDialogComponent = Qt.createComponent(Qt.resolvedUrl("connection/CertificateErrorDialog.qml"));
|
||||
var popup = certDialogComponent.createObject(root);
|
||||
popup.accepted.connect(function(){
|
||||
engine.jsonRpcClient.acceptCertificate(serverUuid, pem);
|
||||
engine.jsonRpcClient.connectToHost(discovery.nymeaHosts.find(serverUuid));
|
||||
})
|
||||
popup.open();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Connections {
|
||||
target: engine.jsonRpcClient
|
||||
onConnectedChanged: {
|
||||
print("json client connected changed", engine.jsonRpcClient.connected)
|
||||
if (engine.jsonRpcClient.connected) {
|
||||
discovery.cacheHost(engine.connection.currentHost)
|
||||
discovery.cacheHost(engine.jsonRpcClient.currentHost)
|
||||
tabSettings.lastConnectedHost = engine.jsonRpcClient.serverUuid
|
||||
}
|
||||
init();
|
||||
@ -348,7 +346,7 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("OK")
|
||||
onClicked: {
|
||||
engine.connection.disconnect();
|
||||
engine.jsonRpcClient.disconnectFromHost();
|
||||
popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,10 +124,10 @@ SettingsPageBase {
|
||||
secondaryIconName: !model.online ? "../images/cloud-error.svg" : ""
|
||||
|
||||
onClicked: {
|
||||
print("clicked, connected:", engine.connection.connected, model.id)
|
||||
if (!engine.connection.connected) {
|
||||
print("clicked, connected:", engine.jsonRpcClient.connected, model.id)
|
||||
if (!engine.jsonRpcClient.connected) {
|
||||
var host = discovery.nymeaHosts.find(model.id)
|
||||
engine.connection.connect(host);
|
||||
engine.jsonRpcClient.connectToHost(host);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -40,14 +40,10 @@ Dialog {
|
||||
width: Math.min(parent.width * .9, 400)
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
standardButtons: Dialog.Yes | Dialog.No
|
||||
standardButtons: Dialog.Ok
|
||||
|
||||
property string url
|
||||
property var fingerprint
|
||||
property string serverUuid
|
||||
property var issuerInfo
|
||||
property var pem
|
||||
|
||||
readonly property bool hasOldFingerprint: engine.connection.isTrusted(url)
|
||||
|
||||
ColumnLayout {
|
||||
id: certLayout
|
||||
@ -60,84 +56,68 @@ Dialog {
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize * 2
|
||||
Layout.preferredWidth: height
|
||||
name: certDialog.hasOldFingerprint ? "../images/lock-broken.svg" : "../images/info.svg"
|
||||
color: certDialog.hasOldFingerprint ? "red" : app.accentColor
|
||||
name: "../images/lock-closed.svg"
|
||||
color: app.accentColor
|
||||
}
|
||||
|
||||
Label {
|
||||
id: titleLabel
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: certDialog.hasOldFingerprint ? qsTr("Warning") : qsTr("Hi there!")
|
||||
color: certDialog.hasOldFingerprint ? "red" : app.accentColor
|
||||
text: qsTr("Certificate information")
|
||||
color: app.accentColor
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: certDialog.hasOldFingerprint ? qsTr("The certificate of this %1:core has changed!").arg(app.systemName) : qsTr("It seems this is the first time you connect to this %1:core.").arg(app.systemName)
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: certDialog.hasOldFingerprint ? qsTr("Did you change the system's configuration? Verify if this information is correct.") : qsTr("This is the certificate for this %1:core. Once you trust it, an encrypted connection will be established.").arg(app.systemName)
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
implicitHeight: certGridLayout.implicitHeight
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: certGridLayout.implicitHeight
|
||||
clip: true
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
policy: contentHeight > height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: certGridLayout
|
||||
columns: 2
|
||||
width: parent.width
|
||||
|
||||
Repeater {
|
||||
model: certDialog.issuerInfo
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: modelData
|
||||
}
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.columnSpan: 2
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
text: qsTr("Fingerprint: ") + certDialog.fingerprint
|
||||
}
|
||||
}
|
||||
}
|
||||
Layout.preferredHeight: app.margins
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
Label {
|
||||
text: qsTr("nymea UUID:")
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: certDialog.hasOldFingerprint ? qsTr("Do you want to connect nevertheless?") : qsTr("Do you want to trust this device?")
|
||||
font.bold: true
|
||||
}
|
||||
Label {
|
||||
text: certDialog.serverUuid
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: app.margins
|
||||
}
|
||||
GridLayout {
|
||||
columns: 2
|
||||
Label {
|
||||
text: qsTr("Organisation:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Label {
|
||||
text: certDialog.issuerInfo["O"]
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Label {
|
||||
text: qsTr("Common name:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Label {
|
||||
text: certDialog.issuerInfo["CN"]
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: app.margins
|
||||
}
|
||||
Label {
|
||||
text: qsTr("Fingerprint:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Label {
|
||||
text: certDialog.issuerInfo["fingerprint"]
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WrapAnywhere
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
engine.connection.acceptCertificate(certDialog.url, certDialog.pem)
|
||||
}
|
||||
onRejected: {
|
||||
engine.connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
45
nymea-app/ui/connection/CertificateErrorDialog.qml
Normal file
45
nymea-app/ui/connection/CertificateErrorDialog.qml
Normal file
@ -0,0 +1,45 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, GNU version 3. This project 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
|
||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
MeaDialog {
|
||||
id: root
|
||||
|
||||
title: qsTr("Insecure connection")
|
||||
headerIcon: "../images/lock-broken.svg"
|
||||
text: qsTr("The certificate for this %1 system has changed. This could be because the configuration has been changed, but could also mean the system has been compromised. Do you want to accept the new certificate?").arg(app.systemName)
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
}
|
||||
@ -45,33 +45,22 @@ Page {
|
||||
pageStack.push(discoveryPage, StackView.Immediate)
|
||||
}
|
||||
|
||||
|
||||
function connectToHost(url, noAnimations) {
|
||||
function connectToHost(host, noAnimations) {
|
||||
var page = pageStack.push(Qt.resolvedUrl("ConnectingPage.qml"), noAnimations ? StackView.Immediate : StackView.PushTransition)
|
||||
page.cancel.connect(function() {
|
||||
engine.connection.disconnect()
|
||||
pageStack.pop(root, StackView.Immediate);
|
||||
pageStack.push(discoveryPage)
|
||||
})
|
||||
engine.connection.connect(url)
|
||||
}
|
||||
|
||||
function connectToHost2(host, noAnimations) {
|
||||
var page = pageStack.push(Qt.resolvedUrl("ConnectingPage.qml"), noAnimations ? StackView.Immediate : StackView.PushTransition)
|
||||
page.cancel.connect(function() {
|
||||
engine.connection.disconnect()
|
||||
engine.jsonRpcClient.disconnectFromHost()
|
||||
pageStack.pop(root, StackView.Immediate);
|
||||
pageStack.push(discoveryPage)
|
||||
})
|
||||
print("Connecting to host", host)
|
||||
engine.connection.connect(host)
|
||||
engine.jsonRpcClient.connectToHost(host)
|
||||
}
|
||||
|
||||
NymeaHostsFilterModel {
|
||||
id: hostsProxy
|
||||
discovery: _discovery
|
||||
showUnreachableBearers: false
|
||||
nymeaConnection: engine.connection
|
||||
jsonRpcClient: engine.jsonRpcClient
|
||||
showUnreachableHosts: false
|
||||
}
|
||||
|
||||
@ -91,7 +80,7 @@ Page {
|
||||
onClicked: {
|
||||
if (index === 2) {
|
||||
var host = discovery.nymeaHosts.createWanHost("Demo server", "nymea://nymea.nymea.io:2222")
|
||||
engine.connection.connect(host)
|
||||
engine.jsonRpcClient.connectToHost(host)
|
||||
} else {
|
||||
pageStack.push(model.get(index).page, {nymeaDiscovery: discovery});
|
||||
}
|
||||
@ -176,7 +165,7 @@ Page {
|
||||
switch (nymeaHost.connections.get(defaultConnectionIndex).bearerType) {
|
||||
case Connection.BearerTypeLan:
|
||||
case Connection.BearerTypeWan:
|
||||
if (engine.connection.availableBearerTypes & NymeaConnection.BearerTypeEthernet != NymeaConnection.BearerTypeNone) {
|
||||
if (engine.jsonRpcClient.availableBearerTypes & NymeaConnection.BearerTypeEthernet != NymeaConnection.BearerTypeNone) {
|
||||
return "../images/network-wired.svg"
|
||||
}
|
||||
return "../images/network-wifi.svg";
|
||||
@ -196,15 +185,13 @@ Page {
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
property bool isSecure: nymeaHost.connections.get(defaultConnectionIndex).secure
|
||||
property bool isTrusted: engine.connection.isTrusted(nymeaHostDelegate.nymeaHost.connections.get(defaultConnectionIndex).url)
|
||||
property bool isOnline: nymeaHost.connections.get(defaultConnectionIndex).bearerType !== Connection.BearerTypeWan ? nymeaHost.connections.get(defaultConnectionIndex).online : true
|
||||
tertiaryIconName: isSecure ? "../images/network-secure.svg" : ""
|
||||
tertiaryIconColor: isTrusted ? app.accentColor : Material.foreground
|
||||
secondaryIconName: !isOnline ? "../images/cloud-error.svg" : ""
|
||||
secondaryIconColor: "red"
|
||||
|
||||
onClicked: {
|
||||
root.connectToHost2(nymeaHostDelegate.nymeaHost)
|
||||
root.connectToHost(nymeaHostDelegate.nymeaHost)
|
||||
}
|
||||
|
||||
contextOptions: [
|
||||
@ -272,7 +259,7 @@ Page {
|
||||
text: qsTr("Demo mode (online)")
|
||||
onClicked: {
|
||||
var host = discovery.nymeaHosts.createWanHost("Demo server", "nymea://nymea.nymea.io:2222")
|
||||
engine.connection.connect(host)
|
||||
engine.jsonRpcClient.connectToHost(host)
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,7 +377,7 @@ Page {
|
||||
switch (model.bearerType) {
|
||||
case Connection.BearerTypeLan:
|
||||
case Connection.BearerTypeWan:
|
||||
if (engine.connection.availableBearerTypes & NymeaConnection.BearerTypeEthernet != NymeaConnection.BearerTypeNone) {
|
||||
if (engine.jsonRpcClient.availableBearerTypes & NymeaConnection.BearerTypeEthernet != NymeaConnection.BearerTypeNone) {
|
||||
return "../images/network-wired.svg"
|
||||
}
|
||||
return "../images/network-wifi.svg";
|
||||
@ -398,19 +385,19 @@ Page {
|
||||
return "../images/bluetooth.svg";
|
||||
case Connection.BearerTypeCloud:
|
||||
return "../images/cloud.svg"
|
||||
case Connection.BearerTypeLoopback:
|
||||
return "../images/network-wired.svg"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
tertiaryIconName: model.secure ? "../images/network-secure.svg" : ""
|
||||
tertiaryIconColor: isTrusted ? app.accentColor : "gray"
|
||||
readonly property bool isTrusted: engine.connection.isTrusted(url)
|
||||
secondaryIconName: !model.online ? "../images/cloud-error.svg" : ""
|
||||
secondaryIconColor: "red"
|
||||
|
||||
onClicked: {
|
||||
dialog.close()
|
||||
engine.connection.connect(dialog.nymeaHost, dialog.nymeaHost.connections.get(index))
|
||||
engine.jsonRpcClient.connectToHost(dialog.nymeaHost, dialog.nymeaHost.connections.get(index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ Page {
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: engine.connection.currentHost.name.length > 0 ? engine.connection.currentHost.name : engine.connection.currentHost.uuid
|
||||
text: engine.jsonRpcClient.currentHost.name.length > 0 ? engine.jsonRpcClient.currentHost.name : engine.jsonRpcClient.currentHost.uuid
|
||||
font.pixelSize: app.smallFont
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
@ -82,7 +82,7 @@ Page {
|
||||
Layout.preferredHeight: 150
|
||||
text: {
|
||||
var errorMessage;
|
||||
switch (engine.connection.connectionStatus) {
|
||||
switch (engine.jsonRpcClient.connectionStatus) {
|
||||
case NymeaConnection.ConnectionStatusUnconnected:
|
||||
case NymeaConnection.ConnectionStatusConnecting:
|
||||
case NymeaConnection.ConnectionStatusConnected:
|
||||
@ -115,7 +115,7 @@ Page {
|
||||
break;
|
||||
case NymeaConnection.ConnectionStatusUnknownError:
|
||||
default:
|
||||
errorMessage = qsTr("An unknown error happened. We're very sorry for that. (Error code: %1)").arg(engine.connection.connectionStatus);
|
||||
errorMessage = qsTr("An unknown error happened. We're very sorry for that. (Error code: %1)").arg(engine.jsonRpcClient.connectionStatus);
|
||||
}
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@ Page {
|
||||
|
||||
print("Try to connect ", rpcUrl)
|
||||
var host = discovery.nymeaHosts.createLanHost("Manual connection", rpcUrl);
|
||||
engine.connection.connect(host)
|
||||
engine.jsonRpcClient.connectToHost(host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ Page {
|
||||
text: qsTr("Connect to %1:core").arg(app.systemName)
|
||||
property string url
|
||||
onClicked: {
|
||||
engine.connection.connect(url)
|
||||
engine.jsonRpcClient.connectToHost(url)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ SettingsPageBase {
|
||||
NymeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Connection:")
|
||||
subText: engine.connection.currentConnection.url
|
||||
subText: engine.jsonRpcClient.currentConnection.url
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
}
|
||||
|
||||
@ -39,18 +39,8 @@ NymeaListItemDelegate {
|
||||
subText: qsTr("Port: %1").arg(model.port)
|
||||
iconName: "../images/network-vpn.svg"
|
||||
progressive: false
|
||||
iconColor: {
|
||||
if ((engine.connection.hostAddress === model.address || model.address === "0.0.0.0")
|
||||
&& engine.connection.port === model.port) {
|
||||
return app.accentColor
|
||||
}
|
||||
return iconKeyColor
|
||||
}
|
||||
|
||||
secondaryIconName: "../images/account.svg"
|
||||
secondaryIconColor: model.authenticationEnabled ? app.accentColor : secondaryIconKeyColor
|
||||
tertiaryIconName: "../images/network-secure.svg"
|
||||
tertiaryIconColor: model.sslEnabled ? app.accentColor : tertiaryIconKeyColor
|
||||
|
||||
// canDelete: true
|
||||
}
|
||||
|
||||
@ -47,7 +47,10 @@ SettingsPageBase {
|
||||
model: engine.nymeaConfiguration.tcpServerConfigurations
|
||||
delegate: ConnectionInterfaceDelegate {
|
||||
Layout.fillWidth: true
|
||||
canDelete: true
|
||||
iconColor: inUse ? app.accentColor : iconKeyColor
|
||||
readonly property bool inUse: (engine.jsonRpcClient.currentConnection.hostAddress === model.address || model.address === "0.0.0.0")
|
||||
&& engine.jsonRpcClient.currentConnection.port === model.port
|
||||
canDelete: !inUse
|
||||
onClicked: {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.tcpServerConfigurations.get(index).clone() });
|
||||
@ -61,7 +64,6 @@ SettingsPageBase {
|
||||
popup.open()
|
||||
}
|
||||
onDeleteClicked: {
|
||||
print("should delete")
|
||||
engine.nymeaConfiguration.deleteTcpServerConfiguration(model.id)
|
||||
}
|
||||
}
|
||||
@ -93,7 +95,10 @@ SettingsPageBase {
|
||||
model: engine.nymeaConfiguration.webSocketServerConfigurations
|
||||
delegate: ConnectionInterfaceDelegate {
|
||||
Layout.fillWidth: true
|
||||
canDelete: true
|
||||
iconColor: inUse ? app.accentColor : iconKeyColor
|
||||
readonly property bool inUse: (engine.jsonRpcClient.currentConnection.hostAddress === model.address || model.address === "0.0.0.0")
|
||||
&& engine.jsonRpcClient.currentConnection.port === model.port
|
||||
canDelete: !inUse
|
||||
onClicked: {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||
var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.webSocketServerConfigurations.get(index).clone() });
|
||||
|
||||
@ -43,7 +43,7 @@ SettingsPageBase {
|
||||
for (var i = 0; i < engine.nymeaConfiguration.webServerConfigurations.count; i++) {
|
||||
var tmp = engine.nymeaConfiguration.webServerConfigurations.get(i)
|
||||
print("checking config:", tmp.id, tmp.address, tmp.port, tmp.sslEnabled)
|
||||
if (tmp.address === engine.connection.currentConnection.hostAddress || tmp.address === "0.0.0.0") {
|
||||
if (tmp.address === engine.jsonRpcClient.currentConnection.hostAddress || tmp.address === "0.0.0.0") {
|
||||
|
||||
// This one prefers https over http...
|
||||
// if (config === null || (!config.sslEnabled && tmp.sslEnabled)) {
|
||||
@ -86,7 +86,7 @@ SettingsPageBase {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("The web server cannot be reached on %1.").arg(engine.connection.currentConnection.hostAddress)
|
||||
text: qsTr("The web server cannot be reached on %1.").arg(engine.jsonRpcClient.currentConnection.hostAddress)
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.smallFont
|
||||
color: "red"
|
||||
@ -111,10 +111,10 @@ SettingsPageBase {
|
||||
enabled: root.usedConfig !== null && engine.nymeaConfiguration.webServerConfigurations.count > 0
|
||||
text: qsTr("Open debug interface")
|
||||
onClicked: {
|
||||
print("opening:", engine.connection.currentConnection.url)
|
||||
print("opening:", engine.jsonRpcClient.currentConnection.url)
|
||||
|
||||
var proto = "http" + (root.usedConfig.sslEnabled ? "s" : "") + "://"
|
||||
var path = engine.connection.currentConnection.hostAddress + ":" + root.usedConfig.port + "/debug"
|
||||
var path = engine.jsonRpcClient.currentConnection.hostAddress + ":" + root.usedConfig.port + "/debug"
|
||||
print("opening:", proto + path)
|
||||
Qt.openUrlExternally(proto + path)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user