refactor: introduce custom datetime validator

This change is a preparation for allowing the user to configure custom epochs for TOTP tokens

Issues: #7
master
Johan Ouwerkerk 2020-07-24 20:54:06 +02:00
parent f632b5ecf3
commit 170d7f1811
7 changed files with 187 additions and 0 deletions

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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");

View File

@ -5,6 +5,7 @@
set(validator_SRCS
countervalidator.cpp
datetimevalidator.cpp
namevalidator.cpp
issuervalidator.cpp
secretvalidator.cpp

View File

@ -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;
}
}

View File

@ -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