refactor: introduce custom datetime validator
This change is a preparation for allowing the user to configure custom epochs for TOTP tokens Issues: #7master
parent
f632b5ecf3
commit
170d7f1811
|
@ -5,6 +5,8 @@
|
|||
|
||||
set(validator_lib_test_SRCS
|
||||
base32-validator.cpp
|
||||
datetime-parsing.cpp
|
||||
epoch-validator.cpp
|
||||
name-validator.cpp
|
||||
issuer-validator.cpp
|
||||
unsigned-long-validator.cpp
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||
*/
|
||||
|
||||
#include "validators/datetimevalidator.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
#include <QValidator>
|
||||
#include <QtDebug>
|
||||
|
||||
Q_DECLARE_METATYPE(std::optional<QDateTime>);
|
||||
|
||||
class DateTimeParsingSamplesTest: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void testParseDateTime(void);
|
||||
void testParseDateTime_data(void);
|
||||
};
|
||||
|
||||
|
||||
void DateTimeParsingSamplesTest::testParseDateTime(void)
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QTEST(validators::parseDateTime(input), "result");
|
||||
}
|
||||
|
||||
static void define_test_case(const QString &input, const std::optional<QDateTime> &result)
|
||||
{
|
||||
QTest::newRow(qPrintable(input)) << input << result;
|
||||
}
|
||||
|
||||
void DateTimeParsingSamplesTest::testParseDateTime_data(void)
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<std::optional<QDateTime>>("result");
|
||||
|
||||
|
||||
define_test_case(QLatin1String("1970-01-01T00:00:00Z"), std::optional<QDateTime>(QDateTime::fromMSecsSinceEpoch(0)));
|
||||
define_test_case(QLatin1String("1970-01-01T00:00:00.500Z"), std::optional<QDateTime>(QDateTime::fromMSecsSinceEpoch(500LL)));
|
||||
define_test_case(QLatin1String("01-01-1970 01:00"), std::nullopt);
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(DateTimeParsingSamplesTest)
|
||||
|
||||
#include "datetime-parsing.moc"
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||
*/
|
||||
|
||||
#include "test-util.h"
|
||||
|
||||
#include "validators/datetimevalidator.h"
|
||||
|
||||
using namespace validators::test;
|
||||
|
||||
static void define_valid_table(void)
|
||||
{
|
||||
define_test_case(QLatin1String("1970-01-01T00:00:00Z"), QLatin1String("1970-01-01T00:00:00Z"), QValidator::Acceptable);
|
||||
define_test_case(QLatin1String("1970-01-01T00:00:00.500Z"), QLatin1String("1970-01-01T00:00:00.500Z"), QValidator::Acceptable);
|
||||
define_test_case(QLatin1String("1969-12-31T23:05:00-01:00"), QLatin1String("1969-12-31T23:05:00-01:00"), QValidator::Acceptable);
|
||||
define_test_case(QLatin1String("1970-01-01T01:05:00+01:00"), QLatin1String("1970-01-01T01:05:00+01:00"), QValidator::Acceptable);
|
||||
}
|
||||
|
||||
static void define_invalid_table(void)
|
||||
{
|
||||
define_test_case(QLatin1String("Not a datetime"), QLatin1String("Not a datetime"), QValidator::Intermediate);
|
||||
define_test_case(QLatin1String("1980-01-01T00:00:00Z"), QLatin1String("1980-01-01T00:00:00Z"), QValidator::Invalid);
|
||||
define_test_case(QLatin1String("1969-12-31T23:05:00.001-01:00"), QLatin1String("1969-12-31T23:05:00.001-01:00"), QValidator::Invalid);
|
||||
define_test_case(QLatin1String("1970-01-01T01:05:00.001+01:00"), QLatin1String("1970-01-01T01:05:00.001+01:00"), QValidator::Invalid);
|
||||
}
|
||||
|
||||
static void define_empty_table(void)
|
||||
{
|
||||
define_test_case(QLatin1String(""), QLatin1String(""), QValidator::Intermediate);
|
||||
define_test_case(QLatin1String(" "), QLatin1String(""), QValidator::Intermediate);
|
||||
define_test_case(QLatin1String("\t"), QLatin1String(""), QValidator::Intermediate);
|
||||
define_test_case(QLatin1String("\r\n"), QLatin1String(""), QValidator::Intermediate);
|
||||
}
|
||||
|
||||
static void define_data(void)
|
||||
{
|
||||
define_empty_table();
|
||||
define_valid_table();
|
||||
define_invalid_table();
|
||||
}
|
||||
|
||||
static qint64 fake_clock(void)
|
||||
{
|
||||
return 300'000LL;
|
||||
}
|
||||
|
||||
DEFINE_VALIDATOR_TEST(EpochValidatorTest, validators::EpochValidator, define_data, fake_clock);
|
||||
|
||||
QTEST_APPLESS_MAIN(EpochValidatorTest)
|
||||
|
||||
#include "epoch-validator.moc"
|
|
@ -15,6 +15,7 @@
|
|||
#include "app/keysmith.h"
|
||||
#include "model/accounts.h"
|
||||
#include "validators/countervalidator.h"
|
||||
#include "validators/datetimevalidator.h"
|
||||
#include "validators/issuervalidator.h"
|
||||
#include "validators/secretvalidator.h"
|
||||
|
||||
|
@ -37,6 +38,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
|||
qmlRegisterUncreatableType<model::AccountView>("Keysmith.Models", 1, 0, "Account", "Use an AccountListModel from the Keysmith singleton to obtain an Account");
|
||||
qmlRegisterType<model::SortedAccountsListModel>("Keysmith.Models", 1, 0, "SortedAccountListModel");
|
||||
qmlRegisterType<model::AccountNameValidator>("Keysmith.Validators", 1, 0, "AccountNameValidator");
|
||||
qmlRegisterType<validators::EpochValidator>("Keysmith.Validators", 1, 0, "TOTPEpochValidator");
|
||||
qmlRegisterType<validators::IssuerValidator>("Keysmith.Validators", 1, 0, "AccountIssuerValidator");
|
||||
qmlRegisterType<validators::Base32Validator>("Keysmith.Validators", 1, 0, "Base32SecretValidator");
|
||||
qmlRegisterType<validators::UnsignedLongValidator>("Keysmith.Validators", 1, 0, "HOTPCounterValidator");
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
set(validator_SRCS
|
||||
countervalidator.cpp
|
||||
datetimevalidator.cpp
|
||||
namevalidator.cpp
|
||||
issuervalidator.cpp
|
||||
secretvalidator.cpp
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||
*/
|
||||
|
||||
#include "datetimevalidator.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace validators
|
||||
{
|
||||
std::optional<QDateTime> parseDateTime(const QString &input)
|
||||
{
|
||||
QDateTime result;
|
||||
|
||||
result = QDateTime::fromString(input, Qt::ISODateWithMs);
|
||||
if (result.isValid()) {
|
||||
return std::optional<QDateTime>(result);
|
||||
}
|
||||
|
||||
result = QDateTime::fromString(input, Qt::ISODate);
|
||||
if (result.isValid()) {
|
||||
return std::optional<QDateTime>(result);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
EpochValidator::EpochValidator(const std::function<qint64(void)> clock, QObject *parent) :
|
||||
QValidator(parent), m_clock(clock)
|
||||
{
|
||||
}
|
||||
|
||||
void EpochValidator::fixup(QString &input) const
|
||||
{
|
||||
input = validators::simplify_spaces(input);
|
||||
}
|
||||
|
||||
QValidator::State EpochValidator::validate(QString &input, int &pos) const
|
||||
{
|
||||
Q_UNUSED(pos);
|
||||
|
||||
const auto parsed = parseDateTime(input);
|
||||
if (!parsed) {
|
||||
return QValidator::Intermediate;
|
||||
}
|
||||
|
||||
return parsed->toMSecsSinceEpoch() <= m_clock() ? QValidator::Acceptable : QValidator::Invalid;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef DATETIME_VALIDATOR_H
|
||||
#define DATETIME_VALIDATOR_H
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QLocale>
|
||||
#include <QValidator>
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
namespace validators
|
||||
{
|
||||
std::optional<QDateTime> parseDateTime(const QString &input);
|
||||
|
||||
class EpochValidator: public QValidator
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EpochValidator(const std::function<qint64(void)> clock = &QDateTime::currentMSecsSinceEpoch, QObject *parent = nullptr);
|
||||
void fixup(QString &input) const override;
|
||||
QValidator::State validate(QString &input, int &pos) const override;
|
||||
private:
|
||||
const std::function<qint64(void)> m_clock;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue