#pragma once // hyperion #include #include // qt #include #include /// /// @brief Authentication table interface /// class AuthTable : public DBManager { public: /// construct wrapper with auth table AuthTable(const QString& rootPath = "", QObject* parent = nullptr, bool readonlyMode = false) : DBManager(parent) { setReadonlyMode(readonlyMode); if(!rootPath.isEmpty()){ // Init Hyperion database usage setRootPath(rootPath); setDatabaseName("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"); }; /// /// @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 Test if a user token is authorized for access. /// @param usr The user name /// @param token The token /// @return True on success else false /// inline bool isUserTokenAuthorized(const QString& usr, const QString& token) { if(getUserToken(usr) == token.toUtf8()) { updateUserUsed(usr); return true; } return false; } /// /// @brief Update token of a user. It's an alternate login path which is replaced on startup. This token is NOT hashed(!) /// @param user The user name /// @return True on success else false /// inline bool setUserToken(const QString& user) { QVariantMap map; map["token"] = QCryptographicHash::hash(QUuid::createUuid().toByteArray(), QCryptographicHash::Sha512).toHex(); VectorPair cond; cond.append(CPair("user", user)); return updateRecord(cond, map); } /// /// @brief Get token of a user. This token is NOT hashed(!) /// @param user The user name /// @return The token /// inline const QByteArray getUserToken(const QString& user) { QVariantMap results; VectorPair cond; cond.append(CPair("user", user)); getRecord(cond, results, QStringList()<<"token"); return results["token"].toByteArray(); } /// /// @brief update password of given user. The user should be tested (isUserAuthorized) to verify this change /// @param user The user name /// @param newPw The new password to set /// @return True on success else false /// inline bool updateUserPassword(const QString& user, const QString& newPw) { QVariantMap map; map["password"] = calcPasswordHashOfUser(user, newPw); VectorPair cond; cond.append(CPair("user", user)); return updateRecord(cond, map); } /// /// @brief Reset password of Hyperion user !DANGER! Used in Hyperion main.cpp /// @return True on success else false /// inline bool resetHyperionUser() { QVariantMap map; map["password"] = calcPasswordHashOfUser("Hyperion", "hyperion"); VectorPair cond; cond.append(CPair("user", "Hyperion")); return updateRecord(cond, map); } /// /// @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 Rename token record by id /// @param[in] id The token id /// @param[in] comment The new comment /// @return true on success else false /// inline bool renameToken(const QString &id, const QString &comment) { QVariantMap map; map["comment"] = comment; VectorPair cond; cond.append(CPair("id", id)); return updateRecord(cond, map); } /// /// @brief Get all 'comment', 'last_use' and 'id' column entries /// @return A vector of all lists /// inline const QVector getTokenList() { QVector 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(); } };