Add user database rotation if the database is broken.

pull/135/head
Simon Stürz 2018-06-26 19:57:47 +02:00 committed by Michael Zanetti
parent 19a3a93980
commit 844f7a53a8
8 changed files with 158 additions and 12 deletions

View File

@ -514,7 +514,7 @@ void NymeaCore::init() {
m_ruleEngine = new RuleEngine(this);
qCDebug(dcApplication()) << "Creating User Manager";
m_userManager = new UserManager(this);
m_userManager = new UserManager(NymeaSettings::settingsPath() + "/user-db.sqlite", this);
qCDebug(dcApplication) << "Creating Server Manager";
m_serverManager = new ServerManager(m_configuration, this);

View File

@ -35,16 +35,28 @@
namespace nymeaserver {
UserManager::UserManager(QObject *parent) : QObject(parent)
UserManager::UserManager(const QString &dbName, QObject *parent):
QObject(parent)
{
m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), "users");
m_db.setDatabaseName(NymeaSettings::settingsPath() + "/user-db.sqlite");
m_db.setDatabaseName(dbName);
if (!m_db.open()) {
qCWarning(dcUserManager) << "Error opening users database:" << m_db.lastError().driverText();
return;
qCDebug(dcUserManager()) << "Opening user database" << m_db.databaseName();
if (!m_db.isValid()) {
qCWarning(dcUserManager()) << "The database is not valid:" << m_db.lastError().driverText() << m_db.lastError().databaseText();
rotate(m_db.databaseName());
}
if (!initDB()) {
qCWarning(dcUserManager()) << "Error initializing user database. Trying to correct it.";
if (QFileInfo(m_db.databaseName()).exists()) {
rotate(m_db.databaseName());
if (!initDB()) {
qCWarning(dcLogEngine()) << "Error fixing user database. Giving up. Users can't be stored.";
}
}
}
initDB();
m_pushButtonDBusService = new PushButtonDBusService("/io/guh/nymead/UserManager", this);
connect(m_pushButtonDBusService, &PushButtonDBusService::pushButtonPressed, this, &UserManager::onPushButtonPressed);
@ -286,13 +298,49 @@ bool UserManager::verifyToken(const QByteArray &token)
return true;
}
void UserManager::initDB()
bool UserManager::initDB()
{
if (!m_db.tables().contains("users")) {
m_db.exec("CREATE TABLE users (username VARCHAR(40) UNIQUE, password VARCHAR(100), salt VARCHAR(100));");
m_db.close();
if (!m_db.open()) {
qCWarning(dcUserManager()) << "Can't open user database. Init failed.";
return false;
}
if (!m_db.tables().contains("users")) {
qCDebug(dcUserManager()) << "Empty user database. Setting up metadata...";
m_db.exec("CREATE TABLE users (username VARCHAR(40) UNIQUE, password VARCHAR(100), salt VARCHAR(100));");
if (m_db.lastError().isValid()) {
qCWarning(dcUserManager) << "Error initualizing user database. Driver error:" << m_db.lastError().driverText() << "Database error:" << m_db.lastError().databaseText();
return false;
}
}
if (!m_db.tables().contains("tokens")) {
qCDebug(dcUserManager()) << "Empty user database. Setting up metadata...";
m_db.exec("CREATE TABLE tokens (id VARCHAR(40) UNIQUE, username VARCHAR(40), token VARCHAR(100) UNIQUE, creationdate DATETIME, devicename VARCHAR(40));");
if (m_db.lastError().isValid()) {
qCWarning(dcUserManager()) << "Error initualizing user database. Driver error:" << m_db.lastError().driverText() << "Database error:" << m_db.lastError().databaseText();
return false;
}
}
qCDebug(dcUserManager()) << "User database initialized successfully.";
return true;
}
void UserManager::rotate(const QString &dbName)
{
int index = 1;
while (QFileInfo(QString("%1.%2").arg(dbName).arg(index)).exists()) {
index++;
}
qCDebug(dcUserManager()) << "Backing up old database file to" << QString("%1.%2").arg(dbName).arg(index);
QFile f(dbName);
if (!f.rename(QString("%1.%2").arg(dbName).arg(index))) {
qCWarning(dcUserManager()) << "Error backing up old database.";
} else {
qCDebug(dcUserManager()) << "Successfully moved old database";
}
}

View File

@ -45,7 +45,7 @@ public:
UserErrorPermissionDenied
};
explicit UserManager(QObject *parent = 0);
explicit UserManager(const QString &dbName, QObject *parent = 0);
bool initRequired() const;
QStringList users() const;
@ -68,7 +68,8 @@ signals:
void pushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
private:
void initDB();
bool initDB();
void rotate(const QString &dbName);
bool validateUsername(const QString &username) const;
bool validateToken(const QByteArray &token) const;

View File

@ -21,3 +21,4 @@ SUBDIRS = versioning \
#coap \ # temporary removed until fixed
configurations \
timemanager \
userloading \

View File

@ -0,0 +1,85 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2018 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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, version 2 of the License. *
* *
**
* 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <QtTest>
#include "logging/logengine.h"
#include "logging/logvaluetool.h"
#include "usermanager.h"
using namespace nymeaserver;
class TestUserLoading: public QObject
{
Q_OBJECT
public:
TestUserLoading(QObject* parent = nullptr);
protected slots:
void initTestCase();
private slots:
void testLogfileRotation();
};
TestUserLoading::TestUserLoading(QObject *parent): QObject(parent)
{
Q_INIT_RESOURCE(userloading);
}
void TestUserLoading::initTestCase()
{
// Important for settings
QCoreApplication::instance()->setOrganizationName("nymea-test");
}
void TestUserLoading::testLogfileRotation()
{
// Create UserManager with log db from resource file
QString temporaryDbName = "/tmp/nymea-test/user-db-broken.sqlite";
QString rotatedDbName = "/tmp/nymea-test/user-db-broken.sqlite.1";
// Remove the files if there are some left
if (QFile::exists(temporaryDbName))
QVERIFY(QFile(temporaryDbName).remove());
if (QFile::exists(rotatedDbName))
QVERIFY(QFile(rotatedDbName).remove());
// Copy broken user db from resources to default settings path and set permissions
qDebug() << "Copy broken user db to" << temporaryDbName;
QVERIFY(QFile::copy(":/user-db-broken.sqlite", temporaryDbName));
QVERIFY(QFile::setPermissions(temporaryDbName, QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup | QFile::ReadOther));
QVERIFY(!QFile::exists(rotatedDbName));
UserManager *userManager = new UserManager(temporaryDbName, this);
QVERIFY(QFile::exists(rotatedDbName));
delete userManager;
QVERIFY(QFile(temporaryDbName).remove());
QVERIFY(QFile(rotatedDbName).remove());
}
#include "testuserloading.moc"
QTEST_MAIN(TestUserLoading)

Binary file not shown.

View File

@ -0,0 +1,6 @@
include(../../../nymea.pri)
include(../autotests.pri)
RESOURCES += userloading.qrc
TARGET = userloading
SOURCES += testuserloading.cpp

View File

@ -0,0 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>user-db-broken.sqlite</file>
</qresource>
</RCC>