add pushbuttin auth support
parent
f070e67bfc
commit
f89b68973e
|
|
@ -92,6 +92,7 @@ void Engine::onConnectedChanged()
|
|||
m_deviceManager->clear();
|
||||
m_ruleManager->clear();
|
||||
if (m_jsonRpcClient->connected()) {
|
||||
qDebug() << "Engine: inital setup required:" << m_jsonRpcClient->initialSetupRequired() << "auth required:" << m_jsonRpcClient->authenticationRequired();
|
||||
if (!m_jsonRpcClient->initialSetupRequired() && !m_jsonRpcClient->authenticationRequired()) {
|
||||
m_deviceManager->init();
|
||||
m_ruleManager->init();
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ JsonRpcClient::JsonRpcClient(NymeaConnection *connection, QObject *parent) :
|
|||
{
|
||||
connect(m_connection, &NymeaConnection::connectedChanged, this, &JsonRpcClient::onInterfaceConnectedChanged);
|
||||
connect(m_connection, &NymeaConnection::dataAvailable, this, &JsonRpcClient::dataReceived);
|
||||
|
||||
registerNotificationHandler(this, "notificationReceived");
|
||||
}
|
||||
|
||||
QString JsonRpcClient::nameSpace() const
|
||||
|
|
@ -78,6 +80,38 @@ void JsonRpcClient::setNotificationsEnabled(bool enabled)
|
|||
void JsonRpcClient::setNotificationsEnabledResponse(const QVariantMap ¶ms)
|
||||
{
|
||||
qDebug() << "Notifications enabled:" << params;
|
||||
|
||||
m_connected = true;
|
||||
emit connectedChanged(true);
|
||||
|
||||
}
|
||||
|
||||
void JsonRpcClient::notificationReceived(const QVariantMap &data)
|
||||
{
|
||||
qDebug() << "JsonRpcClient: Notification received" << 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))))))
|
||||
if (data.value("notification").toString() == "JSONRPC.PushButtonAuthFinished") {
|
||||
qDebug() << "Push button auth finished.";
|
||||
if (data.value("params").toMap().value("transactionId").toInt() != m_pendingPushButtonTransaction) {
|
||||
qDebug() << "This push button transaction is not what we're waiting for...";
|
||||
return;
|
||||
}
|
||||
m_pendingPushButtonTransaction = -1;
|
||||
if (data.value("params").toMap().value("success").toBool()) {
|
||||
qDebug() << "Push button auth succeeded";
|
||||
m_token = data.value("params").toMap().value("token").toByteArray();
|
||||
QSettings settings;
|
||||
settings.beginGroup("jsonTokens");
|
||||
settings.setValue(m_serverUuid, m_token);
|
||||
settings.endGroup();
|
||||
emit authenticationRequiredChanged();
|
||||
|
||||
setNotificationsEnabled(true);
|
||||
} else {
|
||||
emit pushButtonAuthFailed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool JsonRpcClient::connected() const
|
||||
|
|
@ -95,6 +129,11 @@ bool JsonRpcClient::authenticationRequired() const
|
|||
return m_authenticationRequired && m_token.isEmpty();
|
||||
}
|
||||
|
||||
bool JsonRpcClient::pushButtonAuthAvailable() const
|
||||
{
|
||||
return m_pushButtonAuthAvailable;
|
||||
}
|
||||
|
||||
int JsonRpcClient::createUser(const QString &username, const QString &password)
|
||||
{
|
||||
QVariantMap params;
|
||||
|
|
@ -118,11 +157,22 @@ int JsonRpcClient::authenticate(const QString &username, const QString &password
|
|||
return reply->commandId();
|
||||
}
|
||||
|
||||
int JsonRpcClient::requestPushButtonAuth(const QString &deviceName)
|
||||
{
|
||||
qDebug() << "Requesting push button auth for device:" << deviceName;
|
||||
QVariantMap params;
|
||||
params.insert("deviceName", deviceName);
|
||||
JsonRpcReply *reply = createReply("JSONRPC.RequestPushButtonAuth", params, this, "processRequestPushButtonAuth");
|
||||
m_replies.insert(reply->commandId(), reply);
|
||||
m_connection->sendData(QJsonDocument::fromVariant(reply->requestMap()).toJson());
|
||||
return reply->commandId();
|
||||
}
|
||||
|
||||
|
||||
void JsonRpcClient::processAuthenticate(const QVariantMap &data)
|
||||
{
|
||||
qDebug() << "authenticate response" << data;
|
||||
if (data.value("status").toString() == "success" && data.value("params").toMap().value("success").toBool()) {
|
||||
qDebug() << "authentication successful";
|
||||
m_token = data.value("params").toMap().value("token").toByteArray();
|
||||
QSettings settings;
|
||||
settings.beginGroup("jsonTokens");
|
||||
|
|
@ -131,6 +181,9 @@ void JsonRpcClient::processAuthenticate(const QVariantMap &data)
|
|||
emit authenticationRequiredChanged();
|
||||
|
||||
setNotificationsEnabled(true);
|
||||
} else {
|
||||
qWarning() << "Authentication failed";
|
||||
emit authenticationFailed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -140,6 +193,19 @@ void JsonRpcClient::processCreateUser(const QVariantMap &data)
|
|||
if (data.value("status").toString() == "success" && data.value("params").toMap().value("error").toString() == "UserErrorNoError") {
|
||||
m_initialSetupRequired = false;
|
||||
emit initialSetupRequiredChanged();
|
||||
} else {
|
||||
qDebug() << "Emitting create user failed";
|
||||
emit createUserFailed(data.value("params").toMap().value("error").toString());
|
||||
}
|
||||
}
|
||||
|
||||
void JsonRpcClient::processRequestPushButtonAuth(const QVariantMap &data)
|
||||
{
|
||||
qDebug() << "requestPushButtonAuth response" << data;
|
||||
if (data.value("status").toString() == "success" && data.value("params").toMap().value("success").toBool()) {
|
||||
m_pendingPushButtonTransaction = data.value("params").toMap().value("transactionId").toInt();
|
||||
} else {
|
||||
emit pushButtonAuthFailed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,48 +267,58 @@ void JsonRpcClient::dataReceived(const QByteArray &data)
|
|||
if (dataMap.value("id").toInt() == 0) {
|
||||
m_initialSetupRequired = dataMap.value("initialSetupRequired").toBool();
|
||||
m_authenticationRequired = dataMap.value("authenticationRequired").toBool();
|
||||
m_pushButtonAuthAvailable = dataMap.value("pushButtonAuthAvailable").toBool();
|
||||
qDebug() << "Handshake received" << "initRequired:" << m_initialSetupRequired << "authRequired:" << m_authenticationRequired << "pushButtonAvailable:" << m_pushButtonAuthAvailable;;
|
||||
m_serverUuid = dataMap.value("uuid").toString();
|
||||
emit pushButtonAuthAvailableChanged();
|
||||
|
||||
QString protoVersionString = dataMap.value("protocol version").toString();
|
||||
if (!protoVersionString.contains('.')) {
|
||||
protoVersionString.prepend("0.");
|
||||
}
|
||||
|
||||
QVersionNumber minimumRequiredVersion = QVersionNumber(1, 0);
|
||||
QVersionNumber protocolVersion = QVersionNumber::fromString(protoVersionString);
|
||||
if (protocolVersion < minimumRequiredVersion) {
|
||||
m_connection->disconnect();
|
||||
emit invalidProtocolVersion(protocolVersion.toString(), minimumRequiredVersion.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_initialSetupRequired) {
|
||||
emit initialSetupRequiredChanged();
|
||||
} else if (m_authenticationRequired) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_authenticationRequired) {
|
||||
QSettings settings;
|
||||
settings.beginGroup("jsonTokens");
|
||||
m_token = settings.value(m_serverUuid).toByteArray();
|
||||
settings.endGroup();
|
||||
emit authenticationRequiredChanged();
|
||||
|
||||
if (!m_token.isEmpty()) {
|
||||
setNotificationsEnabled(true);
|
||||
if (m_token.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_connected = true;
|
||||
emit connectedChanged(true);
|
||||
|
||||
QVersionNumber minimumRequiredVersion = QVersionNumber(1, 0);
|
||||
QVersionNumber protocolVersion = QVersionNumber::fromString(protoVersionString);
|
||||
if (protocolVersion < minimumRequiredVersion) {
|
||||
m_connection->disconnect();
|
||||
emit invalidProtocolVersion(protocolVersion.toString(), minimumRequiredVersion.toString());
|
||||
}
|
||||
setNotificationsEnabled(true);
|
||||
}
|
||||
|
||||
// check if this is a reply to a request
|
||||
int commandId = dataMap.value("id").toInt();
|
||||
JsonRpcReply *reply = m_replies.take(commandId);
|
||||
if (reply) {
|
||||
// qDebug() << QString("JsonRpc: got response for %1.%2: %3").arg(reply->nameSpace(), reply->method(), QString::fromUtf8(jsonDoc.toJson(QJsonDocument::Indented))) << reply->callback() << reply->callback();
|
||||
qDebug() << QString("JsonRpc: got response for %1.%2: %3").arg(reply->nameSpace(), reply->method(), QString::fromUtf8(jsonDoc.toJson(QJsonDocument::Indented))) << reply->callback() << reply->callback();
|
||||
|
||||
if (dataMap.value("status").toString() == "unauthorized") {
|
||||
qWarning() << "Something's off with the token";
|
||||
m_authenticationRequired = true;
|
||||
m_token.clear();
|
||||
QSettings settings;
|
||||
settings.beginGroup("jsonTokens");
|
||||
settings.setValue(m_serverUuid, m_token);
|
||||
settings.endGroup();
|
||||
emit authenticationRequiredChanged();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class JsonRpcClient : public JsonHandler
|
|||
Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
|
||||
Q_PROPERTY(bool initialSetupRequired READ initialSetupRequired NOTIFY initialSetupRequiredChanged)
|
||||
Q_PROPERTY(bool authenticationRequired READ authenticationRequired NOTIFY authenticationRequiredChanged)
|
||||
Q_PROPERTY(bool pushButtonAuthAvailable READ pushButtonAuthAvailable NOTIFY pushButtonAuthAvailableChanged)
|
||||
|
||||
public:
|
||||
explicit JsonRpcClient(NymeaConnection *connection, QObject *parent = 0);
|
||||
|
|
@ -52,21 +53,24 @@ public:
|
|||
bool connected() const;
|
||||
bool initialSetupRequired() const;
|
||||
bool authenticationRequired() const;
|
||||
bool pushButtonAuthAvailable() const;
|
||||
|
||||
// ui methods
|
||||
Q_INVOKABLE int createUser(const QString &username, const QString &password);
|
||||
Q_INVOKABLE int authenticate(const QString &username, const QString &password, const QString &deviceName);
|
||||
Q_INVOKABLE int requestPushButtonAuth(const QString &deviceName);
|
||||
|
||||
// json handler
|
||||
Q_INVOKABLE void processAuthenticate(const QVariantMap &data);
|
||||
Q_INVOKABLE void processCreateUser(const QVariantMap &data);
|
||||
|
||||
signals:
|
||||
void initialSetupRequiredChanged();
|
||||
void authenticationRequiredChanged();
|
||||
void pushButtonAuthAvailableChanged();
|
||||
void connectedChanged(bool connected);
|
||||
void tokenChanged();
|
||||
void invalidProtocolVersion(const QString &actualVersion, const QString &minimumVersion);
|
||||
void authenticationFailed();
|
||||
void pushButtonAuthFailed();
|
||||
void createUserFailed(const QString &error);
|
||||
|
||||
void responseReceived(const int &commandId, const QVariantMap &response);
|
||||
|
||||
|
|
@ -86,12 +90,22 @@ private:
|
|||
bool m_connected = false;
|
||||
bool m_initialSetupRequired = false;
|
||||
bool m_authenticationRequired = false;
|
||||
bool m_pushButtonAuthAvailable = false;
|
||||
int m_pendingPushButtonTransaction = -1;
|
||||
QString m_serverUuid;
|
||||
QByteArray m_token;
|
||||
QByteArray m_receiveBuffer;
|
||||
|
||||
void setNotificationsEnabled(bool enabled);
|
||||
|
||||
// json handler
|
||||
Q_INVOKABLE void processAuthenticate(const QVariantMap &data);
|
||||
Q_INVOKABLE void processCreateUser(const QVariantMap &data);
|
||||
Q_INVOKABLE void processRequestPushButtonAuth(const QVariantMap &data);
|
||||
|
||||
Q_INVOKABLE void setNotificationsEnabledResponse(const QVariantMap ¶ms);
|
||||
Q_INVOKABLE void notificationReceived(const QVariantMap &data);
|
||||
|
||||
void sendRequest(const QVariantMap &request);
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -88,11 +88,29 @@ void NymeaConnection::onSslErrors(const QList<QSslError> &errors)
|
|||
QByteArray storedFingerPrint = settings.value(m_currentUrl.toString()).toByteArray();
|
||||
settings.endGroup();
|
||||
|
||||
if (storedFingerPrint == error.certificate().digest(QCryptographicHash::Sha256).toBase64()) {
|
||||
QByteArray certificateFingerprint;
|
||||
QByteArray digest = error.certificate().digest(QCryptographicHash::Sha256);
|
||||
for (int i = 0; i < digest.length(); i++) {
|
||||
if (certificateFingerprint.length() > 0) {
|
||||
certificateFingerprint.append(":");
|
||||
}
|
||||
certificateFingerprint.append(digest.mid(i,1).toHex().toUpper());
|
||||
}
|
||||
|
||||
if (storedFingerPrint == certificateFingerprint) {
|
||||
ignoredErrors.append(error);
|
||||
} else {
|
||||
// QString cn = error.certificate().issuerInfo(QSslCertificate::CommonName);
|
||||
emit verifyConnectionCertificate(error.certificate().issuerInfo(QSslCertificate::CommonName).first(), error.certificate().digest(QCryptographicHash::Sha256).toBase64());
|
||||
QStringList info;
|
||||
info << tr("Common Name:") << error.certificate().issuerInfo(QSslCertificate::CommonName);
|
||||
info << tr("Oragnisation:") <<error.certificate().issuerInfo(QSslCertificate::Organization);
|
||||
info << tr("Locality:") << error.certificate().issuerInfo(QSslCertificate::LocalityName);
|
||||
info << tr("Oragnisational Unit:")<< error.certificate().issuerInfo(QSslCertificate::OrganizationalUnitName);
|
||||
info << tr("Country:")<< error.certificate().issuerInfo(QSslCertificate::CountryName);
|
||||
// info << tr("State:")<< error.certificate().issuerInfo(QSslCertificate::StateOrProvinceName);
|
||||
// info << tr("Name Qualifier:")<< error.certificate().issuerInfo(QSslCertificate::DistinguishedNameQualifier);
|
||||
// info << tr("Email:")<< error.certificate().issuerInfo(QSslCertificate::EmailAddress);
|
||||
|
||||
emit verifyConnectionCertificate(info, certificateFingerprint);
|
||||
}
|
||||
} else {
|
||||
// Reject the connection on all other errors...
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public:
|
|||
void sendData(const QByteArray &data);
|
||||
|
||||
signals:
|
||||
void verifyConnectionCertificate(const QString &commonName, const QByteArray &fingerprint);
|
||||
void verifyConnectionCertificate(const QStringList &issuerInfo, const QByteArray &fingerprint);
|
||||
void connectedChanged(bool connected);
|
||||
void connectionError();
|
||||
void dataAvailable(const QByteArray &data);
|
||||
|
|
|
|||
|
|
@ -142,5 +142,7 @@
|
|||
<file>ui/magic/SelectStateDescriptorPage.qml</file>
|
||||
<file>ui/images/select-none.svg</file>
|
||||
<file>ui/images/edit.svg</file>
|
||||
<file>ui/PushButtonAuthPage.qml</file>
|
||||
<file>ui/images/dialog-error-symbolic.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ Page {
|
|||
target: Engine.connection
|
||||
onVerifyConnectionCertificate: {
|
||||
print("verify cert!")
|
||||
certDialog.commonName = commonName
|
||||
certDialog.issuerInfo = issuerInfo
|
||||
certDialog.fingerprint = fingerprint
|
||||
certDialog.open();
|
||||
}
|
||||
|
|
@ -156,32 +156,78 @@ Page {
|
|||
|
||||
Dialog {
|
||||
id: certDialog
|
||||
width: parent.width * .8
|
||||
height: parent.height * .8
|
||||
width: Math.min(parent.width * .9, 400)
|
||||
height: content.height
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
standardButtons: Dialog.Yes | Dialog.No
|
||||
|
||||
property var fingerprint
|
||||
property string commonName
|
||||
property var issuerInfo
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors { left: parent.left; right: parent.right; top: parent.top }
|
||||
height: childrenRect.height
|
||||
spacing: app.margins
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: app.margins
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize * 2
|
||||
Layout.preferredWidth: height
|
||||
name: "../images/dialog-warning-symbolic.svg"
|
||||
color: app.guhAccent
|
||||
}
|
||||
|
||||
Label {
|
||||
id: titleLabel
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Warning")
|
||||
color: app.guhAccent
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: "The authenticity of this nymea box cannot be verified. Do you want to trust this device?"
|
||||
text: "The authenticity of this nymea box cannot be verified."
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: "Device name: " + certDialog.commonName
|
||||
text: "If this is the first time you connect to this box, this is expected. Once you trust a box, you should never see this message again for that one. If you see this message multiple times for the same box, something suspicious is going on!"
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
columns: 2
|
||||
|
||||
Repeater {
|
||||
model: certDialog.issuerInfo
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
text: "Fingerprint: " + certDialog.fingerprint
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: "Do you want to trust this device?"
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
|
|
|
|||
|
|
@ -14,9 +14,37 @@ Page {
|
|||
onBackPressed: root.backPressed()
|
||||
}
|
||||
|
||||
|
||||
Connections {
|
||||
target: Engine.jsonRpcClient
|
||||
onAuthenticationFailed: {
|
||||
var popup = errorDialog.createObject(root)
|
||||
popup.text = qsTr("Sorry, that wasn't right. Try again please.")
|
||||
popup.open();
|
||||
}
|
||||
onCreateUserFailed: {
|
||||
print("create user failed")
|
||||
var text
|
||||
switch (error) {
|
||||
case "UserErrorInvalidUserId":
|
||||
text = qsTr("The email you've entered isn't valid.");
|
||||
break;
|
||||
case "UserErrorBadPassword":
|
||||
text = qsTr("The password you've chose is too weak.");
|
||||
break;
|
||||
default:
|
||||
text = qsTr("An error happened creating the user.");
|
||||
}
|
||||
// var popup = errorDialog.createObject(root, {title: qsTr("Error creating user"), text: text})
|
||||
var popup = errorDialog.createObject(root, {title: "Error creating user", text: text})
|
||||
popup.open();
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
spacing: app.margins
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
|
@ -26,29 +54,66 @@ Page {
|
|||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Username:"
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
text: "Your e-mail address:"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: usernameTextField
|
||||
Layout.fillWidth: true
|
||||
inputMethodHints: Qt.ImhEmailCharactersOnly
|
||||
placeholderText: "john.smith@cooldomain.com"
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
id: usernameTextField
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
inputMethodHints: Qt.ImhEmailCharactersOnly
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Password:"
|
||||
}
|
||||
TextField {
|
||||
id: passwordTextField
|
||||
Layout.fillWidth: true
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
visible: Engine.jsonRpcClient.initialSetupRequired
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Confirm password:"
|
||||
}
|
||||
TextField {
|
||||
id: confirmPasswordTextField
|
||||
Layout.fillWidth: true
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Password:"
|
||||
}
|
||||
TextField {
|
||||
id: passwordTextField
|
||||
Layout.fillWidth: true
|
||||
echoMode: TextInput.Password
|
||||
visible: Engine.jsonRpcClient.initialSetupRequired
|
||||
opacity: (passwordTextField.text.length > 0 && passwordTextField.text.length < 8) || passwordTextField.text != confirmPasswordTextField.text ? 1 : 0
|
||||
text: passwordTextField.text.length < 8 ? qsTr("This password isn't long enought to be secure, add some more characters please.")
|
||||
: qsTr("The passwords don't match.")
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.preferredHeight: confirmPasswordTextField.height * 2
|
||||
color: app.guhAccent
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: "OK"
|
||||
enabled: usernameTextField.text.length >= 5 && passwordTextField.text.length >= 8
|
||||
&& (!Engine.jsonRpcClient.initialSetupRequired || confirmPasswordTextField.text == passwordTextField.text)
|
||||
onClicked: {
|
||||
console.log("foooo")
|
||||
if (Engine.jsonRpcClient.initialSetupRequired) {
|
||||
print("create user")
|
||||
Engine.jsonRpcClient.createUser(usernameTextField.text, passwordTextField.text);
|
||||
|
|
@ -63,4 +128,11 @@ Page {
|
|||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: errorDialog
|
||||
ErrorDialog {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import Mea 1.0
|
||||
import "components"
|
||||
|
||||
Page {
|
||||
id: root
|
||||
signal backPressed();
|
||||
|
||||
header: GuhHeader {
|
||||
text: "Welcome to nymea!"
|
||||
backButtonVisible: true
|
||||
onBackPressed: {
|
||||
root.backPressed();
|
||||
}
|
||||
}
|
||||
|
||||
Component.objectName: {
|
||||
Engine.jsonRpcClient.requestPushButtonAuth("");
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Engine.jsonRpcClient
|
||||
onPushButtonAuthFailed: {
|
||||
var popup = errorDialog.createObject(root)
|
||||
popup.text = qsTr("Sorry, something went wrong during the setup. Try again please.")
|
||||
popup.open();
|
||||
popup.accepted.connect(function() {root.backPressed()})
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter }
|
||||
anchors.margins: app.margins
|
||||
spacing: app.margins
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: app.margins
|
||||
|
||||
ColorIcon {
|
||||
height: app.iconSize * 2
|
||||
width: height
|
||||
color: app.guhAccent
|
||||
name: "../images/info.svg"
|
||||
}
|
||||
|
||||
Label {
|
||||
color: app.guhAccent
|
||||
text: qsTr("Authentication required")
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Please press the button on your nymea box to authenticate this device."
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: errorDialog
|
||||
ErrorDialog {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Layouts 1.2
|
||||
|
||||
Dialog {
|
||||
width: parent.width * .6
|
||||
height: parent.height * .6
|
||||
id: root
|
||||
width: Math.min(parent.width * .6, 400)
|
||||
// height: content.height
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
|
||||
|
|
@ -12,9 +14,40 @@ Dialog {
|
|||
|
||||
standardButtons: Dialog.Ok
|
||||
|
||||
Label {
|
||||
id: contentLabel
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
header: Item {
|
||||
implicitHeight: headerRow.height + app.margins * 2
|
||||
implicitWidth: parent.width
|
||||
RowLayout {
|
||||
id: headerRow
|
||||
anchors { left: parent.left; right: parent.right; top: parent.top; margins: app.margins }
|
||||
spacing: app.margins
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize * 2
|
||||
Layout.preferredWidth: height
|
||||
name: "../images/dialog-error-symbolic.svg"
|
||||
color: app.guhAccent
|
||||
}
|
||||
|
||||
Label {
|
||||
id: titleLabel
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: root.title
|
||||
color: app.guhAccent
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
height: childrenRect.height
|
||||
|
||||
Label {
|
||||
id: contentLabel
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,182 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg4874"
|
||||
version="1.1"
|
||||
inkscape:version="0.91+devel r"
|
||||
viewBox="0 0 96 96.000001"
|
||||
sodipodi:docname="dialog-error-symbolic.svg">
|
||||
<defs
|
||||
id="defs4876" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="7.024999"
|
||||
inkscape:cx="-10.960861"
|
||||
inkscape:cy="37.302478"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g4780"
|
||||
showgrid="true"
|
||||
showborder="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
inkscape:snap-center="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:snap-global="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5451"
|
||||
empspacing="8" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="8,-8.0000001"
|
||||
id="guide4063" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="4,-8.0000001"
|
||||
id="guide4065" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,88.000001"
|
||||
id="guide4067" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,92.000001"
|
||||
id="guide4069" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="104,4"
|
||||
id="guide4071" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-5,8.0000001"
|
||||
id="guide4073" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="92,-8.0000001"
|
||||
id="guide4075" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="88,-8.0000001"
|
||||
id="guide4077" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,84.000001"
|
||||
id="guide4074" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="12,-8.0000001"
|
||||
id="guide4076" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-5,12"
|
||||
id="guide4078" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="84,-9.0000001"
|
||||
id="guide4080" />
|
||||
<sodipodi:guide
|
||||
position="48,-8.0000001"
|
||||
orientation="1,0"
|
||||
id="guide4170" />
|
||||
<sodipodi:guide
|
||||
position="-8,48"
|
||||
orientation="0,1"
|
||||
id="guide4172" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4879">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(67.857146,-78.50504)">
|
||||
<g
|
||||
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
|
||||
id="g4845"
|
||||
style="display:inline">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="next01.png"
|
||||
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
|
||||
id="g4778"
|
||||
inkscape:label="Layer 1">
|
||||
<g
|
||||
transform="matrix(-1,0,0,1,575.99999,611)"
|
||||
id="g4780"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"
|
||||
id="rect4782"
|
||||
width="96.037987"
|
||||
height="96"
|
||||
x="-438.00244"
|
||||
y="345.36221"
|
||||
transform="scale(-1,1)" />
|
||||
<path
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#808080;fill-opacity:1;stroke:none"
|
||||
d="m 364.0904,368.96573 c -0.0215,-0.0161 -0.0354,-0.0404 -0.0567,-0.0566 -0.0253,-0.0201 -0.057,-0.0305 -0.0821,-0.0508 z"
|
||||
id="path4157" />
|
||||
<path
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#808080;fill-opacity:1;stroke:none"
|
||||
d="m 364.07673,417.80167 -0.13873,0.10742 c 0.0251,-0.0203 0.0569,-0.0307 0.0821,-0.0508 0.0214,-0.0162 0.0353,-0.0405 0.0567,-0.0566 z"
|
||||
id="path4344" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079107;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 432,393.36133 c 0,23.17236 -18.83538,42 -42.01562,42 -23.18025,0 -42.01563,-18.82764 -42.01563,-42 0,-23.17236 18.83538,-42 42.01563,-42 23.18024,0 42.01562,18.82764 42.01562,42 z m -4.00195,0 c 0,-21.00932 -16.99476,-37.99805 -38.01367,-37.99805 -21.01892,0 -38.01563,16.98873 -38.01563,37.99805 0,21.00931 16.99671,37.99804 38.01563,37.99805 21.01891,0 38.01367,-16.98874 38.01367,-37.99805 z"
|
||||
id="path4116"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 406.57617,373.94727 -36.01367,36 2.82812,2.83007 36.01368,-36 -2.82813,-2.83007 z"
|
||||
id="path4305"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 373.39062,373.94727 -2.82812,2.83007 36.01367,36 2.82813,-2.83007 -36.01368,-36 z"
|
||||
id="path4307"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.8 KiB |
|
|
@ -42,7 +42,7 @@ ApplicationWindow {
|
|||
Connections {
|
||||
target: Engine.jsonRpcClient
|
||||
onConnectedChanged: {
|
||||
print("json client connected changed")
|
||||
print("json client connected changed", Engine.jsonRpcClient.connected)
|
||||
if (Engine.jsonRpcClient.connected) {
|
||||
settings.lastConnectedHost = Engine.connection.url
|
||||
}
|
||||
|
|
@ -68,14 +68,33 @@ ApplicationWindow {
|
|||
}
|
||||
|
||||
function init() {
|
||||
print("calling init. Auth required:", Engine.jsonRpcClient.authenticationRequired, "initial setup required:", Engine.jsonRpcClient.initialSetupRequired, "jsonrpc connected:", Engine.jsonRpcClient.connected)
|
||||
pageStack.clear()
|
||||
discovery.discovering = false;
|
||||
if (!Engine.connection.connected) {
|
||||
pageStack.push(Qt.resolvedUrl("ConnectPage.qml"))
|
||||
print("starting discovery")
|
||||
discovery.discovering = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Engine.jsonRpcClient.authenticationRequired || Engine.jsonRpcClient.initialSetupRequired) {
|
||||
var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml"));
|
||||
page.backPressed.connect(function() {
|
||||
settings.lastConnectedHost = "";
|
||||
Engine.connection.disconnect()
|
||||
})
|
||||
if (Engine.jsonRpcClient.pushButtonAuthAvailable) {
|
||||
print("opening push button auth")
|
||||
var page = pageStack.push(Qt.resolvedUrl("PushButtonAuthPage.qml"))
|
||||
page.backPressed.connect(function() {
|
||||
settings.lastConnectedHost = "";
|
||||
Engine.connection.disconnect();
|
||||
init();
|
||||
})
|
||||
} else {
|
||||
var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml"));
|
||||
page.backPressed.connect(function() {
|
||||
settings.lastConnectedHost = "";
|
||||
Engine.connection.disconnect()
|
||||
init();
|
||||
})
|
||||
}
|
||||
} else if (Engine.jsonRpcClient.connected) {
|
||||
pageStack.push(Qt.resolvedUrl("MainPage.qml"))
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in New Issue