mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
- The first part
- Added CodeDocs config file for customization - Fixing LGTM alerts - LGTM bug fixed again - added token option to hyperion-remote - fix DBManager::getDB() - next bugfix - correct broken signal from SettingManager to Hyperion - Token list is created after the schema is fetched Signed-off-by: Paulchen-Panther <Paulchen-Panter@protonmail.com>
This commit is contained in:
226
include/db/AuthTable.h
Normal file
226
include/db/AuthTable.h
Normal file
@@ -0,0 +1,226 @@
|
||||
#pragma once
|
||||
|
||||
// hyperion
|
||||
#include <db/DBManager.h>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
// qt
|
||||
#include <QDateTime>
|
||||
#include <QUuid>
|
||||
|
||||
///
|
||||
/// @brief Authentication table interface
|
||||
///
|
||||
class AuthTable : public DBManager
|
||||
{
|
||||
|
||||
public:
|
||||
/// construct wrapper with auth table
|
||||
AuthTable(const QString& rootPath, QObject* parent = nullptr)
|
||||
: DBManager(parent)
|
||||
{
|
||||
// Init Hyperion database usage
|
||||
setRootPath(rootPath);
|
||||
setDB("hyperion");
|
||||
|
||||
// init Auth table
|
||||
setTable("auth");
|
||||
// create table columns
|
||||
createTable(QStringList()<<"user TEXT"<<"password BLOB"<<"token BLOB"<<"salt BLOB"<<"comment TEXT"<<"id TEXT"<<"created_at TEXT"<<"last_use TEXT");
|
||||
};
|
||||
~AuthTable(){};
|
||||
|
||||
///
|
||||
/// @brief Create a user record, if called on a existing user the auth is recreated
|
||||
/// @param[in] user The username
|
||||
/// @param[in] pw The password
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool createUser(const QString& user, const QString& pw)
|
||||
{
|
||||
// new salt
|
||||
QByteArray salt = QCryptographicHash::hash(QUuid::createUuid().toByteArray(), QCryptographicHash::Sha512).toHex();
|
||||
QVariantMap map;
|
||||
map["user"] = user;
|
||||
map["salt"] = salt;
|
||||
map["password"] = hashPasswordWithSalt(pw,salt);
|
||||
map["created_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user",user));
|
||||
return createRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Test if user record exists
|
||||
/// @param[in] user The user id
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool userExist(const QString& user)
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user",user));
|
||||
return recordExists(cond);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Test if a user is authorized for access with given pw.
|
||||
/// @param user The user name
|
||||
/// @param pw The password
|
||||
/// @return True on success else false
|
||||
///
|
||||
inline bool isUserAuthorized(const QString& user, const QString& pw)
|
||||
{
|
||||
if(userExist(user) && (calcPasswordHashOfUser(user, pw) == getPasswordHashOfUser(user)))
|
||||
{
|
||||
updateUserUsed(user);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Update 'last_use' column entry for the corresponding user
|
||||
/// @param[in] user The user to search for
|
||||
///
|
||||
inline void updateUserUsed(const QString& user)
|
||||
{
|
||||
QVariantMap map;
|
||||
map["last_use"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user", user));
|
||||
updateRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Test if token record exists, updates last_use on success
|
||||
/// @param[in] token The token id
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool tokenExist(const QString& token)
|
||||
{
|
||||
QVariantMap map;
|
||||
map["last_use"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("token", hashToken(token)));
|
||||
if(recordExists(cond))
|
||||
{
|
||||
// update it
|
||||
createRecord(cond,map);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Create a new token record with comment
|
||||
/// @param[in] token The token id as plaintext
|
||||
/// @param[in] comment The comment for the token (eg a human readable identifier)
|
||||
/// @param[in] id The id for the token
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool createToken(const QString& token, const QString& comment, const QString& id)
|
||||
{
|
||||
QVariantMap map;
|
||||
map["comment"] = comment;
|
||||
map["id"] = idExist(id) ? QUuid::createUuid().toString().remove("{").remove("}").left(5) : id;
|
||||
map["created_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("token", hashToken(token)));
|
||||
return createRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Delete token record by id
|
||||
/// @param[in] id The token id
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool deleteToken(const QString& id)
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("id", id));
|
||||
return deleteRecord(cond);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Get all 'comment', 'last_use' and 'id' column entries
|
||||
/// @return A vector of all lists
|
||||
///
|
||||
inline const QVector<QVariantMap> getTokenList()
|
||||
{
|
||||
QVector<QVariantMap> results;
|
||||
getRecords(results, QStringList() << "comment" << "id" << "last_use");
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Test if id exists
|
||||
/// @param[in] id The id
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool idExist(const QString& id)
|
||||
{
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("id", id));
|
||||
return recordExists(cond);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Get the passwort hash of a user from db
|
||||
/// @param user The user name
|
||||
/// @return password as hash
|
||||
///
|
||||
inline const QByteArray getPasswordHashOfUser(const QString& user)
|
||||
{
|
||||
QVariantMap results;
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user", user));
|
||||
getRecord(cond, results, QStringList()<<"password");
|
||||
|
||||
return results["password"].toByteArray();
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Calc the password hash of a user based on user name and password
|
||||
/// @param user The user name
|
||||
/// @param pw The password
|
||||
/// @return The calced password hash
|
||||
///
|
||||
inline const QByteArray calcPasswordHashOfUser(const QString& user, const QString& pw)
|
||||
{
|
||||
// get salt
|
||||
QVariantMap results;
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user", user));
|
||||
getRecord(cond, results, QStringList()<<"salt");
|
||||
|
||||
// calc
|
||||
return hashPasswordWithSalt(pw,results["salt"].toByteArray());
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Create a password hash of plaintex password + salt
|
||||
/// @param pw The plaintext password
|
||||
/// @param salt The salt
|
||||
/// @return The password hash with salt
|
||||
///
|
||||
inline const QByteArray hashPasswordWithSalt(const QString& pw, const QByteArray& salt)
|
||||
{
|
||||
return QCryptographicHash::hash(pw.toUtf8().append(salt), QCryptographicHash::Sha512).toHex();
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Create a token hash
|
||||
/// @param token The plaintext token
|
||||
/// @return The token hash
|
||||
///
|
||||
inline const QByteArray hashToken(const QString& token)
|
||||
{
|
||||
return QCryptographicHash::hash(token.toUtf8(), QCryptographicHash::Sha512).toHex();
|
||||
}
|
||||
};
|
130
include/db/DBManager.h
Normal file
130
include/db/DBManager.h
Normal file
@@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
#include <QPair>
|
||||
#include <QVector>
|
||||
|
||||
class QSqlDatabase;
|
||||
class QSqlQuery;
|
||||
|
||||
typedef QPair<QString,QVariant> CPair;
|
||||
typedef QVector<CPair> VectorPair;
|
||||
|
||||
///
|
||||
/// @brief Database interface for SQLite3.
|
||||
/// Inherit this class to create component specific methods based on this interface
|
||||
/// Usage: setTable(name) once before you use read/write actions
|
||||
/// To use another database use setDb(newDB) (defaults to "hyperion")
|
||||
///
|
||||
/// Incompatible functions with SQlite3:
|
||||
/// QSqlQuery::size() returns always -1
|
||||
///
|
||||
class DBManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DBManager(QObject* parent = nullptr);
|
||||
~DBManager();
|
||||
|
||||
/// set root path
|
||||
void setRootPath(const QString& rootPath);
|
||||
/// define the database to work with
|
||||
void setDB(const QString& dbn);
|
||||
/// set a table to work with
|
||||
void setTable(const QString& table);
|
||||
|
||||
/// get current database object set with setDB()
|
||||
QSqlDatabase getDB() const;
|
||||
|
||||
///
|
||||
/// @brief Create a table (if required) with the given columns. Older tables will be updated accordingly with missing columns
|
||||
/// Does not delete or migrate old columns
|
||||
/// @param[in] columns The columns of the table. Requires at least one entry!
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool createTable(QStringList& columns) const;
|
||||
|
||||
///
|
||||
/// @brief Create a column if the column already exists returns false and logs error
|
||||
/// @param[in] column The column of the table
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool createColumn(const QString& column) const;
|
||||
|
||||
///
|
||||
/// @brief Check if at least one record exists in table with the conditions
|
||||
/// @param[in] conditions The search conditions (WHERE)
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool recordExists(const VectorPair& conditions) const;
|
||||
|
||||
///
|
||||
/// @brief Create a new record in table when the conditions find no existing entry. Add additional key:value pairs in columns
|
||||
/// DO NOT repeat column keys between 'conditions' and 'columns' as they will be merged on creation
|
||||
/// @param[in] conditions conditions to search for, as a record may exist and should be updated instead (WHERE)
|
||||
/// @param[in] columns columns to create or update (optional)
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool createRecord(const VectorPair& conditions, const QVariantMap& columns = QVariantMap()) const;
|
||||
|
||||
///
|
||||
/// @brief Update a record with conditions and additional key:value pairs in columns
|
||||
/// @param[in] conditions conditions which rows should be updated (WHERE)
|
||||
/// @param[in] columns columns to update
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool updateRecord(const VectorPair& conditions, const QVariantMap& columns) const;
|
||||
|
||||
///
|
||||
/// @brief Get data of a single record, multiple records are not supported
|
||||
/// @param[in] conditions condition to search for (WHERE)
|
||||
/// @param[out] results results of query
|
||||
/// @param[in] tColumns target columns to search in (optional) if not provided returns all columns
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool getRecord(const VectorPair& conditions, QVariantMap& results, const QStringList& tColumns = QStringList()) const;
|
||||
|
||||
///
|
||||
/// @brief Get data of multiple records, you need to specify the columns. This search is without conditions. Good to grab all data from db
|
||||
/// @param[in] conditions condition to search for (WHERE)
|
||||
/// @param[out] results results of query
|
||||
/// @param[in] tColumns target columns to search in (optional) if not provided returns all columns
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool getRecords(QVector<QVariantMap>& results, const QStringList& tColumns = QStringList()) const;
|
||||
|
||||
///
|
||||
/// @brief Delete a record determined by conditions
|
||||
/// @param[in] conditions conditions of the row to delete it (WHERE)
|
||||
/// @return True on success; on error or not found false
|
||||
///
|
||||
bool deleteRecord(const VectorPair& conditions) const;
|
||||
|
||||
///
|
||||
/// @brief Check if table exists in current database
|
||||
/// @param[in] table The name of the table
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool tableExists(const QString& table) const;
|
||||
|
||||
///
|
||||
/// @brief Delete a table, fails silent (return true) if table is not found
|
||||
/// @param[in] table The name of the table
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool deleteTable(const QString& table) const;
|
||||
|
||||
private:
|
||||
|
||||
Logger* _log;
|
||||
/// databse connection & file name, defaults to hyperion
|
||||
QString _dbn = "hyperion";
|
||||
/// table in database
|
||||
QString _table;
|
||||
|
||||
/// addBindValue to query given by QVariantList
|
||||
void doAddBindValue(QSqlQuery& query, const QVariantList& variants) const;
|
||||
};
|
114
include/db/SettingsTable.h
Normal file
114
include/db/SettingsTable.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
// hyperion
|
||||
#include <db/DBManager.h>
|
||||
|
||||
// qt
|
||||
#include <QDateTime>
|
||||
#include <QJsonDocument>
|
||||
|
||||
///
|
||||
/// @brief settings table db interface
|
||||
///
|
||||
class SettingsTable : public DBManager
|
||||
{
|
||||
|
||||
public:
|
||||
/// construct wrapper with settings table
|
||||
SettingsTable(const quint8& instance, QObject* parent = nullptr)
|
||||
: DBManager(parent)
|
||||
, _hyperion_inst(instance)
|
||||
{
|
||||
setTable("settings");
|
||||
// create table columns
|
||||
createTable(QStringList()<<"type TEXT"<<"config TEXT"<<"hyperion_inst INTEGER"<<"updated_at TEXT");
|
||||
};
|
||||
~SettingsTable(){};
|
||||
|
||||
///
|
||||
/// @brief Create or update a settings record
|
||||
/// @param[in] type type of setting
|
||||
/// @param[in] config The configuration data
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool createSettingsRecord(const QString& type, const QString& config) const
|
||||
{
|
||||
QVariantMap map;
|
||||
map["config"] = config;
|
||||
map["updated_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("type",type));
|
||||
// when a setting is not global we are searching also for the instance
|
||||
if(!isSettingGlobal(type))
|
||||
cond.append(CPair("AND hyperion_inst",_hyperion_inst));
|
||||
return createRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Test if record exist, type can be global setting or local (instance)
|
||||
/// @param[in] type type of setting
|
||||
/// @param[in] hyperion_inst The instance of hyperion assigned (might be empty)
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool recordExist(const QString& type) const
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("type",type));
|
||||
// when a setting is not global we are searching also for the instance
|
||||
if(!isSettingGlobal(type))
|
||||
cond.append(CPair("AND hyperion_inst",_hyperion_inst));
|
||||
return recordExists(cond);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Get 'config' column of settings entry as QJsonDocument
|
||||
/// @param[in] type The settings type
|
||||
/// @return The QJsonDocument
|
||||
///
|
||||
inline QJsonDocument getSettingsRecord(const QString& type) const
|
||||
{
|
||||
QVariantMap results;
|
||||
VectorPair cond;
|
||||
cond.append(CPair("type",type));
|
||||
// when a setting is not global we are searching also for the instance
|
||||
if(!isSettingGlobal(type))
|
||||
cond.append(CPair("AND hyperion_inst",_hyperion_inst));
|
||||
getRecord(cond, results, QStringList("config"));
|
||||
return QJsonDocument::fromJson(results["config"].toByteArray());
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Get 'config' column of settings entry as QString
|
||||
/// @param[in] type The settings type
|
||||
/// @return The QString
|
||||
///
|
||||
inline QString getSettingsRecordString(const QString& type) const
|
||||
{
|
||||
QVariantMap results;
|
||||
VectorPair cond;
|
||||
cond.append(CPair("type",type));
|
||||
// when a setting is not global we are searching also for the instance
|
||||
if(!isSettingGlobal(type))
|
||||
cond.append(CPair("AND hyperion_inst",_hyperion_inst));
|
||||
getRecord(cond, results, QStringList("config"));
|
||||
return results["config"].toString();
|
||||
}
|
||||
|
||||
inline bool isSettingGlobal(const QString& type) const
|
||||
{
|
||||
// list of global settings
|
||||
QStringList list;
|
||||
// server port services
|
||||
list << "jsonServer" << "protoServer" << "flatbufServer" << "udpListener" << "webConfig" << "network"
|
||||
// capture
|
||||
<< "framegrabber" << "grabberV4L2"
|
||||
// other
|
||||
<< "logger";
|
||||
|
||||
return list.contains(type);
|
||||
}
|
||||
|
||||
private:
|
||||
const quint8 _hyperion_inst;
|
||||
};
|
Reference in New Issue
Block a user