fix aws login, workaround wss certificate issue
parent
568b17004c
commit
8af4cd49d5
|
|
@ -104,10 +104,9 @@ AWSClient::AWSClient(QObject *parent) : QObject(parent),
|
|||
m_userId = settings.value("userId").toString();
|
||||
m_password = settings.value("password").toString();
|
||||
m_accessToken = settings.value("accessToken").toByteArray();
|
||||
m_accessTokenExpiry = settings.value("accessTokenExpiry").toDateTime();
|
||||
// m_accessTokenExpiry = settings.value("accessTokenExpiry").toDateTime();
|
||||
m_idToken = settings.value("idToken").toByteArray();
|
||||
m_refreshToken = settings.value("refreshToken").toByteArray();
|
||||
m_confirmationPending = settings.value("confirmationPending", false).toBool();
|
||||
|
||||
m_identityId = settings.value("identityId").toByteArray();
|
||||
|
||||
|
|
@ -302,12 +301,6 @@ void AWSClient::signup(const QString &username, const QString &password)
|
|||
|
||||
emit signupResult(LoginErrorNoError);
|
||||
|
||||
QSettings settings;
|
||||
settings.beginGroup("cloud");
|
||||
settings.setValue("username", m_username);
|
||||
settings.setValue("password", m_password);
|
||||
settings.setValue("confirmationPending", true);
|
||||
|
||||
m_confirmationPending = true;
|
||||
emit confirmationPendingChanged();
|
||||
});
|
||||
|
|
@ -364,6 +357,8 @@ void AWSClient::confirmRegistration(const QString &code)
|
|||
return;
|
||||
}
|
||||
|
||||
m_confirmationPending = false;
|
||||
emit confirmationPendingChanged();
|
||||
emit confirmationResult(LoginErrorNoError);
|
||||
login(m_username, m_password);
|
||||
fetchDevices();
|
||||
|
|
@ -591,9 +586,6 @@ void AWSClient::getId()
|
|||
return;
|
||||
}
|
||||
m_identityId = jsonDoc.toVariant().toMap().value("IdentityId").toByteArray();
|
||||
QSettings settings;
|
||||
settings.beginGroup("cloud");
|
||||
settings.setValue("identityId", m_identityId);
|
||||
|
||||
qDebug() << "Received cognito identity id" << m_identityId;// << qUtf8Printable(data);
|
||||
getCredentialsForIdentity(m_identityId);
|
||||
|
|
@ -767,6 +759,7 @@ void AWSClient::getCredentialsForIdentity(const QString &identityId)
|
|||
|
||||
|
||||
QSettings settings;
|
||||
qDebug() << "settings has:" << settings.childGroups();
|
||||
|
||||
bool newLogin = !settings.childGroups().contains("cloud");
|
||||
|
||||
|
|
@ -777,6 +770,7 @@ void AWSClient::getCredentialsForIdentity(const QString &identityId)
|
|||
settings.setValue("password", m_password);
|
||||
settings.setValue("accessToken", m_accessToken);
|
||||
settings.setValue("accessTokenExpiry", m_accessTokenExpiry);
|
||||
settings.setValue("identityId", m_identityId);
|
||||
settings.setValue("idToken", m_idToken);
|
||||
settings.setValue("refreshToken", m_refreshToken);
|
||||
settings.setValue("accessKeyId", m_accessKeyId);
|
||||
|
|
@ -787,7 +781,7 @@ void AWSClient::getCredentialsForIdentity(const QString &identityId)
|
|||
emit loginResult(LoginErrorNoError);
|
||||
|
||||
if (newLogin) {
|
||||
// qDebug() << "new login!";
|
||||
qDebug() << "new login!";
|
||||
emit isLoggedInChanged();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@
|
|||
#include <QUrlQuery>
|
||||
#include <QSettings>
|
||||
#include <QMetaEnum>
|
||||
#include <QStandardPaths>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
#include "nymeatransportinterface.h"
|
||||
|
||||
|
|
@ -21,21 +24,31 @@ bool NymeaConnection::connect(const QString &url)
|
|||
}
|
||||
|
||||
m_currentUrl = QUrl(url);
|
||||
emit currentUrlChanged();
|
||||
if (!m_transports.contains(m_currentUrl.scheme())) {
|
||||
qWarning() << "Cannot connect to urls of scheme" << m_currentUrl.scheme() << "Supported schemes are" << m_transports.keys();
|
||||
return false;
|
||||
}
|
||||
m_currentTransport = m_transports.value(m_currentUrl.scheme())->createTransport();
|
||||
|
||||
// Create a new transport
|
||||
m_currentTransport = m_transports.value(m_currentUrl.scheme())->createTransport();
|
||||
QObject::connect(m_currentTransport, &NymeaTransportInterface::sslErrors, this, &NymeaConnection::onSslErrors);
|
||||
QObject::connect(m_currentTransport, &NymeaTransportInterface::error, this, &NymeaConnection::onError);
|
||||
QObject::connect(m_currentTransport, &NymeaTransportInterface::connected, this, &NymeaConnection::onConnected);
|
||||
QObject::connect(m_currentTransport, &NymeaTransportInterface::disconnected, this, &NymeaConnection::onDisconnected);
|
||||
|
||||
// signal forwarding
|
||||
QObject::connect(m_currentTransport, &NymeaTransportInterface::dataReady, this, &NymeaConnection::dataAvailable);
|
||||
|
||||
qDebug() << "Should connect to url" << m_currentUrl;
|
||||
// Load any certificate we might have for this url
|
||||
QByteArray pem;
|
||||
if (loadPem(m_currentUrl, pem)) {
|
||||
qDebug() << "Loaded SSL certificate for" << m_currentUrl.host();
|
||||
QList<QSslError> expectedSslErrors;
|
||||
expectedSslErrors.append(QSslError::HostNameMismatch);
|
||||
expectedSslErrors.append(QSslError(QSslError::SelfSignedCertificate, QSslCertificate(pem)));
|
||||
m_currentTransport->ignoreSslErrors(expectedSslErrors);
|
||||
}
|
||||
|
||||
qDebug() << "Connecting to:" << m_currentUrl;
|
||||
return m_currentTransport->connect(m_currentUrl);
|
||||
}
|
||||
|
||||
|
|
@ -48,19 +61,27 @@ void NymeaConnection::disconnect()
|
|||
m_currentTransport->disconnect();
|
||||
}
|
||||
|
||||
void NymeaConnection::acceptCertificate(const QString &url, const QByteArray &fingerprint)
|
||||
void NymeaConnection::acceptCertificate(const QString &url, const QByteArray &pem)
|
||||
{
|
||||
QSettings settings;
|
||||
settings.beginGroup("acceptedCertificates");
|
||||
settings.setValue(QUrl(url).host(), fingerprint);
|
||||
settings.endGroup();
|
||||
storePem(url, pem);
|
||||
}
|
||||
|
||||
bool NymeaConnection::isTrusted(const QString &url)
|
||||
{
|
||||
// Do we have a legacy fingerprint
|
||||
QSettings settings;
|
||||
settings.beginGroup("acceptedCertificates");
|
||||
return settings.contains(QUrl(url).host());
|
||||
if (settings.contains(QUrl(url).host())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do we have a PEM file?
|
||||
QByteArray pem;
|
||||
if (loadPem(url, pem)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NymeaConnection::connected()
|
||||
|
|
@ -103,8 +124,15 @@ 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) {
|
||||
qDebug() << "have a self signed certificate." << error.certificate() << error.certificate().issuerInfoAttributes();
|
||||
qDebug() << "have a self signed certificate." << error.certificate();
|
||||
|
||||
// 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(m_currentUrl.host()).toByteArray();
|
||||
|
|
@ -119,8 +147,20 @@ void NymeaConnection::onSslErrors(const QList<QSslError> &errors)
|
|||
certificateFingerprint.append(digest.mid(i,1).toHex().toUpper());
|
||||
}
|
||||
|
||||
// Check old style fingerprint storage
|
||||
if (storedFingerPrint == certificateFingerprint) {
|
||||
qDebug() << "This fingerprint is known to us.";
|
||||
ignoredErrors.append(error);
|
||||
|
||||
// Update the config to use the new system:
|
||||
storePem(m_currentUrl, error.certificate().toPem());
|
||||
|
||||
// Check new style PEM storage
|
||||
} else if (loadPem(m_currentUrl, 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 {
|
||||
QStringList info;
|
||||
info << tr("Common Name:") << error.certificate().issuerInfo(QSslCertificate::CommonName);
|
||||
|
|
@ -132,14 +172,18 @@ void NymeaConnection::onSslErrors(const QList<QSslError> &errors)
|
|||
// info << tr("Name Qualifier:")<< error.certificate().issuerInfo(QSslCertificate::DistinguishedNameQualifier);
|
||||
// info << tr("Email:")<< error.certificate().issuerInfo(QSslCertificate::EmailAddress);
|
||||
|
||||
emit verifyConnectionCertificate(m_currentUrl.toString(), info, certificateFingerprint);
|
||||
emit verifyConnectionCertificate(m_currentUrl.toString(), info, certificateFingerprint, error.certificate().toPem());
|
||||
}
|
||||
} else {
|
||||
// Reject the connection on all other errors...
|
||||
qDebug() << "SSL Error:" << error.errorString() << error.certificate();
|
||||
}
|
||||
}
|
||||
m_currentTransport->ignoreSslErrors(ignoredErrors);
|
||||
if (ignoredErrors == errors) {
|
||||
// Note, due to a workaround in the WebSocketTransport we must not call this
|
||||
// unless we've handled all the errors or the websocket will ignore unhandled errors too...
|
||||
m_currentTransport->ignoreSslErrors(ignoredErrors);
|
||||
}
|
||||
}
|
||||
|
||||
void NymeaConnection::onError(QAbstractSocket::SocketError error)
|
||||
|
|
@ -170,6 +214,33 @@ void NymeaConnection::onDisconnected()
|
|||
emit connectedChanged(false);
|
||||
}
|
||||
|
||||
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/");
|
||||
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()) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class NymeaConnection : public QObject
|
|||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
|
||||
Q_PROPERTY(QString url READ url NOTIFY connectedChanged)
|
||||
Q_PROPERTY(QString url READ url NOTIFY currentUrlChanged)
|
||||
Q_PROPERTY(QString hostAddress READ hostAddress NOTIFY connectedChanged)
|
||||
Q_PROPERTY(QString bluetoothAddress READ bluetoothAddress NOTIFY connectedChanged)
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ public:
|
|||
|
||||
Q_INVOKABLE bool connect(const QString &url);
|
||||
Q_INVOKABLE void disconnect();
|
||||
Q_INVOKABLE void acceptCertificate(const QString &url, const QByteArray &fingerprint);
|
||||
Q_INVOKABLE void acceptCertificate(const QString &url, const QByteArray &pem);
|
||||
Q_INVOKABLE bool isTrusted(const QString &url);
|
||||
|
||||
bool connected();
|
||||
|
|
@ -37,7 +37,8 @@ public:
|
|||
void sendData(const QByteArray &data);
|
||||
|
||||
signals:
|
||||
void verifyConnectionCertificate(const QString &url, const QStringList &issuerInfo, const QByteArray &fingerprint);
|
||||
void currentUrlChanged();
|
||||
void verifyConnectionCertificate(const QString &url, const QStringList &issuerInfo, const QByteArray &fingerprint, const QByteArray &pem);
|
||||
void connectedChanged(bool connected);
|
||||
void connectionError(const QString &error);
|
||||
void dataAvailable(const QByteArray &data);
|
||||
|
|
@ -49,6 +50,8 @@ private slots:
|
|||
void onDisconnected();
|
||||
|
||||
private:
|
||||
bool storePem(const QUrl &host, const QByteArray &pem);
|
||||
bool loadPem(const QUrl &host, QByteArray &pem);
|
||||
|
||||
private:
|
||||
QHash<QString, NymeaTransportInterfaceFactory*> m_transports;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
WebsocketTransport::WebsocketTransport(QObject *parent) :
|
||||
NymeaTransportInterface(parent)
|
||||
{
|
||||
m_socket = new QWebSocket(QCoreApplication::applicationName(), QWebSocketProtocol::Version13, this);
|
||||
m_socket = new QWebSocket(QCoreApplication::applicationName(), QWebSocketProtocol::VersionLatest, this);
|
||||
|
||||
QObject::connect(m_socket, &QWebSocket::connected, this, &WebsocketTransport::connected);
|
||||
QObject::connect(m_socket, &QWebSocket::disconnected, this, &WebsocketTransport::disconnected);
|
||||
|
|
@ -73,7 +73,14 @@ void WebsocketTransport::sendData(const QByteArray &data)
|
|||
|
||||
void WebsocketTransport::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
m_socket->ignoreSslErrors(errors);
|
||||
// FIXME: We really should provide the exact errors here, like we do on other transports,
|
||||
// however, for some reason I just fail to connect to any wss:// socket if I specify the
|
||||
// errors. It would only continue if calling it without errors parameter...
|
||||
|
||||
// m_socket->ignoreSslErrors(errors);
|
||||
|
||||
Q_UNUSED(errors)
|
||||
m_socket->ignoreSslErrors();
|
||||
}
|
||||
|
||||
void WebsocketTransport::onTextMessageReceived(const QString &data)
|
||||
|
|
|
|||
|
|
@ -86,13 +86,15 @@ Page {
|
|||
secondaryIconName: !model.online ? "../images/cloud-error.svg" : ""
|
||||
|
||||
onClicked: {
|
||||
var page = pageStack.push(Qt.resolvedUrl("../connection/ConnectingPage.qml"))
|
||||
page.cancel.connect(function() {
|
||||
Engine.connection.disconnect()
|
||||
pageStack.pop(root, StackView.Immediate);
|
||||
pageStack.push(discoveryPage)
|
||||
})
|
||||
Engine.connection.connect("cloud://" + model.id)
|
||||
if (!Engine.connection.connected) {
|
||||
var page = pageStack.push(Qt.resolvedUrl("../connection/ConnectingPage.qml"))
|
||||
page.cancel.connect(function() {
|
||||
Engine.connection.disconnect()
|
||||
pageStack.pop(root, StackView.Immediate);
|
||||
pageStack.push(discoveryPage)
|
||||
})
|
||||
Engine.connection.connect("cloud://" + model.id)
|
||||
}
|
||||
}
|
||||
|
||||
onDeleteClicked: {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ Page {
|
|||
target: Engine.connection
|
||||
onVerifyConnectionCertificate: {
|
||||
print("verify cert!")
|
||||
var popup = certDialogComponent.createObject(app, {url: url, issuerInfo: issuerInfo, fingerprint: fingerprint});
|
||||
var popup = certDialogComponent.createObject(root, {url: url, issuerInfo: issuerInfo, fingerprint: fingerprint, pem: pem});
|
||||
popup.open();
|
||||
}
|
||||
onConnectionError: {
|
||||
|
|
@ -331,6 +331,7 @@ Page {
|
|||
property string url
|
||||
property var fingerprint
|
||||
property var issuerInfo
|
||||
property var pem
|
||||
|
||||
readonly property bool hasOldFingerprint: Engine.connection.isTrusted(url)
|
||||
|
||||
|
|
@ -419,9 +420,8 @@ Page {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
onAccepted: {
|
||||
Engine.connection.acceptCertificate(certDialog.url, certDialog.fingerprint)
|
||||
Engine.connection.acceptCertificate(certDialog.url, certDialog.pem)
|
||||
root.connectToHost(certDialog.url)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue