Refactor and update input validation
This change fixes input validation for the following cases: - Check that entered account names are still available - Working validation for time steps (input mask was completely broken) - Allow longer tokens: liboath is no longer used, Keysmith can handle it Additionally the QML code is refactored significantly: - Extracted the main accounts overview page - Extracted the add an account page - Completed the internal renaming of "Oath" to "Keysmith" for QML typesmaster
parent
2a9c80fff5
commit
768ccdba97
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||
*/
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Controls 2.0 as Controls
|
||||
import org.kde.kirigami 2.8 as Kirigami
|
||||
|
||||
import Keysmith.Application 1.0
|
||||
import Keysmith.Models 1.0 as Models
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
id: root
|
||||
/*
|
||||
* Explicitly opt-out of scroll-to-refresh/drag-to-refresh behaviour
|
||||
* Underlying model implementations don't offer the hooks for that.
|
||||
*/
|
||||
supportsRefreshing: false
|
||||
title: i18nc("@title:window", "Accounts")
|
||||
|
||||
signal accountWanted
|
||||
property bool addActionEnabled : true
|
||||
property Models.AccountListModel accounts: Keysmith.accountListModel()
|
||||
|
||||
Component {
|
||||
id: mainListDelegate
|
||||
AccountEntryView {
|
||||
account: model.account
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: mainList
|
||||
model: accounts
|
||||
delegate: Kirigami.DelegateRecycler {
|
||||
width: parent ? parent.width : implicitWidth
|
||||
sourceComponent: mainListDelegate
|
||||
}
|
||||
}
|
||||
|
||||
actions.main: Kirigami.Action {
|
||||
id: addAction
|
||||
text: i18n("Add")
|
||||
iconName: "list-add"
|
||||
visible: addActionEnabled
|
||||
onTriggered: {
|
||||
root.accountWanted();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||
*/
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Controls 2.0 as Controls
|
||||
import org.kde.kirigami 2.8 as Kirigami
|
||||
|
||||
import Keysmith.Application 1.0
|
||||
import Keysmith.Models 1.0 as Models
|
||||
import Keysmith.Validators 1.0 as Validators
|
||||
|
||||
Kirigami.Page {
|
||||
id: root
|
||||
title: i18nc("@title:window", "Add new account")
|
||||
signal dismissed
|
||||
property Models.AccountListModel accounts: Keysmith.accountListModel()
|
||||
|
||||
ColumnLayout {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
Kirigami.FormLayout {
|
||||
Controls.TextField {
|
||||
id: accountName
|
||||
Kirigami.FormData.label: i18nc("@label:textbox", "Account Name:")
|
||||
validator: Validators.AccountNameValidator {
|
||||
accounts: root.accounts
|
||||
}
|
||||
}
|
||||
}
|
||||
TokenDetailsForm {
|
||||
id: tokenDetails
|
||||
}
|
||||
}
|
||||
|
||||
actions.main: Kirigami.Action {
|
||||
text: i18n("Add")
|
||||
iconName: "answer-correct"
|
||||
onTriggered: {
|
||||
if (tokenDetails.isTotp) {
|
||||
accounts.addTotp(accountName.text, tokenDetails.secret, parseInt(tokenDetails.timeStep), tokenDetails.tokenLength);
|
||||
}
|
||||
if (tokenDetails.isHotp) {
|
||||
accounts.addHotp(accountName.text, tokenDetails.secret, parseInt(tokenDetails.counter), tokenDetails.tokenLength);
|
||||
}
|
||||
root.dismissed();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +1,10 @@
|
|||
/*
|
||||
* Copyright 2019 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 3 of
|
||||
* the License or any later version accepted by the membership of
|
||||
* KDE e.V. (or its successor approved by the membership of KDE
|
||||
* e.V.), which shall act as a proxy defined in Section 14 of
|
||||
* version 3 of the license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* SPDX-FileCopyrightText: 2019 Bhushan Shah <bshah@kde.org>
|
||||
* SPDX-FileCopyrightText: 2019-2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||
*/
|
||||
|
||||
import Oath.Validators 1.0 as Validators
|
||||
import Keysmith.Validators 1.0 as Validators
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Controls 2.0 as Controls
|
||||
|
@ -63,7 +48,9 @@ Kirigami.FormLayout {
|
|||
Kirigami.FormData.label: i18nc("@label:textbox", "Timer:")
|
||||
enabled: totpRadio.checked
|
||||
text: "30"
|
||||
inputMask: "0009"
|
||||
validator: IntValidator {
|
||||
bottom: 1
|
||||
}
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
}
|
||||
Controls.TextField {
|
||||
|
@ -77,8 +64,9 @@ Kirigami.FormLayout {
|
|||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
}
|
||||
/*
|
||||
* The liboath API is documented to support tokens which are
|
||||
* 6, 7 or 8 characters long only.
|
||||
* OATH tokens are derived from a 32bit value, base-10 encoded.
|
||||
* Meaning tokens should not be longer than 10 digits max.
|
||||
* In addition tokens must be 6 digits long at minimum.
|
||||
*
|
||||
* Make a virtue of it by offering a spinner for better UX
|
||||
*/
|
||||
|
@ -86,7 +74,7 @@ Kirigami.FormLayout {
|
|||
id: pinLengthField
|
||||
Kirigami.FormData.label: i18nc("@label:spinbox", "Token length:")
|
||||
from: 6
|
||||
to: 8
|
||||
to: 10
|
||||
value: 6
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,11 @@
|
|||
/*
|
||||
* Copyright 2019 Bhushan Shah <bshah@kde.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 3 of
|
||||
* the License or any later version accepted by the membership of
|
||||
* KDE e.V. (or its successor approved by the membership of KDE
|
||||
* e.V.), which shall act as a proxy defined in Section 14 of
|
||||
* version 3 of the license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* SPDX-FileCopyrightText: 2019 Bhushan Shah <bshah@kde.org>
|
||||
* SPDX-FileCopyrightText: 2019-2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||
*/
|
||||
|
||||
import Keysmith.Application 1.0
|
||||
import Keysmith.Models 1.0 as Models
|
||||
import Oath.Validators 1.0 as Validators
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Layouts 1.2
|
||||
|
@ -31,90 +15,30 @@ import org.kde.kirigami 2.4 as Kirigami
|
|||
Kirigami.ApplicationWindow {
|
||||
id: root
|
||||
|
||||
pageStack.initialPage: mainPageComponent
|
||||
pageStack.initialPage: accountsOverviewPage
|
||||
|
||||
property bool addActionEnabled: true
|
||||
|
||||
property Models.AccountListModel accounts: Keysmith.accountListModel()
|
||||
|
||||
Kirigami.Action {
|
||||
id: addAction
|
||||
text: i18n("Add")
|
||||
iconName: "list-add"
|
||||
visible: addActionEnabled
|
||||
onTriggered: {
|
||||
pageStack.push(addPageComponent);
|
||||
addActionEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: mainListDelegate
|
||||
AccountEntryView {
|
||||
account: model.account
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: mainPageComponent
|
||||
Kirigami.ScrollablePage {
|
||||
title: i18nc("@title:window", "Accounts")
|
||||
actions.main: addAction
|
||||
/*
|
||||
* Explicitly opt-out of scroll-to-refresh/drag-to-refresh behaviour
|
||||
* Underlying model implementations don't offer the hooks for that.
|
||||
*/
|
||||
supportsRefreshing: false
|
||||
ListView {
|
||||
id: mainList
|
||||
model: accounts
|
||||
delegate: Kirigami.DelegateRecycler {
|
||||
width: parent ? parent.width : implicitWidth
|
||||
sourceComponent: mainListDelegate
|
||||
}
|
||||
id: accountsOverviewPage
|
||||
AccountsOverview {
|
||||
accounts: root.accounts
|
||||
addActionEnabled: root.addActionEnabled
|
||||
onAccountWanted: {
|
||||
pageStack.push(addPageComponent);
|
||||
root.addActionEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: addPageComponent
|
||||
Kirigami.Page {
|
||||
title: i18nc("@title:window", "Add new account")
|
||||
actions.main: Kirigami.Action {
|
||||
text: i18n("Add")
|
||||
iconName: "answer-correct"
|
||||
onTriggered: {
|
||||
if (tokenDetails.isTotp) {
|
||||
accounts.addTotp(accountName.text, tokenDetails.secret, parseInt(tokenDetails.timeStep), tokenDetails.tokenLength);
|
||||
}
|
||||
if (tokenDetails.isHotp) {
|
||||
accounts.addHotp(accountName.text, tokenDetails.secret, parseInt(tokenDetails.counter), tokenDetails.tokenLength);
|
||||
}
|
||||
|
||||
pageStack.pop();
|
||||
addActionEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: addFormLayout
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
|
||||
Controls.TextField {
|
||||
id: accountName
|
||||
Kirigami.FormData.label: i18nc("@label:textbox", "Account Name:")
|
||||
validator: Validators.AccountNameValidator {
|
||||
id: nameValidator
|
||||
}
|
||||
}
|
||||
}
|
||||
TokenDetailsForm {
|
||||
id: tokenDetails
|
||||
}
|
||||
AddAccount {
|
||||
accounts: root.accounts
|
||||
onDismissed: {
|
||||
pageStack.pop();
|
||||
addActionEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
|
||||
#include "app/keysmith.h"
|
||||
#include "model/accounts.h"
|
||||
#include "../validators/qmlsupport.h"
|
||||
#include "validators/countervalidator.h"
|
||||
#include "validators/secretvalidator.h"
|
||||
|
||||
Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||
{
|
||||
|
@ -45,9 +46,11 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
|||
QQmlApplicationEngine engine;
|
||||
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
|
||||
|
||||
validators::registerValidatorTypes();
|
||||
qmlRegisterUncreatableType<model::SimpleAccountListModel>("Keysmith.Models", 1, 0, "AccountListModel", "Use the Keysmith singleton to obtain an AccountListModel");
|
||||
qmlRegisterUncreatableType<model::AccountView>("Keysmith.Models", 1, 0, "Account", "Use an AccountListModel from the Keysmith singleton to obtain an Account");
|
||||
qmlRegisterType<model::AccountNameValidator>("Keysmith.Validators", 1, 0, "AccountNameValidator");
|
||||
qmlRegisterType<validators::Base32Validator>("Keysmith.Validators", 1, 0, "Base32SecretValidator");
|
||||
qmlRegisterType<validators::UnsignedLongValidator>("Keysmith.Validators", 1, 0, "HOTPCounterValidator");
|
||||
qmlRegisterSingletonType<app::Keysmith>("Keysmith.Application", 1, 0, "Keysmith", [](QQmlEngine *qml, QJSEngine *js) -> QObject *
|
||||
{
|
||||
Q_UNUSED(qml);
|
||||
|
|
|
@ -3,5 +3,7 @@
|
|||
<file alias="main.qml">contents/ui/main.qml</file>
|
||||
<file alias="TokenDetailsForm.qml">contents/ui/TokenDetailsForm.qml</file>
|
||||
<file alias="AccountEntryView.qml">contents/ui/AccountEntryView.qml</file>
|
||||
<file alias="AccountsOverview.qml">contents/ui/AccountsOverview.qml</file>
|
||||
<file alias="AddAccount.qml">contents/ui/AddAccount.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -2,12 +2,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# SPDX-FileCopyrightText: 2019-2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||
#
|
||||
# This directory contains (custom) validators to support form field validation in the QML UI
|
||||
# For the QML bindings see: qmlsupport.cpp
|
||||
#
|
||||
|
||||
set(validator_SRCS
|
||||
qmlsupport.cpp
|
||||
countervalidator.cpp
|
||||
namevalidator.cpp
|
||||
secretvalidator.cpp
|
||||
|
@ -15,4 +11,4 @@ set(validator_SRCS
|
|||
)
|
||||
|
||||
add_library(validator_lib STATIC ${validator_SRCS})
|
||||
target_link_libraries(validator_lib Qt5::Core Qt5::Qml Qt5::Gui base32_lib)
|
||||
target_link_libraries(validator_lib Qt5::Core Qt5::Gui base32_lib)
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/*****************************************************************************
|
||||
* Copyright: 2019 Johan Ouwerkerk <jm.ouwerkerk@gmail.com> *
|
||||
* *
|
||||
* This project is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This project is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#include "qmlsupport.h"
|
||||
|
||||
#include "countervalidator.h"
|
||||
#include "namevalidator.h"
|
||||
#include "secretvalidator.h"
|
||||
|
||||
#include <QtQml>
|
||||
|
||||
namespace validators
|
||||
{
|
||||
void registerValidatorTypes(void)
|
||||
{
|
||||
qmlRegisterType<validators::NameValidator>("Oath.Validators", 1, 0, "AccountNameValidator");
|
||||
qmlRegisterType<validators::UnsignedLongValidator>("Oath.Validators", 1, 0, "HOTPCounterValidator");
|
||||
qmlRegisterType<validators::Base32Validator>("Oath.Validators", 1, 0, "Base32SecretValidator");
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*****************************************************************************
|
||||
* Copyright: 2019 Johan Ouwerkerk <jm.ouwerkerk@gmail.com> *
|
||||
* *
|
||||
* This project is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This project is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef VALIDATOR_QML_SUPPORT_H
|
||||
#define VALIDATOR_QML_SUPPORT_H
|
||||
|
||||
namespace validators
|
||||
{
|
||||
void registerValidatorTypes(void);
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue