From 914a6bae00ecd90e0868ffba2f3897a648152533 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 31 Aug 2017 12:18:38 -0700 Subject: [PATCH 01/14] Commerce: Wallet Passphrase Modal --- .../hifi/commerce/wallet/PassphraseModal.qml | 232 ++++++++++++++++++ .../commerce/wallet/PassphraseSelection.qml | 4 - .../qml/hifi/commerce/wallet/Wallet.qml | 32 +++ interface/src/commerce/QmlCommerce.cpp | 1 + interface/src/commerce/QmlCommerce.h | 1 + interface/src/commerce/Wallet.cpp | 7 +- interface/src/commerce/Wallet.h | 3 +- scripts/system/commerce/wallet.js | 1 + 8 files changed, 273 insertions(+), 8 deletions(-) create mode 100644 interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml new file mode 100644 index 0000000000..71cb61686e --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -0,0 +1,232 @@ +// +// PassphraseModal.qml +// qml/hifi/commerce/wallet +// +// PassphraseModal +// +// Created by Zach Fox on 2017-08-18 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 as Hifi +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "../../../styles-uit" +import "../../../controls-uit" as HifiControlsUit +import "../../../controls" as HifiControls + +// references XXX from root context + +Item { + HifiConstants { id: hifi; } + + id: root; + Hifi.QmlCommerce { + id: commerce; + + onSecurityImageResult: { + passphraseModalSecurityImage.source = ""; + passphraseModalSecurityImage.source = "image://security/securityImage"; + } + } + + // This object is always used in a popup. + // This MouseArea is used to prevent a user from being + // able to click on a button/mouseArea underneath the popup. + MouseArea { + anchors.fill: parent; + propagateComposedEvents: false; + } + + // This will cause a bug -- if you bring up passphrase selection in HUD mode while + // in HMD while having HMD preview enabled, then move, then finish passphrase selection, + // HMD preview will stay off. + // TODO: Fix this unlikely bug + onVisibleChanged: { + if (visible) { + passphraseField.focus = true; + sendSignalToWallet({method: 'disableHmdPreview'}); + } else { + sendSignalToWallet({method: 'maybeEnableHmdPreview'}); + } + } + + // Background rectangle + Rectangle { + anchors.fill: parent; + color: "black"; + opacity: 0.9; + } + + Rectangle { + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + height: 220; + color: hifi.colors.baseGray; + + RalewaySemiBold { + id: instructionsText; + text: "Enter Wallet Passphrase"; + size: 16; + anchors.top: parent.top; + anchors.topMargin: 30; + anchors.left: parent.left; + anchors.leftMargin: 16; + width: passphraseField.width; + height: paintedHeight; + // Style + color: hifi.colors.faintGray; + // Alignment + horizontalAlignment: Text.AlignLeft; + } + + HifiControlsUit.TextField { + id: passphraseField; + anchors.top: instructionsText.bottom; + anchors.topMargin: 4; + anchors.left: instructionsText.left; + width: 280; + height: 50; + echoMode: TextInput.Password; + placeholderText: "passphrase"; + + onFocusChanged: { + if (focus) { + sendSignalToWallet({method: 'walletSetup_raiseKeyboard'}); + } else if (!passphraseFieldAgain.focus) { + sendSignalToWallet({method: 'walletSetup_lowerKeyboard'}); + } + } + + MouseArea { + anchors.fill: parent; + onClicked: { + parent.focus = true; + sendSignalToWallet({method: 'walletSetup_raiseKeyboard'}); + } + } + + onAccepted: { + //commerce.submitWalletPassphrase(passphraseField.text); + } + } + + // Show passphrase text + HifiControlsUit.CheckBox { + id: showPassphrase; + colorScheme: hifi.colorSchemes.dark; + anchors.left: passphraseField.left; + anchors.top: passphraseField.bottom; + anchors.topMargin: 8; + height: 30; + text: "Show passphrase as plain text"; + boxSize: 24; + onClicked: { + passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password; + } + } + + // Security Image + Item { + id: securityImageContainer; + // Anchors + anchors.top: instructionsText.top; + anchors.left: passphraseField.right; + anchors.leftMargin: 12; + anchors.right: parent.right; + Image { + id: passphraseModalSecurityImage; + anchors.top: parent.top; + anchors.horizontalCenter: parent.horizontalCenter; + height: 75; + width: height; + fillMode: Image.PreserveAspectFit; + mipmap: true; + source: "image://security/securityImage"; + cache: false; + onVisibleChanged: { + commerce.getSecurityImage(); + } + } + Image { + id: passphraseModalSecurityImageOverlay; + source: "images/lockOverlay.png"; + width: passphraseModalSecurityImage.width * 0.45; + height: passphraseModalSecurityImage.height * 0.45; + anchors.bottom: passphraseModalSecurityImage.bottom; + anchors.right: passphraseModalSecurityImage.right; + mipmap: true; + opacity: 0.9; + } + // "Security image" text below pic + RalewayRegular { + text: "security image"; + // Text size + size: 12; + // Anchors + anchors.top: passphraseModalSecurityImage.bottom; + anchors.topMargin: 4; + anchors.left: securityImageContainer.left; + anchors.right: securityImageContainer.right; + height: paintedHeight; + // Style + color: hifi.colors.faintGray; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + } + + // + // ACTION BUTTONS START + // + Item { + id: passphrasePopupActionButtonsContainer; + // Size + width: root.width; + height: 50; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 10; + + // "Cancel" button + HifiControlsUit.Button { + id: cancelPassphraseInputButton; + color: hifi.buttons.black; + colorScheme: hifi.colorSchemes.dark; + anchors.top: parent.top; + height: 40; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: parent.width/2 - anchors.leftMargin*2; + text: "Cancel" + onClicked: { + sendSignalToWallet({method: 'passphrasePopup_cancelClicked'}); + } + } + + // "Submit" button + HifiControlsUit.Button { + id: submitPassphraseInputButton; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.top: parent.top; + height: 40; + anchors.right: parent.right; + anchors.rightMargin: 20; + width: parent.width/2 - anchors.rightMargin*2; + text: "Submit" + onClicked: { + //commerce.submitWalletPassphrase(passphraseField.text); + } + } + } + } + + signal sendSignalToWallet(var msg); +} diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml index 39d07315d5..2bad454b63 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml @@ -58,10 +58,6 @@ Item { } } - SecurityImageModel { - id: gridModel; - } - HifiControlsUit.TextField { id: passphraseField; anchors.top: parent.top; diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 53838fa58c..42760abacd 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -62,6 +62,16 @@ Rectangle { root.activeView = "walletHome"; } } + + onWalletAuthenticatedStatus: { + if (!isAuthenticated && !passphraseModal.visible) { + passphraseModal.visible = true; + } else if (isAuthenticated && passphraseModal.visible) { + passphraseModal.visible = false; + commerce.getSecurityImage(); + commerce.getKeyFilePathIfExists(); + } + } } SecurityImageModel { @@ -218,6 +228,28 @@ Rectangle { } } + PassphraseModal { + id: passphraseModal; + z: 998; + //visible: false; + anchors.top: titleBarContainer.bottom; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + + Connections { + onSendSignalToWallet: { + if (msg.method === 'walletSetup_raiseKeyboard') { + root.keyboardRaised = true; + } else if (msg.method === 'walletSetup_lowerKeyboard') { + root.keyboardRaised = false; + } else { + sendToScript(msg); + } + } + } + } + NotSetUp { id: notSetUp; visible: root.activeView === "notSetUp"; diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 655f228672..2f424b7cc8 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -27,6 +27,7 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) { connect(wallet.data(), &Wallet::securityImageResult, this, &QmlCommerce::securityImageResult); connect(ledger.data(), &Ledger::historyResult, this, &QmlCommerce::historyResult); connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult); + connect(wallet.data(), &Wallet::walletAuthenticatedStatus, this, &QmlCommerce::walletAuthenticatedStatus); } void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) { diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index deb11b7714..a9eca34242 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -38,6 +38,7 @@ signals: void passphraseSetupStatusResult(bool passphraseIsSetup); void historyResult(QJsonObject result); void keyFilePathIfExistsResult(const QString& path); + void walletAuthenticatedStatus(bool isAuthenticated); protected: Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = ""); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 32852602d7..569e0f9110 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -55,13 +55,14 @@ QString imageFilePath() { // use the cached _passphrase if it exists, otherwise we need to prompt int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) { // just return a hardcoded pwd for now - auto passphrase = DependencyManager::get()->getPassphrase(); + auto wallet = DependencyManager::get(); + auto passphrase = wallet->getPassphrase(); if (passphrase) { strcpy(password, passphrase->toLocal8Bit().constData()); + emit wallet->walletAuthenticatedStatus(true); return static_cast(passphrase->size()); } else { - // ok gotta bring up modal dialog... But right now lets just - // just keep it empty + emit wallet->walletAuthenticatedStatus(false); return 0; } } diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 4acd913181..24575c5ca4 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -43,8 +43,9 @@ public: void reset(); signals: - void securityImageResult(bool exists) ; + void securityImageResult(bool exists); void keyFilePathIfExistsResult(const QString& path); + void walletAuthenticatedStatus(bool isAuthenticated); private: QStringList _publicKeys{}; diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index a7b7b50379..5f07c4cbe7 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -56,6 +56,7 @@ var isHmdPreviewDisabled = true; function fromQml(message) { switch (message.method) { + case 'passphrasePopup_cancelClicked': case 'walletSetup_cancelClicked': case 'needsLogIn_cancelClicked': tablet.gotoHomeScreen(); From 21a73b2f3989a13fe3bd0e67d18732b9acf405cb Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 31 Aug 2017 13:15:22 -0700 Subject: [PATCH 02/14] Changes in what I think are the right direction --- .../qml/hifi/commerce/wallet/Wallet.qml | 43 +++++++++++++------ interface/src/commerce/QmlCommerce.cpp | 16 +++++-- interface/src/commerce/QmlCommerce.h | 3 +- interface/src/commerce/Wallet.cpp | 8 ++-- interface/src/commerce/Wallet.h | 4 +- 5 files changed, 50 insertions(+), 24 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 42760abacd..04278e85a9 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -40,8 +40,32 @@ Rectangle { root.activeView = "needsLogIn"; } else if (isLoggedIn) { root.activeView = "initialize"; - commerce.getSecurityImage(); - commerce.getKeyFilePathIfExists(); + commerce.getPassphraseSetupStatus(); + } + } + + onPassphraseSetupStatusResult: { + if (!passphraseIsSetup && root.activeView !== "notSetUp") { + root.activeView = "notSetUp"; + } else if (passphraseIsSetup && root.activeView === "initialize") { + commerce.getWalletAuthenticatedStatus(); + } + } + + onWalletAuthenticatedStatusResult: { + if (!isAuthenticated && !passphraseModal.visible) { + passphraseModal.visible = true; + } else if (isAuthenticated) { + if (passphraseModal.visible) { + passphraseModal.visible = false; + } + + if (!securityImageResultReceived) { + commerce.getSecurityImage(); + } + if (!keyFilePathIfExistsResultReceived) { + commerce.getKeyFilePathIfExists(); + } } } @@ -62,16 +86,6 @@ Rectangle { root.activeView = "walletHome"; } } - - onWalletAuthenticatedStatus: { - if (!isAuthenticated && !passphraseModal.visible) { - passphraseModal.visible = true; - } else if (isAuthenticated && passphraseModal.visible) { - passphraseModal.visible = false; - commerce.getSecurityImage(); - commerce.getKeyFilePathIfExists(); - } - } } SecurityImageModel { @@ -99,7 +113,8 @@ Rectangle { if (msg.method === 'walletSetup_cancelClicked') { walletSetupLightbox.visible = false; } else if (msg.method === 'walletSetup_finished') { - root.activeView = "walletHome"; + root.activeView = "initialize"; + commerce.getPassphraseSetupStatus(); } else if (msg.method === 'walletSetup_raiseKeyboard') { root.keyboardRaised = true; } else if (msg.method === 'walletSetup_lowerKeyboard') { @@ -231,7 +246,7 @@ Rectangle { PassphraseModal { id: passphraseModal; z: 998; - //visible: false; + visible: false; anchors.top: titleBarContainer.bottom; anchors.bottom: parent.bottom; anchors.left: parent.left; diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 2f424b7cc8..ccbc37ed13 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -27,7 +27,6 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) { connect(wallet.data(), &Wallet::securityImageResult, this, &QmlCommerce::securityImageResult); connect(ledger.data(), &Ledger::historyResult, this, &QmlCommerce::historyResult); connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult); - connect(wallet.data(), &Wallet::walletAuthenticatedStatus, this, &QmlCommerce::walletAuthenticatedStatus); } void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) { @@ -76,12 +75,23 @@ void QmlCommerce::getLoginStatus() { } void QmlCommerce::setPassphrase(const QString& passphrase) { - emit passphraseSetupStatusResult(true); + auto wallet = DependencyManager::get(); + wallet->setPassphrase(passphrase); + getPassphraseSetupStatus(); } void QmlCommerce::getPassphraseSetupStatus() { - emit passphraseSetupStatusResult(false); + auto wallet = DependencyManager::get(); + // ????? WHAT DO I DO HERE + emit passphraseSetupStatusResult(wallet->getPassphraseIsCached()); } + +void QmlCommerce::getWalletAuthenticatedStatus() { + auto wallet = DependencyManager::get(); + // ????? WHAT DO I DO HERE + emit walletAuthenticatedStatusResult(wallet->getPassphraseIsCached()); +} + void QmlCommerce::getKeyFilePathIfExists() { auto wallet = DependencyManager::get(); wallet->sendKeyFilePathIfExists(); diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index a9eca34242..5561f331f9 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -38,7 +38,7 @@ signals: void passphraseSetupStatusResult(bool passphraseIsSetup); void historyResult(QJsonObject result); void keyFilePathIfExistsResult(const QString& path); - void walletAuthenticatedStatus(bool isAuthenticated); + void walletAuthenticatedStatusResult(bool isAuthenticated); protected: Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = ""); @@ -50,6 +50,7 @@ protected: Q_INVOKABLE void getLoginStatus(); Q_INVOKABLE void setPassphrase(const QString& passphrase); Q_INVOKABLE void getPassphraseSetupStatus(); + Q_INVOKABLE void getWalletAuthenticatedStatus(); Q_INVOKABLE void getKeyFilePathIfExists(); Q_INVOKABLE void reset(); }; diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 569e0f9110..cfd5b6baa5 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -55,14 +55,14 @@ QString imageFilePath() { // use the cached _passphrase if it exists, otherwise we need to prompt int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) { // just return a hardcoded pwd for now - auto wallet = DependencyManager::get(); - auto passphrase = wallet->getPassphrase(); + auto passphrase = DependencyManager::get()->getPassphrase(); if (passphrase) { strcpy(password, passphrase->toLocal8Bit().constData()); - emit wallet->walletAuthenticatedStatus(true); return static_cast(passphrase->size()); } else { - emit wallet->walletAuthenticatedStatus(false); + // Old comment below...this should never happen once we're here...what if it does? + // ok gotta bring up modal dialog... But right now lets just + // just keep it empty return 0; } } diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 24575c5ca4..cb0e318598 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -39,19 +39,19 @@ public: void setPassphrase(const QString& passphrase); QString* getPassphrase() { return _passphrase; } + bool getPassphraseIsCached() { return !(_passphrase->isEmpty()); } void reset(); signals: void securityImageResult(bool exists); void keyFilePathIfExistsResult(const QString& path); - void walletAuthenticatedStatus(bool isAuthenticated); private: QStringList _publicKeys{}; QPixmap* _securityImage { nullptr }; QByteArray _salt {"iamsalt!"}; - QString* _passphrase { new QString("pwd") }; + QString* _passphrase { new QString("") }; void updateImageProvider(); bool encryptFile(const QString& inputFilePath, const QString& outputFilePath); From ce32d2d42068c45ae99b4da58807f9a2c10f9425 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 31 Aug 2017 14:12:05 -0700 Subject: [PATCH 03/14] Checkout and Purchases --- .../qml/hifi/commerce/checkout/Checkout.qml | 54 ++++++++++++++++--- .../qml/hifi/commerce/purchases/Purchases.qml | 48 +++++++++++++++-- .../hifi/commerce/wallet/PassphraseModal.qml | 54 ++++++++++++++++--- .../qml/hifi/commerce/wallet/Wallet.qml | 10 +--- scripts/system/marketplaces/marketplaces.js | 1 + 5 files changed, 141 insertions(+), 26 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 109e357206..04dacf67c0 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -46,10 +46,38 @@ Rectangle { root.activeView = "needsLogIn"; } else if (isLoggedIn) { root.activeView = "initialize"; - commerce.getSecurityImage(); - commerce.getKeyFilePathIfExists(); - commerce.balance(); - commerce.inventory(); + commerce.getPassphraseSetupStatus(); + } + } + + onPassphraseSetupStatusResult: { + if (!passphraseIsSetup && root.activeView !== "notSetUp") { + root.activeView = "notSetUp"; + } else if (passphraseIsSetup && root.activeView === "initialize") { + commerce.getWalletAuthenticatedStatus(); + } + } + + onWalletAuthenticatedStatusResult: { + if (!isAuthenticated && !passphraseModal.visible) { + passphraseModal.visible = true; + } else if (isAuthenticated) { + if (passphraseModal.visible) { + passphraseModal.visible = false; + } + + if (!securityImageResultReceived) { + commerce.getSecurityImage(); + } + if (!keyFilePathIfExistsResultReceived) { + commerce.getKeyFilePathIfExists(); + } + if (!balanceReceived) { + commerce.balance(); + } + if (!purchasesReceived) { + commerce.inventory(); + } } } @@ -110,10 +138,6 @@ Rectangle { } } - HifiWallet.SecurityImageModel { - id: securityImageModel; - } - // // TITLE BAR START // @@ -221,7 +245,21 @@ Rectangle { } } + PassphraseModal { + id: passphraseModal; + z: 998; + visible: false; + anchors.top: titleBarContainer.bottom; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + Connections { + onSendSignalToParent: { + sendToScript(msg); + } + } + } // // "WALLET NOT SET UP" START diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index bc843a140d..f9871d1b2a 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -41,9 +41,35 @@ Rectangle { root.activeView = "needsLogIn"; } else if (isLoggedIn) { root.activeView = "initialize"; - commerce.getSecurityImage(); - commerce.getKeyFilePathIfExists(); - commerce.inventory(); + commerce.getPassphraseSetupStatus(); + } + } + + onPassphraseSetupStatusResult: { + if (!passphraseIsSetup && root.activeView !== "notSetUp") { + root.activeView = "notSetUp"; + } else if (passphraseIsSetup && root.activeView === "initialize") { + commerce.getWalletAuthenticatedStatus(); + } + } + + onWalletAuthenticatedStatusResult: { + if (!isAuthenticated && !passphraseModal.visible) { + passphraseModal.visible = true; + } else if (isAuthenticated) { + if (passphraseModal.visible) { + passphraseModal.visible = false; + } + + if (!securityImageResultReceived) { + commerce.getSecurityImage(); + } + if (!keyFilePathIfExistsResultReceived) { + commerce.getKeyFilePathIfExists(); + } + if (!purchasesReceived) { + commerce.inventory(); + } } } @@ -191,6 +217,22 @@ Rectangle { commerce.getLoginStatus(); } } + + PassphraseModal { + id: passphraseModal; + z: 998; + visible: false; + anchors.top: titleBarContainer.bottom; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + + Connections { + onSendSignalToParent: { + sendToScript(msg); + } + } + } // // "WALLET NOT SET UP" START diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index 71cb61686e..5a6df37485 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -48,9 +48,9 @@ Item { onVisibleChanged: { if (visible) { passphraseField.focus = true; - sendSignalToWallet({method: 'disableHmdPreview'}); + sendSignalToParent({method: 'disableHmdPreview'}); } else { - sendSignalToWallet({method: 'maybeEnableHmdPreview'}); + sendSignalToParent({method: 'maybeEnableHmdPreview'}); } } @@ -96,9 +96,9 @@ Item { onFocusChanged: { if (focus) { - sendSignalToWallet({method: 'walletSetup_raiseKeyboard'}); + keyboard.raised = true; } else if (!passphraseFieldAgain.focus) { - sendSignalToWallet({method: 'walletSetup_lowerKeyboard'}); + keyboard.raised = false; } } @@ -106,7 +106,7 @@ Item { anchors.fill: parent; onClicked: { parent.focus = true; - sendSignalToWallet({method: 'walletSetup_raiseKeyboard'}); + keyboard.raised = true; } } @@ -206,7 +206,7 @@ Item { width: parent.width/2 - anchors.leftMargin*2; text: "Cancel" onClicked: { - sendSignalToWallet({method: 'passphrasePopup_cancelClicked'}); + sendSignalToParent({method: 'passphrasePopup_cancelClicked'}); } } @@ -228,5 +228,45 @@ Item { } } - signal sendSignalToWallet(var msg); + Item { + id: keyboardContainer; + z: 999; + visible: keyboard.raised; + property bool punctuationMode: false; + anchors { + bottom: parent.bottom; + left: parent.left; + right: parent.right; + } + + Image { + id: lowerKeyboardButton; + source: "images/lowerKeyboard.png"; + anchors.horizontalCenter: parent.horizontalCenter; + anchors.bottom: keyboard.top; + height: 30; + width: 120; + + MouseArea { + anchors.fill: parent; + + onClicked: { + root.keyboardRaised = false; + } + } + } + + HifiControlsUit.Keyboard { + id: keyboard; + raised: HMD.mounted && root.keyboardRaised; + numeric: parent.punctuationMode; + anchors { + bottom: parent.bottom; + left: parent.left; + right: parent.right; + } + } + } + + signal sendSignalToParent(var msg); } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 04278e85a9..8858c25964 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -253,14 +253,8 @@ Rectangle { anchors.right: parent.right; Connections { - onSendSignalToWallet: { - if (msg.method === 'walletSetup_raiseKeyboard') { - root.keyboardRaised = true; - } else if (msg.method === 'walletSetup_lowerKeyboard') { - root.keyboardRaised = false; - } else { - sendToScript(msg); - } + onSendSignalToParent: { + sendToScript(msg); } } } diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 7d1aaee157..ccaa03baa8 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -231,6 +231,7 @@ case 'purchases_goToMarketplaceClicked': tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); break; + case 'passphrasePopup_cancelClicked': case 'needsLogIn_cancelClicked': tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); break; From eab2947d4055e1073c88af89941234dc92757c19 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 31 Aug 2017 15:16:29 -0700 Subject: [PATCH 04/14] Auth flow --- .../qml/hifi/commerce/checkout/Checkout.qml | 69 ++++++++----------- .../qml/hifi/commerce/purchases/Purchases.qml | 58 ++++++---------- .../hifi/commerce/wallet/PassphraseModal.qml | 14 +++- .../commerce/wallet/PassphraseSelection.qml | 8 +-- .../qml/hifi/commerce/wallet/Wallet.qml | 53 +++++--------- .../commerce/wallet/WalletSetupLightbox.qml | 4 +- interface/src/commerce/QmlCommerce.cpp | 69 +++++++++---------- interface/src/commerce/QmlCommerce.h | 26 +++---- interface/src/commerce/Wallet.cpp | 18 +++++ interface/src/commerce/Wallet.h | 1 + 10 files changed, 154 insertions(+), 166 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 04dacf67c0..1a1579443c 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -29,7 +29,6 @@ Rectangle { property bool purchasesReceived: false; property bool balanceReceived: false; property bool securityImageResultReceived: false; - property bool keyFilePathIfExistsResultReceived: false; property string itemId: ""; property string itemHref: ""; property double balanceAfterPurchase: 0; @@ -46,38 +45,15 @@ Rectangle { root.activeView = "needsLogIn"; } else if (isLoggedIn) { root.activeView = "initialize"; - commerce.getPassphraseSetupStatus(); - } - } - - onPassphraseSetupStatusResult: { - if (!passphraseIsSetup && root.activeView !== "notSetUp") { - root.activeView = "notSetUp"; - } else if (passphraseIsSetup && root.activeView === "initialize") { - commerce.getWalletAuthenticatedStatus(); + commerce.getKeyFilePathIfExists(); } } - onWalletAuthenticatedStatusResult: { - if (!isAuthenticated && !passphraseModal.visible) { - passphraseModal.visible = true; - } else if (isAuthenticated) { - if (passphraseModal.visible) { - passphraseModal.visible = false; - } - - if (!securityImageResultReceived) { - commerce.getSecurityImage(); - } - if (!keyFilePathIfExistsResultReceived) { - commerce.getKeyFilePathIfExists(); - } - if (!balanceReceived) { - commerce.balance(); - } - if (!purchasesReceived) { - commerce.inventory(); - } + onKeyFilePathIfExistsResult: { + if (path === "" && root.activeView !== "notSetUp") { + root.activeView = "notSetUp"; + } else if (path !== "" && root.activeView === "initialize") { + commerce.getSecurityImage(); } } @@ -85,8 +61,9 @@ Rectangle { securityImageResultReceived = true; if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up" root.activeView = "notSetUp"; - } else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") { + } else if (exists && root.activeView === "initialize") { root.activeView = "checkoutMain"; + commerce.getWalletAuthenticatedStatus(); } else if (exists) { // just set the source again (to be sure the change was noticed) securityImage.source = ""; @@ -94,12 +71,16 @@ Rectangle { } } - onKeyFilePathIfExistsResult: { - keyFilePathIfExistsResultReceived = true; - if (path === "" && root.activeView !== "notSetUp") { - root.activeView = "notSetUp"; - } else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") { - root.activeView = "checkoutMain"; + onWalletAuthenticatedStatusResult: { + if (!isAuthenticated && !passphraseModal.visible) { + passphraseModal.visible = true; + } else if (isAuthenticated) { + if (!balanceReceived) { + commerce.balance(); + } + if (!purchasesReceived) { + commerce.inventory(); + } } } @@ -219,7 +200,6 @@ Rectangle { securityImageResultReceived = false; purchasesReceived = false; balanceReceived = false; - keyFilePathIfExistsResultReceived = false; commerce.getLoginStatus(); } } @@ -245,7 +225,7 @@ Rectangle { } } - PassphraseModal { + HifiWallet.PassphraseModal { id: passphraseModal; z: 998; visible: false; @@ -256,7 +236,16 @@ Rectangle { Connections { onSendSignalToParent: { - sendToScript(msg); + if (msg.method === 'passphraseModal_authSuccess') { + if (!balanceReceived) { + commerce.balance(); + } + if (!purchasesReceived) { + commerce.inventory(); + } + } else { + sendToScript(msg); + } } } } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index f9871d1b2a..a3a09f6452 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -28,7 +28,6 @@ Rectangle { property string activeView: "initialize"; property string referrerURL: ""; property bool securityImageResultReceived: false; - property bool keyFilePathIfExistsResultReceived: false; property bool purchasesReceived: false; property bool punctuationMode: false; // Style @@ -41,35 +40,15 @@ Rectangle { root.activeView = "needsLogIn"; } else if (isLoggedIn) { root.activeView = "initialize"; - commerce.getPassphraseSetupStatus(); - } - } - - onPassphraseSetupStatusResult: { - if (!passphraseIsSetup && root.activeView !== "notSetUp") { - root.activeView = "notSetUp"; - } else if (passphraseIsSetup && root.activeView === "initialize") { - commerce.getWalletAuthenticatedStatus(); + commerce.getKeyFilePathIfExists(); } } - onWalletAuthenticatedStatusResult: { - if (!isAuthenticated && !passphraseModal.visible) { - passphraseModal.visible = true; - } else if (isAuthenticated) { - if (passphraseModal.visible) { - passphraseModal.visible = false; - } - - if (!securityImageResultReceived) { - commerce.getSecurityImage(); - } - if (!keyFilePathIfExistsResultReceived) { - commerce.getKeyFilePathIfExists(); - } - if (!purchasesReceived) { - commerce.inventory(); - } + onKeyFilePathIfExistsResult: { + if (path === "" && root.activeView !== "notSetUp") { + root.activeView = "notSetUp"; + } else if (path !== "" && root.activeView === "initialize") { + commerce.getSecurityImage(); } } @@ -77,8 +56,9 @@ Rectangle { securityImageResultReceived = true; if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up" root.activeView = "notSetUp"; - } else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") { + } else if (exists && root.activeView === "initialize") { root.activeView = "purchasesMain"; + commerce.getWalletAuthenticatedStatus(); } else if (exists) { // just set the source again (to be sure the change was noticed) securityImage.source = ""; @@ -86,12 +66,11 @@ Rectangle { } } - onKeyFilePathIfExistsResult: { - keyFilePathIfExistsResultReceived = true; - if (path === "" && root.activeView !== "notSetUp") { - root.activeView = "notSetUp"; - } else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") { - root.activeView = "purchasesMain"; + onWalletAuthenticatedStatusResult: { + if (!isAuthenticated && !passphraseModal.visible) { + passphraseModal.visible = true; + } else if (isAuthenticated) { + commerce.inventory(); } } @@ -192,7 +171,6 @@ Rectangle { Component.onCompleted: { securityImageResultReceived = false; purchasesReceived = false; - keyFilePathIfExistsResultReceived = false; commerce.getLoginStatus(); } } @@ -218,7 +196,7 @@ Rectangle { } } - PassphraseModal { + HifiWallet.PassphraseModal { id: passphraseModal; z: 998; visible: false; @@ -229,7 +207,13 @@ Rectangle { Connections { onSendSignalToParent: { - sendToScript(msg); + if (msg.method === 'passphraseModal_authSuccess') { + if (!purchasesReceived) { + commerce.inventory(); + } + } else { + sendToScript(msg); + } } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index 5a6df37485..f9b2c88a5b 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -31,6 +31,17 @@ Item { passphraseModalSecurityImage.source = ""; passphraseModalSecurityImage.source = "image://security/securityImage"; } + + onWalletAuthenticatedStatusResult: { + submitPassphraseInputButton.enabled = true; + if (!isAuthenticated) { + // Auth failed, show error text + + } else { + root.visible = false; + sendSignalToParent({method: 'passphraseModal_authSuccess'}); + } + } } // This object is always used in a popup. @@ -222,7 +233,8 @@ Item { width: parent.width/2 - anchors.rightMargin*2; text: "Submit" onClicked: { - //commerce.submitWalletPassphrase(passphraseField.text); + submitPassphraseInputButton.enabled = false; + commerce.setPassphrase(passphraseField.text); } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml index 2bad454b63..7dca5a75b8 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml @@ -40,8 +40,8 @@ Item { passphrasePageSecurityImage.source = "image://security/securityImage"; } - onPassphraseSetupStatusResult: { - sendMessageToLightbox({method: 'statusResult', status: passphraseIsSetup}); + onWalletAuthenticatedStatusResult: { + sendMessageToLightbox({method: 'statusResult', status: isAuthenticated}); } } @@ -195,7 +195,7 @@ Item { // Text below TextFields RalewaySemiBold { id: passwordReqs; - text: "Passphrase must be at least 4 characters"; + text: "Passphrase must be at least 3 characters"; // Text size size: 16; // Anchors @@ -252,7 +252,7 @@ Item { } function validateAndSubmitPassphrase() { - if (passphraseField.text.length < 4) { + if (passphraseField.text.length < 3) { setErrorText("Passphrase too short."); return false; } else if (passphraseField.text !== passphraseFieldAgain.text) { diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 8858c25964..f672530d1f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -26,8 +26,6 @@ Rectangle { id: root; property string activeView: "initialize"; - property bool securityImageResultReceived: false; - property bool keyFilePathIfExistsResultReceived: false; property bool keyboardRaised: false; // Style @@ -40,14 +38,22 @@ Rectangle { root.activeView = "needsLogIn"; } else if (isLoggedIn) { root.activeView = "initialize"; - commerce.getPassphraseSetupStatus(); + commerce.getKeyFilePathIfExists(); } } - - onPassphraseSetupStatusResult: { - if (!passphraseIsSetup && root.activeView !== "notSetUp") { + + onKeyFilePathIfExistsResult: { + if (path === "" && root.activeView !== "notSetUp") { root.activeView = "notSetUp"; - } else if (passphraseIsSetup && root.activeView === "initialize") { + } else if (path !== "" && root.activeView === "initialize") { + commerce.getSecurityImage(); + } + } + + onSecurityImageResult: { + if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up" + root.activeView = "notSetUp"; + } else if (exists && root.activeView === "initialize") { commerce.getWalletAuthenticatedStatus(); } } @@ -56,33 +62,6 @@ Rectangle { if (!isAuthenticated && !passphraseModal.visible) { passphraseModal.visible = true; } else if (isAuthenticated) { - if (passphraseModal.visible) { - passphraseModal.visible = false; - } - - if (!securityImageResultReceived) { - commerce.getSecurityImage(); - } - if (!keyFilePathIfExistsResultReceived) { - commerce.getKeyFilePathIfExists(); - } - } - } - - onSecurityImageResult: { - securityImageResultReceived = true; - if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up" - root.activeView = "notSetUp"; - } else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") { - root.activeView = "walletHome"; - } - } - - onKeyFilePathIfExistsResult: { - keyFilePathIfExistsResultReceived = true; - if (path === "" && root.activeView !== "notSetUp") { - root.activeView = "notSetUp"; - } else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") { root.activeView = "walletHome"; } } @@ -254,7 +233,11 @@ Rectangle { Connections { onSendSignalToParent: { - sendToScript(msg); + if (msg.method === 'passphraseModal_authSuccess') { + root.activeView = "walletHome"; + } else { + sendToScript(msg); + } } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml index 4470ec7a75..982a2cf9a4 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml @@ -39,9 +39,9 @@ Rectangle { } } - onPassphraseSetupStatusResult: { + onWalletAuthenticatedStatusResult: { securityImageContainer.visible = false; - if (passphraseIsSetup) { + if (isAuthenticated) { privateKeysReadyContainer.visible = true; } else { choosePassphraseContainer.visible = true; diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index ccbc37ed13..32abb11ab0 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -29,6 +29,40 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) { connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult); } +void QmlCommerce::getLoginStatus() { + emit loginStatusResult(DependencyManager::get()->isLoggedIn()); +} + +void QmlCommerce::getKeyFilePathIfExists() { + auto wallet = DependencyManager::get(); + wallet->sendKeyFilePathIfExists(); +} + +void QmlCommerce::getWalletAuthenticatedStatus() { + auto wallet = DependencyManager::get(); + emit walletAuthenticatedStatusResult(wallet->walletIsAuthenticatedWithPassphrase()); +} + +void QmlCommerce::getSecurityImage() { + auto wallet = DependencyManager::get(); + wallet->getSecurityImage(); +} + + + +void QmlCommerce::chooseSecurityImage(const QString& imageFile) { + auto wallet = DependencyManager::get(); + wallet->chooseSecurityImage(imageFile); +} + +void QmlCommerce::setPassphrase(const QString& passphrase) { + auto wallet = DependencyManager::get(); + wallet->setPassphrase(passphrase); + getWalletAuthenticatedStatus(); +} + + + void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); @@ -60,42 +94,7 @@ void QmlCommerce::history() { ledger->history(wallet->listPublicKeys()); } -void QmlCommerce::chooseSecurityImage(const QString& imageFile) { - auto wallet = DependencyManager::get(); - wallet->chooseSecurityImage(imageFile); -} -void QmlCommerce::getSecurityImage() { - auto wallet = DependencyManager::get(); - wallet->getSecurityImage(); -} - -void QmlCommerce::getLoginStatus() { - emit loginStatusResult(DependencyManager::get()->isLoggedIn()); -} - -void QmlCommerce::setPassphrase(const QString& passphrase) { - auto wallet = DependencyManager::get(); - wallet->setPassphrase(passphrase); - getPassphraseSetupStatus(); -} - -void QmlCommerce::getPassphraseSetupStatus() { - auto wallet = DependencyManager::get(); - // ????? WHAT DO I DO HERE - emit passphraseSetupStatusResult(wallet->getPassphraseIsCached()); -} - -void QmlCommerce::getWalletAuthenticatedStatus() { - auto wallet = DependencyManager::get(); - // ????? WHAT DO I DO HERE - emit walletAuthenticatedStatusResult(wallet->getPassphraseIsCached()); -} - -void QmlCommerce::getKeyFilePathIfExists() { - auto wallet = DependencyManager::get(); - wallet->sendKeyFilePathIfExists(); -} void QmlCommerce::reset() { auto ledger = DependencyManager::get(); diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index 5561f331f9..f66bf518f5 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -28,30 +28,32 @@ public: QmlCommerce(QQuickItem* parent = nullptr); signals: + void loginStatusResult(bool isLoggedIn); + void keyFilePathIfExistsResult(const QString& path); + void securityImageResult(bool exists); + void walletAuthenticatedStatusResult(bool isAuthenticated); + void buyResult(QJsonObject result); // Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and // because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain). void balanceResult(QJsonObject result); void inventoryResult(QJsonObject result); - void securityImageResult(bool exists); - void loginStatusResult(bool isLoggedIn); - void passphraseSetupStatusResult(bool passphraseIsSetup); void historyResult(QJsonObject result); - void keyFilePathIfExistsResult(const QString& path); - void walletAuthenticatedStatusResult(bool isAuthenticated); protected: + Q_INVOKABLE void getLoginStatus(); + Q_INVOKABLE void getKeyFilePathIfExists(); + Q_INVOKABLE void getSecurityImage(); + Q_INVOKABLE void getWalletAuthenticatedStatus(); + + Q_INVOKABLE void chooseSecurityImage(const QString& imageFile); + Q_INVOKABLE void setPassphrase(const QString& passphrase); + Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = ""); Q_INVOKABLE void balance(); Q_INVOKABLE void inventory(); Q_INVOKABLE void history(); - Q_INVOKABLE void chooseSecurityImage(const QString& imageFile); - Q_INVOKABLE void getSecurityImage(); - Q_INVOKABLE void getLoginStatus(); - Q_INVOKABLE void setPassphrase(const QString& passphrase); - Q_INVOKABLE void getPassphraseSetupStatus(); - Q_INVOKABLE void getWalletAuthenticatedStatus(); - Q_INVOKABLE void getKeyFilePathIfExists(); + Q_INVOKABLE void reset(); }; diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index cfd5b6baa5..7d427bf70f 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -342,6 +342,24 @@ bool Wallet::decryptFile(const QString& inputFilePath, unsigned char** outputBuf return true; } +bool Wallet::walletIsAuthenticatedWithPassphrase() { + // try to read existing keys if they exist... + + // FIXME: initialize OpenSSL elsewhere soon + initialize(); + + auto publicKey = readPublicKey(keyFilePath().toStdString().c_str()); + + if (publicKey.size() > 0) { + if (auto key = readPrivateKey(keyFilePath().toStdString().c_str())) { + RSA_free(key); + return true; + } + } + + return false; +} + bool Wallet::createIfNeeded() { if (_publicKeys.count() > 0) return false; diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index cb0e318598..675fbc7dd8 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -40,6 +40,7 @@ public: void setPassphrase(const QString& passphrase); QString* getPassphrase() { return _passphrase; } bool getPassphraseIsCached() { return !(_passphrase->isEmpty()); } + bool walletIsAuthenticatedWithPassphrase(); void reset(); From 2c2500875fb7e2665e8b00805c4efd1581176fd7 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 31 Aug 2017 15:39:58 -0700 Subject: [PATCH 05/14] Fixes and improvements --- .../qml/hifi/commerce/checkout/Checkout.qml | 1 - .../qml/hifi/commerce/purchases/Purchases.qml | 1 - .../hifi/commerce/wallet/PassphraseModal.qml | 43 ++++++++++++++----- .../qml/hifi/commerce/wallet/Wallet.qml | 1 - 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 1a1579443c..4d019685fd 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -227,7 +227,6 @@ Rectangle { HifiWallet.PassphraseModal { id: passphraseModal; - z: 998; visible: false; anchors.top: titleBarContainer.bottom; anchors.bottom: parent.bottom; diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index a3a09f6452..71e182e023 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -198,7 +198,6 @@ Rectangle { HifiWallet.PassphraseModal { id: passphraseModal; - z: 998; visible: false; anchors.top: titleBarContainer.bottom; anchors.bottom: parent.bottom; diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index f9b2c88a5b..ae4ad63047 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -24,6 +24,9 @@ Item { HifiConstants { id: hifi; } id: root; + z: 998; + property bool keyboardRaised: false; + Hifi.QmlCommerce { id: commerce; @@ -35,8 +38,7 @@ Item { onWalletAuthenticatedStatusResult: { submitPassphraseInputButton.enabled = true; if (!isAuthenticated) { - // Auth failed, show error text - + errorText.text = "Authentication failed - please try again."; } else { root.visible = false; sendSignalToParent({method: 'passphraseModal_authSuccess'}); @@ -76,7 +78,7 @@ Item { anchors.top: parent.top; anchors.left: parent.left; anchors.right: parent.right; - height: 220; + height: 250; color: hifi.colors.baseGray; RalewaySemiBold { @@ -106,23 +108,21 @@ Item { placeholderText: "passphrase"; onFocusChanged: { - if (focus) { - keyboard.raised = true; - } else if (!passphraseFieldAgain.focus) { - keyboard.raised = false; - } + root.keyboardRaised = focus; } MouseArea { anchors.fill: parent; + onClicked: { parent.focus = true; - keyboard.raised = true; + root.keyboardRaised = true; } } onAccepted: { - //commerce.submitWalletPassphrase(passphraseField.text); + submitPassphraseInputButton.enabled = false; + commerce.setPassphrase(passphraseField.text); } } @@ -139,7 +139,7 @@ Item { onClicked: { passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password; } - } + } // Security Image Item { @@ -191,6 +191,27 @@ Item { verticalAlignment: Text.AlignVCenter; } } + + // Error text above buttons + RalewaySemiBold { + id: errorText; + text: ""; + // Text size + size: 16; + // Anchors + anchors.bottom: passphrasePopupActionButtonsContainer.top; + anchors.bottomMargin: 4; + anchors.left: parent.left; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + height: 30; + // Style + color: hifi.colors.redHighlight; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } // // ACTION BUTTONS START diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index f672530d1f..ad8f6531bc 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -224,7 +224,6 @@ Rectangle { PassphraseModal { id: passphraseModal; - z: 998; visible: false; anchors.top: titleBarContainer.bottom; anchors.bottom: parent.bottom; From 0a9e3e8967c90dee56fb961df7c6b36d8b134b14 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 31 Aug 2017 15:42:32 -0700 Subject: [PATCH 06/14] Remove unnecessary code --- .../qml/hifi/commerce/checkout/Checkout.qml | 13 ++----------- .../qml/hifi/commerce/purchases/Purchases.qml | 10 ++-------- .../qml/hifi/commerce/wallet/PassphraseModal.qml | 1 - .../resources/qml/hifi/commerce/wallet/Wallet.qml | 6 +----- 4 files changed, 5 insertions(+), 25 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 4d019685fd..28e5b32c4d 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -62,7 +62,6 @@ Rectangle { if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up" root.activeView = "notSetUp"; } else if (exists && root.activeView === "initialize") { - root.activeView = "checkoutMain"; commerce.getWalletAuthenticatedStatus(); } else if (exists) { // just set the source again (to be sure the change was noticed) @@ -75,6 +74,7 @@ Rectangle { if (!isAuthenticated && !passphraseModal.visible) { passphraseModal.visible = true; } else if (isAuthenticated) { + root.activeView = "checkoutMain"; if (!balanceReceived) { commerce.balance(); } @@ -235,16 +235,7 @@ Rectangle { Connections { onSendSignalToParent: { - if (msg.method === 'passphraseModal_authSuccess') { - if (!balanceReceived) { - commerce.balance(); - } - if (!purchasesReceived) { - commerce.inventory(); - } - } else { - sendToScript(msg); - } + sendToScript(msg); } } } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 71e182e023..383223a49c 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -57,7 +57,6 @@ Rectangle { if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up" root.activeView = "notSetUp"; } else if (exists && root.activeView === "initialize") { - root.activeView = "purchasesMain"; commerce.getWalletAuthenticatedStatus(); } else if (exists) { // just set the source again (to be sure the change was noticed) @@ -70,6 +69,7 @@ Rectangle { if (!isAuthenticated && !passphraseModal.visible) { passphraseModal.visible = true; } else if (isAuthenticated) { + root.activeView = "purchasesMain"; commerce.inventory(); } } @@ -206,13 +206,7 @@ Rectangle { Connections { onSendSignalToParent: { - if (msg.method === 'passphraseModal_authSuccess') { - if (!purchasesReceived) { - commerce.inventory(); - } - } else { - sendToScript(msg); - } + sendToScript(msg); } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index ae4ad63047..519885acc9 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -41,7 +41,6 @@ Item { errorText.text = "Authentication failed - please try again."; } else { root.visible = false; - sendSignalToParent({method: 'passphraseModal_authSuccess'}); } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index ad8f6531bc..5adf37ba2b 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -232,11 +232,7 @@ Rectangle { Connections { onSendSignalToParent: { - if (msg.method === 'passphraseModal_authSuccess') { - root.activeView = "walletHome"; - } else { - sendToScript(msg); - } + sendToScript(msg); } } } From fa25c434ca64065ee8efe9222f586ad4ea87ee50 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 31 Aug 2017 15:50:19 -0700 Subject: [PATCH 07/14] Messages to marketplaces.js --- .../qml/hifi/commerce/wallet/PassphraseModal.qml | 2 +- scripts/system/marketplaces/marketplaces.js | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index 519885acc9..4157e4082f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -4,7 +4,7 @@ // // PassphraseModal // -// Created by Zach Fox on 2017-08-18 +// Created by Zach Fox on 2017-08-31 // Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index ccaa03baa8..2eaefe7565 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -197,6 +197,7 @@ // Description: // -Called when a message is received from Checkout.qml. The "message" argument is what is sent from the Checkout QML // in the format "{method, params}", like json-rpc. + var isHmdPreviewDisabled = true; function fromQml(message) { switch (message.method) { case 'checkout_setUpClicked': @@ -238,6 +239,13 @@ case 'needsLogIn_loginClicked': openLoginWindow(); break; + case 'disableHmdPreview': + isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview"); + Menu.setIsOptionChecked("Disable Preview", true); + break; + case 'maybeEnableHmdPreview': + Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled); + break; default: print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message)); } From 1bfc767dafc4bc967b4cb9602ce0b0e20444956a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 31 Aug 2017 16:42:14 -0700 Subject: [PATCH 08/14] Quick fixes --- interface/resources/qml/hifi/commerce/wallet/Wallet.qml | 2 +- .../resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 5adf37ba2b..5695d47a68 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -93,7 +93,7 @@ Rectangle { walletSetupLightbox.visible = false; } else if (msg.method === 'walletSetup_finished') { root.activeView = "initialize"; - commerce.getPassphraseSetupStatus(); + commerce.getLoginStatus(); } else if (msg.method === 'walletSetup_raiseKeyboard') { root.keyboardRaised = true; } else if (msg.method === 'walletSetup_lowerKeyboard') { diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml index 982a2cf9a4..2c02dc78a5 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml @@ -210,7 +210,7 @@ Rectangle { onVisibleChanged: { if (visible) { - commerce.getPassphraseSetupStatus(); + commerce.getWalletAuthenticatedStatus(); } } From 8fcdfa171d8a9703b930886697ba2d62a13b8c4b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 31 Aug 2017 16:47:24 -0700 Subject: [PATCH 09/14] Add debug button to clear passphrase --- .../qml/hifi/commerce/wallet/Help.qml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index 47b3f6daf6..402209f38d 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -47,11 +47,26 @@ Item { HifiControlsUit.Button { color: hifi.buttons.black; colorScheme: hifi.colorSchemes.dark; - anchors.bottom: helpText.bottom; + anchors.bottom: resetButton.top; + anchors.bottomMargin: 15; anchors.horizontalCenter: parent.horizontalCenter; height: 50; width: 250; - text: "Testing: Reset Wallet!"; + text: "DEBUG: Clear Cached Passphrase"; + onClicked: { + commerce.setPassphrase(""); + } + } + HifiControlsUit.Button { + id: resetButton; + color: hifi.buttons.red; + colorScheme: hifi.colorSchemes.dark; + anchors.bottom: helpText.bottom; + anchors.bottomMargin: 15; + anchors.horizontalCenter: parent.horizontalCenter; + height: 50; + width: 250; + text: "DEBUG: Reset Wallet!"; onClicked: { commerce.reset(); sendSignalToWallet({method: 'walletReset'}); From 451936214dab3202526a8419b0ed42e98c12a11a Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 31 Aug 2017 17:05:55 -0700 Subject: [PATCH 10/14] initial stab at changing passphrase --- interface/src/commerce/QmlCommerce.cpp | 22 ++++---- interface/src/commerce/Wallet.cpp | 76 ++++++++++++++++++++++++++ interface/src/commerce/Wallet.h | 3 +- 3 files changed, 88 insertions(+), 13 deletions(-) diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 32abb11ab0..b03da0d8b7 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -48,21 +48,11 @@ void QmlCommerce::getSecurityImage() { wallet->getSecurityImage(); } - - void QmlCommerce::chooseSecurityImage(const QString& imageFile) { auto wallet = DependencyManager::get(); wallet->chooseSecurityImage(imageFile); } -void QmlCommerce::setPassphrase(const QString& passphrase) { - auto wallet = DependencyManager::get(); - wallet->setPassphrase(passphrase); - getWalletAuthenticatedStatus(); -} - - - void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); @@ -94,11 +84,19 @@ void QmlCommerce::history() { ledger->history(wallet->listPublicKeys()); } - +void QmlCommerce::setPassphrase(const QString& passphrase) { + auto wallet = DependencyManager::get(); + if (wallet->getPassphrase()) { + wallet->changePassphrase(passphrase); + } else { + wallet->setPassphrase(passphrase); + } + getWalletAuthenticatedStatus(); +} void QmlCommerce::reset() { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); ledger->reset(); wallet->reset(); -} \ No newline at end of file +} diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 7d427bf70f..1385067bd0 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -217,6 +217,55 @@ RSA* readPrivateKey(const char* filename) { return key; } +RSA* readKeys(const char* filename) { + FILE* fp; + RSA* key = NULL; + if ((fp = fopen(filename, "rt"))) { + // file opened successfully + qCDebug(commerce) << "opened key file" << filename; + if ((key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL))) { + // now read private key + + qCDebug(commerce) << "read public key"; + + if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) { + qCDebug(commerce) << "read private key"; + + return key; + } + qCDebug(commerce) << "failed to read private key"; + } + qCDebug(commerce) << "failed to read public key"; + } else { + qCDebug(commerce) << "failed to open key file" << filename; + } + return key; +} + +bool writeKeys(const char* filename, RSA* keys) { + FILE* fp; + bool retval = false; + if ((fp = fopen(filename, "wt"))) { + if (!PEM_write_RSAPublicKey(fp, keys)) { + fclose(fp); + qCDebug(commerce) << "failed to write public key"; + return retval; + } + + if (!PEM_write_RSAPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { + fclose(fp); + qCDebug(commerce) << "failed to write private key"; + return retval; + } + + retval = true; + qCDebug(commerce) << "wrote keys successfully"; + } else { + qCDebug(commerce) << "failed to open key file" << filename; + } + return retval; +} + static const unsigned char IVEC[16] = "IAmAnIVecYay123"; void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) { @@ -531,3 +580,30 @@ void Wallet::reset() { keyFile.remove(); imageFile.remove(); } + +bool Wallet::changePassphrase(const QString& newPassphrase) { + RSA* keys = readKeys(keyFilePath().toStdString().c_str()); + if (keys) { + // we read successfully, so now write to a new temp file + // save old passphrase just in case + // TODO: force re-enter? + QString oldPassphrase = *_passphrase; + setPassphrase(newPassphrase); + QString tempFileName = QString("%1.%2").arg(keyFilePath(), QString("temp")); + if (writeKeys(tempFileName.toStdString().c_str(), keys)) { + // ok, now move the temp file to the correct spot + QFile(QString(keyFilePath())).remove(); + QFile(tempFileName).rename(QString(keyFilePath())); + emit passphraseSetupStatusResult(true); + return true; + } else { + qCDebug(commerce) << "couldn't write keys"; + setPassphrase(oldPassphrase); + emit passphraseSetupStatusResult(false); + return false; + } + } + qCDebug(commerce) << "couldn't read keys"; + emit passphraseSetupStatusResult(false); + return false; +} diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 675fbc7dd8..19f5f64590 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -41,13 +41,14 @@ public: QString* getPassphrase() { return _passphrase; } bool getPassphraseIsCached() { return !(_passphrase->isEmpty()); } bool walletIsAuthenticatedWithPassphrase(); + bool changePassphrase(const QString& newPassphrase); void reset(); signals: void securityImageResult(bool exists); void keyFilePathIfExistsResult(const QString& path); - + void passphraseSetupStatusResult(bool successful); private: QStringList _publicKeys{}; QPixmap* _securityImage { nullptr }; From 51699c9a6a4662a0d7593c90472c0230c878e00e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 1 Sep 2017 11:44:46 -0700 Subject: [PATCH 11/14] Minor changes to fonts and colors --- .../qml/hifi/commerce/wallet/WalletHome.qml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index b55f7f800a..077c1ae52c 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -88,13 +88,14 @@ Item { height: 60; Rectangle { id: hfcBalanceField; + color: hifi.colors.darkGray; anchors.right: parent.right; anchors.left: parent.left; anchors.bottom: parent.bottom; height: parent.height - 15; // "HFC" balance label - RalewayRegular { + FiraSansRegular { id: balanceLabel; text: "HFC"; // Text size @@ -106,7 +107,7 @@ Item { anchors.rightMargin: 4; width: paintedWidth; // Style - color: hifi.colors.darkGray; + color: hifi.colors.lightGrayText; // Alignment horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignVCenter; @@ -121,7 +122,7 @@ Item { } // Balance Text - FiraSansRegular { + FiraSansSemiBold { id: balanceText; text: "--"; // Text size @@ -133,7 +134,7 @@ Item { anchors.right: balanceLabel.left; anchors.rightMargin: 4; // Style - color: hifi.colors.darkGray; + color: hifi.colors.lightGrayText; // Alignment horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignVCenter; @@ -258,7 +259,7 @@ Item { delegate: Item { width: parent.width; height: transactionText.height + 30; - RalewayRegular { + FiraSansRegular { id: transactionText; text: model.text; // Style @@ -288,7 +289,7 @@ Item { } // This should never be visible (since you immediately get 100 HFC) - RalewayRegular { + FiraSansRegular { id: emptyTransationHistory; size: 24; visible: !transactionHistory.visible && root.historyReceived; From 9fe6ce0337e769794a4d61c837495b2b85b9e04c Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 1 Sep 2017 13:11:56 -0700 Subject: [PATCH 12/14] working change passphrase. some cleanup to come --- .../qml/hifi/commerce/wallet/Wallet.qml | 2 +- .../commerce/wallet/WalletSetupLightbox.qml | 4 ++-- interface/src/commerce/QmlCommerce.cpp | 2 +- interface/src/commerce/Wallet.cpp | 21 +++++++++++-------- interface/src/commerce/Wallet.h | 2 +- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 5adf37ba2b..f18e520ae6 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -93,7 +93,7 @@ Rectangle { walletSetupLightbox.visible = false; } else if (msg.method === 'walletSetup_finished') { root.activeView = "initialize"; - commerce.getPassphraseSetupStatus(); + commerce.getWalletAuthenticatedStatus(); } else if (msg.method === 'walletSetup_raiseKeyboard') { root.keyboardRaised = true; } else if (msg.method === 'walletSetup_lowerKeyboard') { diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml index 982a2cf9a4..2956dfb518 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml @@ -117,7 +117,7 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 16; height: 280; - + Connections { onSendSignalToWallet: { sendSignalToWallet(msg); @@ -210,7 +210,7 @@ Rectangle { onVisibleChanged: { if (visible) { - commerce.getPassphraseSetupStatus(); + commerce.getWalletAuthenticatedStatus(); } } diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index b03da0d8b7..96f2a02f31 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -86,7 +86,7 @@ void QmlCommerce::history() { void QmlCommerce::setPassphrase(const QString& passphrase) { auto wallet = DependencyManager::get(); - if (wallet->getPassphrase()) { + if (wallet->getPassphrase() && !wallet->getPassphrase()->isEmpty()) { wallet->changePassphrase(passphrase); } else { wallet->setPassphrase(passphrase); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 1385067bd0..58ad70a365 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -56,7 +56,7 @@ QString imageFilePath() { int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) { // just return a hardcoded pwd for now auto passphrase = DependencyManager::get()->getPassphrase(); - if (passphrase) { + if (passphrase && !passphrase->isEmpty()) { strcpy(password, passphrase->toLocal8Bit().constData()); return static_cast(passphrase->size()); } else { @@ -134,12 +134,14 @@ QPair generateRSAKeypair() { if ((fp = fopen(keyFilePath().toStdString().c_str(), "at"))) { if (!PEM_write_RSAPublicKey(fp, keyPair)) { fclose(fp); + QFile(keyFilePath()).remove(); qCDebug(commerce) << "failed to write public key"; return retval; } if (!PEM_write_RSAPrivateKey(fp, keyPair, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { fclose(fp); + QFile(keyFilePath()).remove(); qCDebug(commerce) << "failed to write private key"; return retval; } @@ -202,9 +204,6 @@ RSA* readPrivateKey(const char* filename) { // file opened successfully qCDebug(commerce) << "opened key file" << filename; if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) { - // cleanup - fclose(fp); - qCDebug(commerce) << "parsed private key file successfully"; } else { @@ -230,12 +229,14 @@ RSA* readKeys(const char* filename) { if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) { qCDebug(commerce) << "read private key"; - + fclose(fp); return key; } qCDebug(commerce) << "failed to read private key"; + } else { + qCDebug(commerce) << "failed to read public key"; } - qCDebug(commerce) << "failed to read public key"; + fclose(fp); } else { qCDebug(commerce) << "failed to open key file" << filename; } @@ -249,17 +250,20 @@ bool writeKeys(const char* filename, RSA* keys) { if (!PEM_write_RSAPublicKey(fp, keys)) { fclose(fp); qCDebug(commerce) << "failed to write public key"; + QFile(QString(filename)).remove(); return retval; } if (!PEM_write_RSAPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { fclose(fp); qCDebug(commerce) << "failed to write private key"; + QFile(QString(filename)).remove(); return retval; } retval = true; qCDebug(commerce) << "wrote keys successfully"; + fclose(fp); } else { qCDebug(commerce) << "failed to open key file" << filename; } @@ -582,6 +586,7 @@ void Wallet::reset() { } bool Wallet::changePassphrase(const QString& newPassphrase) { + qCDebug(commerce) << "changing passphrase"; RSA* keys = readKeys(keyFilePath().toStdString().c_str()); if (keys) { // we read successfully, so now write to a new temp file @@ -594,16 +599,14 @@ bool Wallet::changePassphrase(const QString& newPassphrase) { // ok, now move the temp file to the correct spot QFile(QString(keyFilePath())).remove(); QFile(tempFileName).rename(QString(keyFilePath())); - emit passphraseSetupStatusResult(true); + qCDebug(commerce) << "passphrase changed successfully"; return true; } else { qCDebug(commerce) << "couldn't write keys"; setPassphrase(oldPassphrase); - emit passphraseSetupStatusResult(false); return false; } } qCDebug(commerce) << "couldn't read keys"; - emit passphraseSetupStatusResult(false); return false; } diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 19f5f64590..3b470210de 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -48,7 +48,7 @@ public: signals: void securityImageResult(bool exists); void keyFilePathIfExistsResult(const QString& path); - void passphraseSetupStatusResult(bool successful); + private: QStringList _publicKeys{}; QPixmap* _securityImage { nullptr }; From feaa8d312022289e1aacb1ccef1eb3ea20998679 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 1 Sep 2017 14:06:43 -0700 Subject: [PATCH 13/14] Someday, a real rain will come and wash Wallet.cpp Till then, this little change will have to do. --- interface/src/commerce/Wallet.cpp | 135 +++++++++++++----------------- 1 file changed, 59 insertions(+), 76 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 58ad70a365..6caa9c9a5b 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -67,6 +67,61 @@ int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) { } } +RSA* readKeys(const char* filename) { + FILE* fp; + RSA* key = NULL; + if ((fp = fopen(filename, "rt"))) { + // file opened successfully + qCDebug(commerce) << "opened key file" << filename; + if ((key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL))) { + // now read private key + + qCDebug(commerce) << "read public key"; + + if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) { + qCDebug(commerce) << "read private key"; + fclose(fp); + return key; + } + qCDebug(commerce) << "failed to read private key"; + } else { + qCDebug(commerce) << "failed to read public key"; + } + fclose(fp); + } else { + qCDebug(commerce) << "failed to open key file" << filename; + } + return key; +} + +bool writeKeys(const char* filename, RSA* keys) { + FILE* fp; + bool retval = false; + if ((fp = fopen(filename, "wt"))) { + if (!PEM_write_RSAPublicKey(fp, keys)) { + fclose(fp); + qCDebug(commerce) << "failed to write public key"; + QFile(QString(filename)).remove(); + return retval; + } + + if (!PEM_write_RSAPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { + fclose(fp); + qCDebug(commerce) << "failed to write private key"; + QFile(QString(filename)).remove(); + return retval; + } + + retval = true; + qCDebug(commerce) << "wrote keys successfully"; + fclose(fp); + } else { + qCDebug(commerce) << "failed to open key file" << filename; + } + return retval; +} + + // BEGIN copied code - this will be removed/changed at some point soon // copied (without emits for various signals) from libraries/networking/src/RSAKeypairGenerator.cpp. // We will have a different implementation in practice, but this gives us a start for now @@ -125,27 +180,9 @@ QPair generateRSAKeypair() { } - - // now lets persist them to files - // FIXME: for now I'm appending to the file if it exists. As long as we always put - // the keys in the same order, this works fine. TODO: verify this will skip over - // anything else (like an embedded image) - FILE* fp; - if ((fp = fopen(keyFilePath().toStdString().c_str(), "at"))) { - if (!PEM_write_RSAPublicKey(fp, keyPair)) { - fclose(fp); - QFile(keyFilePath()).remove(); - qCDebug(commerce) << "failed to write public key"; - return retval; - } - - if (!PEM_write_RSAPrivateKey(fp, keyPair, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { - fclose(fp); - QFile(keyFilePath()).remove(); - qCDebug(commerce) << "failed to write private key"; - return retval; - } - fclose(fp); + if (!writeKeys(keyFilePath().toStdString().c_str(), keyPair)) { + qCDebug(commerce) << "couldn't save keys!"; + return retval; } RSA_free(keyPair); @@ -215,61 +252,6 @@ RSA* readPrivateKey(const char* filename) { } return key; } - -RSA* readKeys(const char* filename) { - FILE* fp; - RSA* key = NULL; - if ((fp = fopen(filename, "rt"))) { - // file opened successfully - qCDebug(commerce) << "opened key file" << filename; - if ((key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL))) { - // now read private key - - qCDebug(commerce) << "read public key"; - - if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) { - qCDebug(commerce) << "read private key"; - fclose(fp); - return key; - } - qCDebug(commerce) << "failed to read private key"; - } else { - qCDebug(commerce) << "failed to read public key"; - } - fclose(fp); - } else { - qCDebug(commerce) << "failed to open key file" << filename; - } - return key; -} - -bool writeKeys(const char* filename, RSA* keys) { - FILE* fp; - bool retval = false; - if ((fp = fopen(filename, "wt"))) { - if (!PEM_write_RSAPublicKey(fp, keys)) { - fclose(fp); - qCDebug(commerce) << "failed to write public key"; - QFile(QString(filename)).remove(); - return retval; - } - - if (!PEM_write_RSAPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { - fclose(fp); - qCDebug(commerce) << "failed to write private key"; - QFile(QString(filename)).remove(); - return retval; - } - - retval = true; - qCDebug(commerce) << "wrote keys successfully"; - fclose(fp); - } else { - qCDebug(commerce) << "failed to open key file" << filename; - } - return retval; -} - static const unsigned char IVEC[16] = "IAmAnIVecYay123"; void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) { @@ -603,6 +585,7 @@ bool Wallet::changePassphrase(const QString& newPassphrase) { return true; } else { qCDebug(commerce) << "couldn't write keys"; + QFile(tempFileName).remove(); setPassphrase(oldPassphrase); return false; } From 47cd47f819052becaa57680422e586912e93c395 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 1 Sep 2017 14:50:45 -0700 Subject: [PATCH 14/14] log when no passphrase was set before trying to decrypt --- interface/src/commerce/Wallet.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 6caa9c9a5b..1f56ae32c4 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -60,9 +60,9 @@ int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) { strcpy(password, passphrase->toLocal8Bit().constData()); return static_cast(passphrase->size()); } else { - // Old comment below...this should never happen once we're here...what if it does? - // ok gotta bring up modal dialog... But right now lets just - // just keep it empty + // this shouldn't happen - so lets log it to tell us we have + // a problem with the flow... + qCCritical(commerce) << "no cached passphrase while decrypting!"; return 0; } }