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>
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*
|
* SPDX-FileCopyrightText: 2019 Bhushan Shah <bshah@kde.org>
|
||||||
* This program is free software; you can redistribute it and/or
|
* SPDX-FileCopyrightText: 2019-2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||||
* 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/>.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Oath.Validators 1.0 as Validators
|
import Keysmith.Validators 1.0 as Validators
|
||||||
import QtQuick 2.1
|
import QtQuick 2.1
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Controls 2.0 as Controls
|
import QtQuick.Controls 2.0 as Controls
|
||||||
|
@ -63,7 +48,9 @@ Kirigami.FormLayout {
|
||||||
Kirigami.FormData.label: i18nc("@label:textbox", "Timer:")
|
Kirigami.FormData.label: i18nc("@label:textbox", "Timer:")
|
||||||
enabled: totpRadio.checked
|
enabled: totpRadio.checked
|
||||||
text: "30"
|
text: "30"
|
||||||
inputMask: "0009"
|
validator: IntValidator {
|
||||||
|
bottom: 1
|
||||||
|
}
|
||||||
inputMethodHints: Qt.ImhDigitsOnly
|
inputMethodHints: Qt.ImhDigitsOnly
|
||||||
}
|
}
|
||||||
Controls.TextField {
|
Controls.TextField {
|
||||||
|
@ -77,8 +64,9 @@ Kirigami.FormLayout {
|
||||||
inputMethodHints: Qt.ImhDigitsOnly
|
inputMethodHints: Qt.ImhDigitsOnly
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* The liboath API is documented to support tokens which are
|
* OATH tokens are derived from a 32bit value, base-10 encoded.
|
||||||
* 6, 7 or 8 characters long only.
|
* 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
|
* Make a virtue of it by offering a spinner for better UX
|
||||||
*/
|
*/
|
||||||
|
@ -86,7 +74,7 @@ Kirigami.FormLayout {
|
||||||
id: pinLengthField
|
id: pinLengthField
|
||||||
Kirigami.FormData.label: i18nc("@label:spinbox", "Token length:")
|
Kirigami.FormData.label: i18nc("@label:spinbox", "Token length:")
|
||||||
from: 6
|
from: 6
|
||||||
to: 8
|
to: 10
|
||||||
value: 6
|
value: 6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,11 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2019 Bhushan Shah <bshah@kde.org>
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*
|
* SPDX-FileCopyrightText: 2019 Bhushan Shah <bshah@kde.org>
|
||||||
* This program is free software; you can redistribute it and/or
|
* SPDX-FileCopyrightText: 2019-2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||||
* 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/>.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Keysmith.Application 1.0
|
import Keysmith.Application 1.0
|
||||||
import Keysmith.Models 1.0 as Models
|
import Keysmith.Models 1.0 as Models
|
||||||
import Oath.Validators 1.0 as Validators
|
|
||||||
|
|
||||||
import QtQuick 2.1
|
import QtQuick 2.1
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
@ -31,90 +15,30 @@ import org.kde.kirigami 2.4 as Kirigami
|
||||||
Kirigami.ApplicationWindow {
|
Kirigami.ApplicationWindow {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
pageStack.initialPage: mainPageComponent
|
pageStack.initialPage: accountsOverviewPage
|
||||||
|
|
||||||
property bool addActionEnabled: true
|
property bool addActionEnabled: true
|
||||||
|
|
||||||
property Models.AccountListModel accounts: Keysmith.accountListModel()
|
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 {
|
Component {
|
||||||
id: mainListDelegate
|
id: accountsOverviewPage
|
||||||
AccountEntryView {
|
AccountsOverview {
|
||||||
account: model.account
|
accounts: root.accounts
|
||||||
}
|
addActionEnabled: root.addActionEnabled
|
||||||
}
|
onAccountWanted: {
|
||||||
|
pageStack.push(addPageComponent);
|
||||||
Component {
|
root.addActionEnabled = false;
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: addPageComponent
|
id: addPageComponent
|
||||||
Kirigami.Page {
|
AddAccount {
|
||||||
title: i18nc("@title:window", "Add new account")
|
accounts: root.accounts
|
||||||
actions.main: Kirigami.Action {
|
onDismissed: {
|
||||||
text: i18n("Add")
|
pageStack.pop();
|
||||||
iconName: "answer-correct"
|
addActionEnabled = true;
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,8 @@
|
||||||
|
|
||||||
#include "app/keysmith.h"
|
#include "app/keysmith.h"
|
||||||
#include "model/accounts.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[])
|
Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
@ -45,9 +46,11 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
engine.rootContext()->setContextObject(new KLocalizedContext(&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::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");
|
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 *
|
qmlRegisterSingletonType<app::Keysmith>("Keysmith.Application", 1, 0, "Keysmith", [](QQmlEngine *qml, QJSEngine *js) -> QObject *
|
||||||
{
|
{
|
||||||
Q_UNUSED(qml);
|
Q_UNUSED(qml);
|
||||||
|
|
|
@ -3,5 +3,7 @@
|
||||||
<file alias="main.qml">contents/ui/main.qml</file>
|
<file alias="main.qml">contents/ui/main.qml</file>
|
||||||
<file alias="TokenDetailsForm.qml">contents/ui/TokenDetailsForm.qml</file>
|
<file alias="TokenDetailsForm.qml">contents/ui/TokenDetailsForm.qml</file>
|
||||||
<file alias="AccountEntryView.qml">contents/ui/AccountEntryView.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>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -2,12 +2,8 @@
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
# SPDX-FileCopyrightText: 2019-2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
# 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
|
set(validator_SRCS
|
||||||
qmlsupport.cpp
|
|
||||||
countervalidator.cpp
|
countervalidator.cpp
|
||||||
namevalidator.cpp
|
namevalidator.cpp
|
||||||
secretvalidator.cpp
|
secretvalidator.cpp
|
||||||
|
@ -15,4 +11,4 @@ set(validator_SRCS
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(validator_lib STATIC ${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