enabled delete account again

This commit is contained in:
Michael Zanetti 2018-09-05 17:33:56 +02:00
parent 303527b75b
commit 59cb232744
10 changed files with 145 additions and 55 deletions

View File

@ -74,9 +74,23 @@ AWSClient::AWSClient(QObject *parent) : QObject(parent),
config.apiEndpoint = "testapi-cloud.guh.io";
m_configs.append(config);
// Marantec environment
config.clientId = "7rf6da8pcqi1qi8tp1evf933h2";
config.poolId = "eu-west-1_d4DdcqKJ8";
config.identityPoolId = "eu-west-1:d32f6d94-caae-4f08-a193-f9fba8652646";
// Generating certificates is not supported for the Marantec environment
config.certificateEndpoint = "";
config.certificateApiKey = "";
config.certificateVendorId = "";
config.mqttEndpoint = "a27q7a2x15m8h3.iot.eu-west-1.amazonaws.com";
config.region = "eu-west-1";
config.apiEndpoint = "api-cloud.guh.io";
m_configs.append(config);
QSettings settings;
settings.beginGroup("cloud");
m_username = settings.value("username").toString();
m_userId = settings.value("userId").toString();
m_password = settings.value("password").toString();
m_accessToken = settings.value("accessToken").toByteArray();
m_accessTokenExpiry = settings.value("accessTokenExpiry").toDateTime();
@ -94,7 +108,7 @@ AWSClient::AWSClient(QObject *parent) : QObject(parent),
bool AWSClient::isLoggedIn() const
{
return !m_username.isEmpty() && !m_password.isEmpty();
return !m_userId.isEmpty() && !m_username.isEmpty() && !m_password.isEmpty();
}
QString AWSClient::username() const
@ -181,18 +195,30 @@ void AWSClient::login(const QString &username, const QString &password)
m_idToken = authenticationResult.value("IdToken").toByteArray();
m_refreshToken = authenticationResult.value("RefreshToken").toByteArray();
qDebug() << "AWS ID token" << m_idToken;
QList<QByteArray> jwtParts = m_idToken.split('.');
if (jwtParts.count() != 3) {
qWarning() << "JWT token doesn't have 3 parts";
return;
}
// qDebug() << "decoded header:" << QByteArray::fromBase64(jwtParts.at(0));
// qDebug() << "decoded payload:" << QByteArray::fromBase64(jwtParts.at(1));
QJsonDocument tokenPayloadJsonDoc = QJsonDocument::fromJson(QByteArray::fromBase64(jwtParts.at(1)));
m_userId = tokenPayloadJsonDoc.toVariant().toMap().value("cognito:username").toByteArray();
QSettings settings;
settings.remove("cloud");
settings.beginGroup("cloud");
settings.setValue("username", m_username);
settings.setValue("userId", m_userId);
settings.setValue("password", m_password);
settings.setValue("accessToken", m_accessToken);
settings.setValue("accessTokenExpiry", m_accessTokenExpiry);
settings.setValue("idToken", m_idToken);
settings.setValue("refreshToken", m_refreshToken);
qDebug() << "AWS login successful";// << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
qDebug() << "AWS login successful. Userid:" << m_userId;// << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
emit isLoggedInChanged();
qDebug() << "Getting cognito ID";
@ -202,8 +228,10 @@ void AWSClient::login(const QString &username, const QString &password)
void AWSClient::logout()
{
m_userId.clear();
m_username.clear();
m_password.clear();
m_devices->clear();
QSettings settings;
settings.remove("cloud");
emit isLoggedInChanged();
@ -441,7 +469,7 @@ void AWSClient::deleteAccount()
}
qDebug() << "Deleting account";
QUrl url(QString("https://%1/users/profiles/%2").arg(m_configs.at(m_usedConfigIndex).apiEndpoint).arg(m_username));
QUrl url(QString("https://%1/users/profiles/%2").arg(m_configs.at(m_usedConfigIndex).apiEndpoint).arg(m_userId));
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("x-api-idToken", m_idToken);
@ -458,14 +486,17 @@ void AWSClient::deleteAccount()
QByteArray data = reply->readAll();
if (reply->error() != QNetworkReply::NoError) {
qWarning() << "Error deleting cloud user account:" << reply->error() << reply->errorString() << qUtf8Printable(data);
emit deleteAccountResult(LoginErrorUnknownError);
return;
}
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
qWarning() << "Failed to parse JSON from server" << error.errorString() << qUtf8Printable(data);
emit deleteAccountResult(LoginErrorUnknownError);
return;
}
emit deleteAccountResult(LoginErrorNoError);
logout();
qDebug() << "Account deleted" << data;
});
@ -489,9 +520,11 @@ void AWSClient::unpairDevice(const QString &boxId)
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("x-api-idToken", m_idToken);
m_devices->setBusy(true);
QNetworkReply *reply = m_nam->deleteResource(request);
connect(reply, &QNetworkReply::finished, this, [this, reply, boxId]() {
reply->deleteLater();
m_devices->setBusy(false);
QByteArray data = reply->readAll();
if (reply->error() != QNetworkReply::NoError) {
qWarning() << "Error unpairing cloud device:" << reply->error() << reply->errorString() << qUtf8Printable(data);
@ -787,9 +820,11 @@ void AWSClient::fetchDevices()
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("x-api-idToken", m_idToken);
m_devices->setBusy(true);
QNetworkReply *reply = m_nam->get(request);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
m_devices->setBusy(false);
QByteArray data = reply->readAll();
if (reply->error() != QNetworkReply::NoError) {
qWarning() << "Error fetching cloud devices:" << reply->error() << reply->errorString() << qUtf8Printable(data);
@ -931,6 +966,19 @@ QHash<int, QByteArray> AWSDevices::roleNames() const
return roles;
}
bool AWSDevices::busy() const
{
return m_busy;
}
void AWSDevices::setBusy(bool busy)
{
if (m_busy != busy) {
m_busy = busy;
emit busyChanged();
}
}
AWSDevice *AWSDevices::getDevice(const QString &uuid) const
{
for (int i = 0; i < m_list.count(); i++) {
@ -977,6 +1025,16 @@ void AWSDevices::remove(const QString &uuid)
emit countChanged();
}
void AWSDevices::clear()
{
beginResetModel();
while (m_list.count() > 0) {
m_list.takeFirst()->deleteLater();
}
endResetModel();
emit countChanged();
}
AWSDevice::AWSDevice(const QString &id, const QString &name, bool online, QObject *parent):
QObject (parent),
m_id(id),

View File

@ -33,6 +33,7 @@ private:
class AWSDevices: public QAbstractListModel {
Q_OBJECT
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
public:
enum Roles {
RoleName,
@ -43,14 +44,19 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
bool busy() const;
void setBusy(bool busy);
Q_INVOKABLE AWSDevice* getDevice(const QString &uuid) const;
Q_INVOKABLE AWSDevice* get(int index) const;
void insert(AWSDevice *device);
void remove(const QString &uuid);
void clear();
signals:
void countChanged();
void busyChanged();
private:
QList<AWSDevice*> m_list;
bool m_busy = false;
};
class AWSConfiguration {
@ -127,6 +133,7 @@ signals:
void confirmationResult(LoginError error);
void forgotPasswordResult(LoginError error);
void confirmForgotPasswordResult(LoginError error);
void deleteAccountResult(LoginError error);
void isLoggedInChanged();
void confirmationPendingChanged();

View File

@ -127,14 +127,14 @@ Engine::Engine(QObject *parent) :
connect(m_aws, &AWSClient::devicesFetched, this, [this]() {
if (m_jsonRpcClient->connected() && m_jsonRpcClient->cloudConnectionState() == JsonRpcClient::CloudConnectionStateConnected) {
if (m_aws->awsDevices()->getDevice(m_jsonRpcClient->serverUuid()) == nullptr) {
m_jsonRpcClient->setupRemoteAccess(m_aws->idToken(), m_aws->cognitoIdentityId());
m_jsonRpcClient->setupRemoteAccess(m_aws->idToken(), m_aws->userId());
}
}
});
connect(m_jsonRpcClient, &JsonRpcClient::connectedChanged, this, [this]() {
if (m_jsonRpcClient->connected() && m_jsonRpcClient->cloudConnectionState() == JsonRpcClient::CloudConnectionStateConnected) {
if (m_aws->awsDevices()->getDevice(m_jsonRpcClient->serverUuid()) == nullptr) {
m_jsonRpcClient->setupRemoteAccess(m_aws->idToken(), m_aws->cognitoIdentityId());
m_jsonRpcClient->setupRemoteAccess(m_aws->idToken(), m_aws->userId());
}
}
});

View File

@ -96,7 +96,7 @@ void JsonRpcClient::setNotificationsEnabledResponse(const QVariantMap &params)
void JsonRpcClient::notificationReceived(const QVariantMap &data)
{
//JsonRpcClient: Notification received QMap(("id", QVariant(double, 2))("notification", QVariant(QString, "JSONRPC.PushButtonAuthFinished"))("params", QVariant(QVariantMap, QMap(("success", QVariant(bool, true))("token", QVariant(QString, "FJPaAJ8FEtrqcC+/s0s/lAcDubz0OyEtwbRsyFIWM9c="))("transactionId", QVariant(double, 2))))))
qDebug() << "Notification received:" << data;
if (data.value("notification").toString() == "JSONRPC.PushButtonAuthFinished") {
qDebug() << "Push button auth finished.";
if (data.value("params").toMap().value("transactionId").toInt() != m_pendingPushButtonTransaction) {

View File

@ -18,7 +18,7 @@ Page {
ListElement { iconSource: "../images/share.svg"; text: qsTr("Configure things"); page: "EditDevicesPage.qml" }
ListElement { iconSource: "../images/magic.svg"; text: qsTr("Magic"); page: "MagicPage.qml" }
ListElement { iconSource: "../images/stock_application.svg"; text: qsTr("App settings"); page: "appsettings/AppSettingsPage.qml" }
ListElement { iconSource: "../images/settings.svg"; text: qsTr("System settings"); page: "SettingsPage.qml" }
ListElement { iconSource: "../images/settings.svg"; text: qsTr("Box settings"); page: "SettingsPage.qml" }
}
onClicked: {
@ -173,39 +173,38 @@ Page {
}
}
TabBar {
id: tabBar
Layout.fillWidth: true
Material.elevation: 3
currentIndex: settings.currentMainViewIndex
position: TabBar.Footer
Layout.preferredHeight: 70 + (app.landscape ?
((systemProductType === "ios" && Screen.height === 375) ? -10 : -20) :
(systemProductType === "ios" && Screen.height === 812) ? 14 : 0)
}
footer: TabBar {
id: tabBar
Material.elevation: 3
currentIndex: settings.currentMainViewIndex
position: TabBar.Footer
implicitHeight: 70 + (app.landscape ?
((systemProductType === "ios" && Screen.height === 375) ? -10 : -20) :
(systemProductType === "ios" && Screen.height === 812) ? 14 : 0)
// FIXME: All this can go away when we require Controls 2.3 (Qt 5.10) or greater as TabBar got a major rework there.
// Ideally we'd just list the 3 items and set visible to false if the server version isn't good enough but TabBar
// has troubles dealing with that. For now, let's manually fill it and use a timer to initialize the currentIndex.
Component.onCompleted: {
var pi = 0;
if (Engine.jsonRpcClient.ensureServerVersion(1.6)) {
tabEntryComponent.createObject(tabBar, {text: qsTr("Favorites"), iconSource: "../images/starred.svg", pageIndex: pi++})
}
tabEntryComponent.createObject(tabBar, {text: qsTr("Things"), iconSource: "../images/share.svg", pageIndex: pi++})
tabEntryComponent.createObject(tabBar, {text: qsTr("Scenes"), iconSource: "../images/slideshow.svg", pageIndex: pi++})
initTimer.start()
// FIXME: All this can go away when we require Controls 2.3 (Qt 5.10) or greater as TabBar got a major rework there.
// Ideally we'd just list the 3 items and set visible to false if the server version isn't good enough but TabBar
// has troubles dealing with that. For now, let's manually fill it and use a timer to initialize the currentIndex.
Component.onCompleted: {
var pi = 0;
if (Engine.jsonRpcClient.ensureServerVersion(1.6)) {
tabEntryComponent.createObject(tabBar, {text: qsTr("Favorites"), iconSource: "../images/starred.svg", pageIndex: pi++})
}
Timer { id: initTimer; interval: 1; repeat: false; onTriggered: tabBar.currentIndex = Qt.binding(function() {return settings.currentMainViewIndex;})}
tabEntryComponent.createObject(tabBar, {text: qsTr("Things"), iconSource: "../images/share.svg", pageIndex: pi++})
tabEntryComponent.createObject(tabBar, {text: qsTr("Scenes"), iconSource: "../images/slideshow.svg", pageIndex: pi++})
initTimer.start()
}
Timer { id: initTimer; interval: 1; repeat: false; onTriggered: tabBar.currentIndex = Qt.binding(function() {return settings.currentMainViewIndex;})}
Component {
id: tabEntryComponent
MainPageTabButton {
property int pageIndex: 0
Component {
id: tabEntryComponent
MainPageTabButton {
property int pageIndex: 0
// height: tabBar.height
onClicked: settings.currentMainViewIndex = pageIndex
alignment: app.landscape ? Qt.Horizontal : Qt.Vertical
}
onClicked: settings.currentMainViewIndex = pageIndex
alignment: app.landscape ? Qt.Horizontal : Qt.Vertical
}
}
}

View File

@ -8,7 +8,7 @@ import "components"
Page {
id: root
header: GuhHeader {
text: qsTr("System settings")
text: qsTr("Box settings")
backButtonVisible: true
onBackPressed: pageStack.pop()
}

View File

@ -124,7 +124,7 @@ Page {
}
MeaListItemDelegate {
Layout.fillWidth: true
visible: settings.showHiddenOption
visible: settings.showHiddenOptions
text: qsTr("Developer options")
iconName: "../images/configure.svg"
onClicked: pageStack.push(Qt.resolvedUrl("DeveloperOptionsPage.qml"))

View File

@ -25,10 +25,20 @@ Page {
Engine.awsClient.fetchDevices();
}
}
onDeleteAccountResult: {
busyOverlay.shown = false;
if (error !== AWSClient.LoginErrorNoError) {
var errorDialog = Qt.createComponent(Qt.resolvedUrl("../components/ErrorDialog.qml"));
var text = qsTr("Sorry, an error happened removing the account. Please try again later.");
var popup = errorDialog.createObject(app, {text: text})
popup.open()
return;
}
}
}
ColumnLayout {
anchors { left: parent.left; top: parent.top; right: parent.right }
anchors.fill: parent
visible: Engine.awsClient.isLoggedIn
Label {
Layout.fillWidth: true
@ -64,6 +74,7 @@ Page {
Layout.fillWidth: true
Layout.fillHeight: true
model: Engine.awsClient.awsDevices
clip: true
delegate: MeaListItemDelegate {
width: parent.width
text: model.name
@ -77,6 +88,11 @@ Page {
Engine.awsClient.unpairDevice(model.id);
}
}
BusyIndicator {
anchors.centerIn: parent
visible: Engine.awsClient.awsDevices.busy
}
}
}
@ -84,28 +100,29 @@ Page {
id: logoutDialog
title: qsTr("Goodbye")
// Deleting user profile not working in cloud yet
// text: qsTr("Sorry to see you go. If you log out you won't be able to connect to %1 boxes remotely any more. However, you can come back any time, we'll keep your user account. If you whish to completely delete your account and all the data associated with it, check the box below before hitting ok.").arg(app.systemName)
text: qsTr("Sorry to see you go. If you log out you won't be able to connect to %1 boxes remotely any more. However, you can come back any time.").arg(app.systemName)
text: qsTr("Sorry to see you go. If you log out you won't be able to connect to %1 boxes remotely any more. However, you can come back any time, we'll keep your user account. If you whish to completely delete your account and all the data associated with it, check the box below before hitting ok.").arg(app.systemName)
// text: qsTr("Sorry to see you go. If you log out you won't be able to connect to %1 boxes remotely any more. However, you can come back any time.").arg(app.systemName)
headerIcon: "../images/dialog-warning-symbolic.svg"
standardButtons: Dialog.Cancel | Dialog.Ok
// RowLayout {
// CheckBox {
// id: deleteCheckbox
// }
// Label {
// Layout.fillWidth: true
// wrapMode: Text.WordWrap
// text: qsTr("Delete my account")
// }
// }
RowLayout {
CheckBox {
id: deleteCheckbox
}
Label {
Layout.fillWidth: true
wrapMode: Text.WordWrap
text: qsTr("Delete my account")
}
}
onAccepted: {
// if (deleteCheckbox.checked) {
// Engine.awsClient.deleteAccount()
// } else {
if (deleteCheckbox.checked) {
busyOverlay.shown = true;
Engine.awsClient.deleteAccount()
} else {
Engine.awsClient.logout()
// }
}
}
}

View File

@ -25,7 +25,7 @@ Page {
ComboBox {
currentIndex: app.settings.cloudEnvironment
model: [qsTr("Community"), qsTr("Testing")]
model: [qsTr("Community"), qsTr("Testing"), qsTr("Marantec")]
onActivated: {
app.settings.cloudEnvironment = index;
}

View File

@ -18,8 +18,12 @@ Page {
Connections {
target: Engine.jsonRpcClient
onCloudConnectionStateChanged: {
print("cloud connection state changed", Engine.jsonRpcClient.cloudConnectionState)
if (Engine.jsonRpcClient.cloudConnectionState == JsonRpcClient.CloudConnectionStateConnected) {
d.deploymentStarted = false;
if (Engine.awsClient.awsDevices.getDevice(Engine.jsonRpcClient.serverUuid) === null) {
Engine.jsonRpcClient.setupRemoteAccess(Engine.awsClient.idToken, Engine.awsClient.userId)
}
}
}
}
@ -37,6 +41,11 @@ Page {
wrapMode: Text.WordWrap
}
// Button {
// text: "pair"
// onClicked: Engine.jsonRpcClient.setupRemoteAccess(Engine.awsClient.idToken, Engine.awsClient.userId)
// }
SwitchDelegate {
Layout.fillWidth: true
text: qsTr("Cloud connection enabled")