Add user database rotation if the database is broken.
This commit is contained in:
parent
19a3a93980
commit
844f7a53a8
@ -514,7 +514,7 @@ void NymeaCore::init() {
|
|||||||
m_ruleEngine = new RuleEngine(this);
|
m_ruleEngine = new RuleEngine(this);
|
||||||
|
|
||||||
qCDebug(dcApplication()) << "Creating User Manager";
|
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";
|
qCDebug(dcApplication) << "Creating Server Manager";
|
||||||
m_serverManager = new ServerManager(m_configuration, this);
|
m_serverManager = new ServerManager(m_configuration, this);
|
||||||
|
|||||||
@ -35,16 +35,28 @@
|
|||||||
|
|
||||||
namespace nymeaserver {
|
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 = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), "users");
|
||||||
m_db.setDatabaseName(NymeaSettings::settingsPath() + "/user-db.sqlite");
|
m_db.setDatabaseName(dbName);
|
||||||
|
|
||||||
if (!m_db.open()) {
|
qCDebug(dcUserManager()) << "Opening user database" << m_db.databaseName();
|
||||||
qCWarning(dcUserManager) << "Error opening users database:" << m_db.lastError().driverText();
|
|
||||||
return;
|
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);
|
m_pushButtonDBusService = new PushButtonDBusService("/io/guh/nymead/UserManager", this);
|
||||||
connect(m_pushButtonDBusService, &PushButtonDBusService::pushButtonPressed, this, &UserManager::onPushButtonPressed);
|
connect(m_pushButtonDBusService, &PushButtonDBusService::pushButtonPressed, this, &UserManager::onPushButtonPressed);
|
||||||
@ -286,13 +298,49 @@ bool UserManager::verifyToken(const QByteArray &token)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserManager::initDB()
|
bool UserManager::initDB()
|
||||||
{
|
{
|
||||||
if (!m_db.tables().contains("users")) {
|
m_db.close();
|
||||||
m_db.exec("CREATE TABLE users (username VARCHAR(40) UNIQUE, password VARCHAR(100), salt VARCHAR(100));");
|
|
||||||
|
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")) {
|
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));");
|
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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,7 @@ public:
|
|||||||
UserErrorPermissionDenied
|
UserErrorPermissionDenied
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit UserManager(QObject *parent = 0);
|
explicit UserManager(const QString &dbName, QObject *parent = 0);
|
||||||
|
|
||||||
bool initRequired() const;
|
bool initRequired() const;
|
||||||
QStringList users() const;
|
QStringList users() const;
|
||||||
@ -68,7 +68,8 @@ signals:
|
|||||||
void pushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
|
void pushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initDB();
|
bool initDB();
|
||||||
|
void rotate(const QString &dbName);
|
||||||
bool validateUsername(const QString &username) const;
|
bool validateUsername(const QString &username) const;
|
||||||
bool validateToken(const QByteArray &token) const;
|
bool validateToken(const QByteArray &token) const;
|
||||||
|
|
||||||
|
|||||||
@ -21,3 +21,4 @@ SUBDIRS = versioning \
|
|||||||
#coap \ # temporary removed until fixed
|
#coap \ # temporary removed until fixed
|
||||||
configurations \
|
configurations \
|
||||||
timemanager \
|
timemanager \
|
||||||
|
userloading \
|
||||||
|
|||||||
85
tests/auto/userloading/testuserloading.cpp
Normal file
85
tests/auto/userloading/testuserloading.cpp
Normal 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)
|
||||||
BIN
tests/auto/userloading/user-db-broken.sqlite
Executable file
BIN
tests/auto/userloading/user-db-broken.sqlite
Executable file
Binary file not shown.
6
tests/auto/userloading/userloading.pro
Normal file
6
tests/auto/userloading/userloading.pro
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
include(../../../nymea.pri)
|
||||||
|
include(../autotests.pri)
|
||||||
|
|
||||||
|
RESOURCES += userloading.qrc
|
||||||
|
TARGET = userloading
|
||||||
|
SOURCES += testuserloading.cpp
|
||||||
5
tests/auto/userloading/userloading.qrc
Normal file
5
tests/auto/userloading/userloading.qrc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
|
<qresource>
|
||||||
|
<file>user-db-broken.sqlite</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
Reference in New Issue
Block a user