From e36924c0873c7f9541b67be483b30eae2124effb Mon Sep 17 00:00:00 2001 From: eelke Date: Thu, 8 Nov 2018 21:50:49 +0100 Subject: [PATCH] Passwords are now saved in a password manager. The password manager uses strong encryption using a key derived from the passphrase using scrypt key strengthening algorithm. This ensures encryption is performed using a strong key and that brute forcing the passphrase is time consuming. If the user loses his passphrase no recovery is possible. --- core/PasswordManager.cpp | 357 ++++++++++++++++++----- core/PasswordManager.h | 36 ++- core/core.pro | 1 + pglab/ConnectionList.cpp | 7 + pglab/ConnectionList.h | 5 + pglab/ConnectionListModel.cpp | 7 +- pglab/ConnectionListModel.h | 4 +- pglab/ConnectionManagerWindow.h | 6 +- pglab/CrudModel.cpp | 2 +- pglab/CrudModel.h | 10 +- pglab/MasterController.cpp | 178 ++++++++--- pglab/MasterController.h | 23 +- pglab/PassPhraseForm.cpp | 45 --- pglab/PassPhraseForm.h | 22 -- pglab/PassPhraseForm.ui | 75 ----- pglab/PasswordPromptDialog.cpp | 92 ++++-- pglab/PasswordPromptDialog.h | 24 +- pglab/pglab.pro | 5 +- pglablib/ConnectionConfig.cpp | 13 + pglablib/ConnectionConfig.h | 15 +- pglablib/PgConstraintContainer.cpp | 4 +- pglablib/PgConstraintContainer.h | 4 +- pglablib/codebuilder/CodeBuilder.cpp | 2 +- pglablib/codebuilder/StringEscapeRule.h | 2 +- pgsql/Pgsql_Params.h | 4 +- pgsql/Pgsql_Value.h | 6 +- tests/pglabtests/tst_PasswordManager.cpp | 2 +- 27 files changed, 605 insertions(+), 346 deletions(-) delete mode 100644 pglab/PassPhraseForm.cpp delete mode 100644 pglab/PassPhraseForm.h delete mode 100644 pglab/PassPhraseForm.ui diff --git a/core/PasswordManager.cpp b/core/PasswordManager.cpp index 166cde5..b57b972 100644 --- a/core/PasswordManager.cpp +++ b/core/PasswordManager.cpp @@ -1,22 +1,128 @@ #include "PasswordManager.h" -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include +#include +#include +#include +#include +#include #include #include -#include -#include +//#include +#include + +//#include #include #include +namespace { + + class SqlException : public std::runtime_error { + public: + QSqlError error; + SqlException(const QSqlError &err) + : std::runtime_error(err.text().toUtf8().data()) + , error(err) + {} + + }; + + class QPSK_Database : public Botan::Encrypted_PSK_Database + { + public: + /** + * @param master_key specifies the master key used to encrypt all + * keys and value. It can be of any length, but should be at least 256 bits. + * + * Subkeys for the cryptographic algorithms used are derived from this + * master key. No key stretching is performed; if encrypting a PSK database + * using a password, it is recommended to use PBKDF2 to derive the database + * master key. + */ + QPSK_Database(const Botan::secure_vector& master_key, QSqlDatabase &db, const QString &table_name) + : Encrypted_PSK_Database(master_key) + , m_db(db) + , m_tableName(table_name) + { + QSqlQuery q_create_table(m_db); + q_create_table.prepare("CREATE TABLE IF NOT EXISTS " + table_name + + "(psk_name TEXT PRIMARY KEY, psk_value TEXT)"); + if (!q_create_table.exec()) { + auto err = q_create_table.lastError(); + throw SqlException(err); + } + } + + protected: + /// Save a encrypted (name.value) pair to the database. Both will be base64 encoded strings. + virtual void kv_set(const std::string& index, const std::string& value) override + { + QSqlQuery q(m_db); + q.prepare("insert or replace into " + m_tableName + " values(:name, :value)"); + q.bindValue(":name", QString::fromUtf8(index.c_str())); + q.bindValue(":value", QString::fromUtf8(value.c_str())); + if (!q.exec()) { + auto err = q.lastError(); + throw SqlException(err); + } + } + + /// Get a value previously saved with set_raw_value. Should return an empty + /// string if index is not found. + virtual std::string kv_get(const std::string& index) const override + { + QSqlQuery q(m_db); + q.prepare("SELECT psk_value FROM " + m_tableName + + " WHERE psk_name = :name"); + q.bindValue(":name", QString::fromUtf8(index.c_str())); + if (q.exec()) { + if (q.next()) { + return q.value(0).toString().toUtf8().data(); + } + } + else { + auto err = q.lastError(); + throw SqlException(err); + } + return std::string(); + } + + /// Remove an index + virtual void kv_del(const std::string& index) override + { + QSqlQuery q(m_db); + q.prepare("DELETE FROM " + m_tableName + " WHERE psk_name=:name"); + q.bindValue(":name", QString::fromUtf8(index.c_str())); + if (!q.exec()) { + auto err = q.lastError(); + throw SqlException(err); + } + } + + /// Return all indexes in the table. + virtual std::set kv_get_all() const override + { + QSqlQuery q(m_db); + q.prepare("SELECT psk_name FROM " + m_tableName); + + std::set result; + if (q.exec()) { + while (q.next()) { + result.insert(q.value(0).toString().toUtf8().data()); + } + } + else { + auto err = q.lastError(); + throw SqlException(err); + } + return result; + } + private: + QSqlDatabase &m_db; + QString m_tableName; + }; + + +} Botan::secure_vector PasswordManager::KeyStrengthener::derive(const std::string &passphrase) @@ -27,69 +133,129 @@ Botan::secure_vector PasswordManager::KeyStrengthener::derive(const std return master_key; } -void PasswordManager::KeyStrengthener::saveParams(std::shared_ptr db, const std::string &table_name) +void PasswordManager::KeyStrengthener::saveParams(QSqlDatabase &db, const QString &table_name) { auto sc = dynamic_cast(m_hasher.get()); size_t i1 = sc->N(); size_t i2 = sc->r(); size_t i3 = sc->p(); + + auto salt_str = QString::fromUtf8(Botan::base64_encode(m_salt).c_str()); // SAVE parameters in database - auto stmt = db->new_statement("INSERT INTO " + table_name + "(id, algo, i1, i2, i3, ks, salt) VALUES(?1, ?2, ?3, ?4, ?5)"); - stmt->bind(1, 1); - stmt->bind(2, "Scrypt"); - stmt->bind(3, i1); - stmt->bind(4, i2); - stmt->bind(5, i3); - stmt->bind(6, m_keySize); - stmt->bind(7, Botan::base64_encode(m_salt)); - stmt->spin(); + QSqlQuery insert_statement(db); + insert_statement.prepare("INSERT OR REPLACE INTO " + table_name + "(id, algo, i1, i2, i3, ks, salt) " + + "VALUES(:id, :algo, :i1, :i2, :i3, :ks, :salt)"); + insert_statement.bindValue(":id", 1); + insert_statement.bindValue(":algo", "Scrypt"); + insert_statement.bindValue(":i1", i1); + insert_statement.bindValue(":i2", i2); + insert_statement.bindValue(":i3", i3); + insert_statement.bindValue(":ks", m_keySize); + insert_statement.bindValue(":salt", salt_str); + if (!insert_statement.exec()) { + //throw std::runtime_error("PasswordManager::KeyStrengthener::saveParams failed"); + auto err = insert_statement.lastError(); + throw SqlException(err); + + } } // ------------------------- -void PasswordManager::openDatabase(std::shared_ptr db, std::string passphrase) +PasswordManager::PasswordManager() = default; +PasswordManager::~PasswordManager() = default; + + +bool PasswordManager::initialized(QSqlDatabase& db) { -// std::string psk_db_file_name; -// auto db = std::make_shared(psk_db_file_name); - - KeyStrengthener ks; - // if (database exists) - if (isPskStoreInitialized(db)) { - ks = getKeyStrengthener(db); - } - else { - initializeNewPskStore(db); - ks = createKeyStrengthener(); - ks.saveParams(db, m_secretAlgoTableName); - } - - Botan::secure_vector master_key = ks.derive(passphrase); - m_pskDatabase = std::make_unique(master_key, db, m_passwordTableName); + return isPskStoreInitialized(db); } +bool PasswordManager::createDatabase(QSqlDatabase &db, QString passphrase) +{ + if (!isPskStoreInitialized(db)) { + initializeNewPskStore(db); + auto ks = createKeyStrengthener(); + ks.saveParams(db, m_secretAlgoTableName); + + auto master_key = ks.derive(passphrase.toUtf8().data()); + + std::unique_ptr hash3(Botan::HashFunction::create("SHA-3")); + hash3->update(master_key); + auto mkh = QString::fromUtf8(Botan::base64_encode(hash3->final()).c_str()); + + QSqlQuery q_ins_hash(db); + q_ins_hash.prepare("INSERT INTO " + m_secretHashTableName + "(id, hash) VALUES(:id, :hash)"); + q_ins_hash.bindValue(":id", 1); + q_ins_hash.bindValue(":hash", mkh); + if (!q_ins_hash.exec()) { + auto err = q_ins_hash.lastError(); + qDebug() << err.text(); + throw SqlException(err); + } + + m_pskDatabase = std::make_unique(master_key, db, m_passwordTableName); + return true; + } + return false; +} + +bool PasswordManager::openDatabase(QSqlDatabase &db, QString passphrase) +{ + if (isPskStoreInitialized(db)) { + auto ks = getKeyStrengthener(db); + + auto master_key = ks.derive(passphrase.toUtf8().data()); + std::unique_ptr hash3(Botan::HashFunction::create("SHA-3")); + hash3->update(master_key); + auto mkh_bin = hash3->final(); + + QSqlQuery q("SELECT hash FROM " + m_secretHashTableName + " WHERE id=1", db); + if (q.next()) { + auto hash_b64 = q.value(0).toString().toUtf8(); + auto hash_bin = Botan::base64_decode(hash_b64.data(), static_cast(hash_b64.size())); + if (hash_bin == mkh_bin) { + m_pskDatabase = std::make_unique(master_key, db, m_passwordTableName); + return true; + } + } + } + return false; +} void PasswordManager::closeDatabase() { m_pskDatabase.reset(); } +bool PasswordManager::locked() const +{ + return m_pskDatabase == nullptr; +} void PasswordManager::set(const std::string &id, const std::string &passwd) { if (m_pskDatabase) { - + m_pskDatabase->set_str(id, passwd); } else { throw PasswordManagerLockedException(); } } -std::string PasswordManager::get(const std::string &id, const std::string &passwd) +bool PasswordManager::get(const std::string &id, std::string &password) { if (m_pskDatabase) { - + try { + password = m_pskDatabase->get_str(id); + return true; + } + catch (const Botan::Invalid_Argument &) { + // not present + return false; + } } else { throw PasswordManagerLockedException(); @@ -99,7 +265,7 @@ std::string PasswordManager::get(const std::string &id, const std::string &passw void PasswordManager::remove(const std::string &id) { if (m_pskDatabase) { - + m_pskDatabase->remove(id); } else { throw PasswordManagerLockedException(); @@ -107,57 +273,100 @@ void PasswordManager::remove(const std::string &id) } -void PasswordManager::initializeNewPskStore(std::shared_ptr db) +void PasswordManager::initializeNewPskStore(QSqlDatabase &db) { - // Create tables - // - psk_masterkey_algo - // - psk_passwd - std::string create_statement = +// // Create tables +// // - psk_masterkey_algo +// // - psk_passwd + { + QSqlQuery create_tbl(db); + create_tbl.prepare( "CREATE TABLE IF NOT EXISTS " + m_secretAlgoTableName + "( \n" - " id INTEGER PRIMARY KEY \n" - " algo TEXT \n" - " i1 INTEGER \n" - " i2 INTEGER \n" - " i3 INTEGER \n" - " ks INTEGER \n" + " id INTEGER PRIMARY KEY, \n" + " algo TEXT, \n" + " i1 INTEGER, \n" + " i2 INTEGER, \n" + " i3 INTEGER, \n" + " ks INTEGER, \n" " salt TEXT \n" - ");"; - db->create_table(create_statement); + ");"); + if (!create_tbl.exec()) { +// auto sql_error = create_tbl.lastError(); +// throw std::runtime_error("create table failed"); + auto err = create_tbl.lastError(); + throw SqlException(err); + } + } +// db->create_table( + QSqlQuery create_tbl(db); + create_tbl.prepare( + "CREATE TABLE IF NOT EXISTS " + m_secretHashTableName + "( \n" + " id INTEGER PRIMARY KEY, \n" + " hash TEXT \n" + ");"); + if (!create_tbl.exec()) { + auto err = create_tbl.lastError(); + throw SqlException(err); + } } -bool PasswordManager::isPskStoreInitialized(std::shared_ptr db) +bool PasswordManager::isPskStoreInitialized(QSqlDatabase& db) { // Is the table with the secret data present and filled? - auto stmt = db->new_statement("SELECT name FROM sqlite_master WHERE type='table' AND name=?1"); - stmt->bind(1, m_secretAlgoTableName); - bool ok = stmt->step(); - if (ok) { - auto stmt = db->new_statement("SELECT algo FROM " + m_secretAlgoTableName + " WHERE id=1"); - return stmt->step(); + QSqlQuery query(db); + query.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=:name"); + query.bindValue(":name", m_secretAlgoTableName); + if (!query.exec()) { + auto err = query.lastError(); + throw SqlException(err); } - return false; + if (!query.next()) { + return false; + } + + query.bindValue(":name", m_secretHashTableName); + if (!query.exec()) { + auto err = query.lastError(); + throw SqlException(err); + } + if (!query.next()) { + return false; + } + + QSqlQuery sel_algo("SELECT algo FROM " + m_secretAlgoTableName + " WHERE id=1", db); + if (!sel_algo.next()) { + return false; + } + + QSqlQuery sel_hash("SELECT hash FROM " + m_secretHashTableName + " WHERE id=1", db); + if (!sel_hash.next()) { + return false; + } + + return true; } -PasswordManager::KeyStrengthener PasswordManager::getKeyStrengthener(std::shared_ptr db) +PasswordManager::KeyStrengthener PasswordManager::getKeyStrengthener(QSqlDatabase &db) { - auto stmt = db->new_statement("SELECT algo, i1, i2, i3, ks, salt FROM " + m_secretAlgoTableName + " WHERE id=1"); - if (stmt->step()) { - std::string algo = stmt->get_str(0); - size_t i1 = boost::lexical_cast(stmt->get_str(1)); - size_t i2 = boost::lexical_cast(stmt->get_str(2)); - size_t i3 = boost::lexical_cast(stmt->get_str(3)); - size_t ks = boost::lexical_cast(stmt->get_str(4)); + QSqlQuery query("SELECT algo, i1, i2, i3, ks, salt FROM " + m_secretAlgoTableName + " WHERE id=1", db); + if (query.next()) { + std::string algo = query.value(0).toString().toUtf8().data(); + size_t i1 = query.value(1).toUInt(); + size_t i2 = query.value(2).toUInt(); + size_t i3 = query.value(3).toUInt(); + size_t ks = query.value(4).toUInt(); + auto salt = query.value(5).toString().toUtf8(); auto pwh_fam = Botan::PasswordHashFamily::create(algo); return KeyStrengthener( pwh_fam->from_params(i1, i2, i3), - Botan::base64_decode(stmt->get_str(5)), + Botan::base64_decode(salt.data(), static_cast(salt.size())), ks ); } else { - + throw std::runtime_error("fail"); } } diff --git a/core/PasswordManager.h b/core/PasswordManager.h index e014ebc..fc918e6 100644 --- a/core/PasswordManager.h +++ b/core/PasswordManager.h @@ -2,6 +2,7 @@ #define PASSWORDMANAGER_H #include "Expected.h" +#include #include #include #include @@ -16,8 +17,8 @@ namespace Botan { - class Encrypted_PSK_Database_SQL; - class Sqlite3_Database; + class Encrypted_PSK_Database; + //class Sqlite3_Database; class PasswordHash; } @@ -41,21 +42,31 @@ public: Error }; - PasswordManager() = default; + PasswordManager(); + ~PasswordManager(); - void openDatabase(std::shared_ptr db, std::string passphrase); + /** Check if it has been initialized before. + * + * If returns false then use createDatabase to set it up + * else use openDatabase to get access. + */ + bool initialized(QSqlDatabase &db); + bool createDatabase(QSqlDatabase &db, QString passphrase); + bool openDatabase(QSqlDatabase &db, QString passphrase); void closeDatabase(); + bool locked() const; void set(const std::string &id, const std::string &passwd); - std::string get(const std::string &id, const std::string &passwd); + bool get(const std::string &id, std::string &password); void remove(const std::string &id); private: - std::string m_passwordTableName = "psk_passwd"; - std::string m_secretAlgoTableName = "psk_masterkey_algo"; - std::unique_ptr m_pskDatabase; + QString m_passwordTableName = "psk_passwd"; + QString m_secretAlgoTableName = "psk_masterkey_algo"; + QString m_secretHashTableName = "psk_masterkey_hash"; + std::unique_ptr m_pskDatabase; - bool isPskStoreInitialized(std::shared_ptr db); - void initializeNewPskStore(std::shared_ptr db); + bool isPskStoreInitialized(QSqlDatabase& db); + void initializeNewPskStore(QSqlDatabase &db); class KeyStrengthener { public: @@ -74,6 +85,7 @@ private: , m_salt (std::move(rhs.m_salt)) , m_keySize(rhs.m_keySize) {} + KeyStrengthener& operator=(KeyStrengthener &&rhs) { if (&rhs != this) { @@ -85,7 +97,7 @@ private: } Botan::secure_vector derive(const std::string &passphrase); - void saveParams(std::shared_ptr db, const std::string &table_name); + void saveParams(QSqlDatabase &db, const QString &table_name); private: std::unique_ptr m_hasher; Botan::secure_vector m_salt; @@ -93,7 +105,7 @@ private: }; /// Get PasswordHash from parameters in database - KeyStrengthener getKeyStrengthener(std::shared_ptr db); + KeyStrengthener getKeyStrengthener(QSqlDatabase &db); KeyStrengthener createKeyStrengthener(); }; diff --git a/core/core.pro b/core/core.pro index f603227..c1ea4ac 100644 --- a/core/core.pro +++ b/core/core.pro @@ -5,6 +5,7 @@ #------------------------------------------------- QT -= gui +QT += sql TARGET = core TEMPLATE = lib diff --git a/pglab/ConnectionList.cpp b/pglab/ConnectionList.cpp index 8476b6f..9187697 100644 --- a/pglab/ConnectionList.cpp +++ b/pglab/ConnectionList.cpp @@ -27,6 +27,7 @@ namespace { settings.setValue("sslkey", stdStrToQ(cc.sslKey())); settings.setValue("sslrootcert", stdStrToQ(cc.sslRootCert())); settings.setValue("sslcrl", stdStrToQ(cc.sslCrl())); + settings.setValue("passwordState", static_cast(cc.passwordState())); } template @@ -56,6 +57,12 @@ namespace { cc.setSslKey(qvarToStdStr(settings.value("sslkey"))); cc.setSslRootCert(qvarToStdStr(settings.value("sslrootcert"))); cc.setSslCrl(qvarToStdStr(settings.value("sslcrl"))); + + PasswordState pwstate; + QVariant v = settings.value("passwordState"); + if (v.isNull()) pwstate = PasswordState::NotStored; + else pwstate = static_cast(v.toInt()); + cc.setPasswordState(pwstate); } diff --git a/pglab/ConnectionList.h b/pglab/ConnectionList.h index 5916fa9..38e974e 100644 --- a/pglab/ConnectionList.h +++ b/pglab/ConnectionList.h @@ -19,6 +19,11 @@ public: return m_connections.at(idx); } + void setConfigByIdx(size_t idx, const ConnectionConfig &cc) + { + m_connections[idx] = cc; + } + size_t createNew(); void remove(size_t idx, size_t count); diff --git a/pglab/ConnectionListModel.cpp b/pglab/ConnectionListModel.cpp index f1f3649..4449609 100644 --- a/pglab/ConnectionListModel.cpp +++ b/pglab/ConnectionListModel.cpp @@ -140,9 +140,9 @@ void ConnectionListModel::newItem() emit dataChanged(idx, idx); } -Expected ConnectionListModel::get(int row) +Expected ConnectionListModel::get(size_t row) { - if (row >= 0 && row < (int)m_connections->size()) { + if (row < m_connections->size()) { return m_connections->getConfigByIdx(row); } else { @@ -204,5 +204,6 @@ void ConnectionListModel::save(size_t index) void ConnectionListModel::save(size_t index, const ConnectionConfig &cc) { - + m_connections->setConfigByIdx(index, cc); + m_connections->save(index); } diff --git a/pglab/ConnectionListModel.h b/pglab/ConnectionListModel.h index e9cde8d..fccce79 100644 --- a/pglab/ConnectionListModel.h +++ b/pglab/ConnectionListModel.h @@ -20,7 +20,7 @@ class ConnectionListModel : public QAbstractListModel { public: ConnectionListModel(ConnectionList *conns, QObject *parent); ConnectionListModel(const ConnectionListModel&) = delete; - ~ConnectionListModel(); + ~ConnectionListModel() override; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex &/*parent*/) const override; @@ -30,7 +30,7 @@ public: virtual Qt::ItemFlags flags(const QModelIndex &index) const override; void newItem(); - Expected get(int row); + Expected get(size_t row); virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; void save(); diff --git a/pglab/ConnectionManagerWindow.h b/pglab/ConnectionManagerWindow.h index 0821f8e..0bdf9ad 100644 --- a/pglab/ConnectionManagerWindow.h +++ b/pglab/ConnectionManagerWindow.h @@ -2,7 +2,7 @@ #define CONNECTIONMANAGERWINDOW_H #include -#include +#include namespace Ui { class ConnectionManagerWindow; @@ -19,7 +19,7 @@ class QStandardItemModel; class ConnectionManagerWindow : public QMainWindow { Q_OBJECT public: - explicit ConnectionManagerWindow(MasterController *master, QWidget *parent = 0); + explicit ConnectionManagerWindow(MasterController *master, QWidget *parent = nullptr); ~ConnectionManagerWindow(); private slots: @@ -36,7 +36,7 @@ private: QDataWidgetMapper *m_mapper = nullptr; MasterController *m_masterController; - boost::optional prevSelection; + std::optional prevSelection; void setupWidgetMappings(); }; diff --git a/pglab/CrudModel.cpp b/pglab/CrudModel.cpp index 091bf9a..9d4499c 100644 --- a/pglab/CrudModel.cpp +++ b/pglab/CrudModel.cpp @@ -98,7 +98,7 @@ CrudModel::Value CrudModel::getData(const QModelIndex &index) const //Oid o = m_roData->type(col); // First see if we have buffered editted values that still need saving - boost::optional val; + std::optional val; if (row_mapping.pending) { val = m_pendingRowList.getValue(col, row_mapping.rowKey); } diff --git a/pglab/CrudModel.h b/pglab/CrudModel.h index 277b49d..8a3f466 100644 --- a/pglab/CrudModel.h +++ b/pglab/CrudModel.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include class PgConstraint; class OpenDatabase; @@ -121,7 +121,7 @@ private: // std::shared_ptr resultToRowList(std::shared_ptr result); - using Value = boost::optional; + using Value = std::optional; /** Used to remember the changes that have been made to rows. * @@ -194,7 +194,7 @@ private: iter->second.setValue(col, value); } - boost::optional getValue(int col, int row) const + std::optional getValue(int col, int row) const { auto iter = m_rows.find(row); if (iter != m_rows.end()) { @@ -204,7 +204,7 @@ private: return cell->second; } - return boost::none; + return std::nullopt; } auto begin() { return m_rows.begin(); } @@ -233,7 +233,7 @@ private: ASyncWindow * m_asyncWindow; std::shared_ptr m_database; PgClass m_table; - boost::optional m_primaryKey; + std::optional m_primaryKey; ASyncDBConnection m_dbConn; bool callLoadData = false; diff --git a/pglab/MasterController.cpp b/pglab/MasterController.cpp index 79ab6dd..4dc03a2 100644 --- a/pglab/MasterController.cpp +++ b/pglab/MasterController.cpp @@ -2,11 +2,30 @@ #include "ConnectionManagerWindow.h" #include "ConnectionList.h" #include "ConnectionListModel.h" +#include "PasswordManager.h" #include "MainWindow.h" #include "ServerWindow.h" #include "BackupDialog.h" #include "PasswordPromptDialog.h" +#include +#include +#include +#include +namespace { + + QString GetUserConfigDatabaseName() + { + QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + QDir dir(path); + if (!dir.exists()) { + dir.mkpath("."); + } + path += "/pglabuser.db"; + return path; + } + +} MasterController::MasterController(QObject *parent) : QObject(parent) {} @@ -20,6 +39,21 @@ MasterController::~MasterController() void MasterController::init() { + //std::string dbfilename = QDir::toNativeSeparators(GetUserConfigDatabaseName()).toUtf8().data(); + //m_userConfigDatabase = std::make_shared(dbfilename); + + m_userConfigDatabase = QSqlDatabase::addDatabase("QSQLITE"); + m_userConfigDatabase.setDatabaseName(GetUserConfigDatabaseName()); + + if (!m_userConfigDatabase.open()) { + qDebug() << "Error: connection with database fail"; + } + else { + qDebug() << "Database: connection ok"; + } + + m_passwordManager = std::make_shared(); + m_connectionList = new ConnectionList; m_connectionList->load(); m_connectionListModel = new ConnectionListModel(m_connectionList, this); @@ -41,8 +75,8 @@ void MasterController::openSqlWindowForConnection(size_t connection_index) if (res.valid()) { auto cc = res.get(); - m_connectionListModel->save(connection_index, cc); if (retrieveConnectionPassword(cc)) { + m_connectionListModel->save(connection_index, cc); // TODO instead of directly openening the mainwindow // do async connect and only open window when we have // working connection @@ -55,55 +89,65 @@ void MasterController::openSqlWindowForConnection(size_t connection_index) } -void MasterController::openBackupDlgForConnection(int connection_index) +void MasterController::openBackupDlgForConnection(size_t connection_index) { auto res = m_connectionListModel->get(connection_index); if (res.valid()) { auto cc = res.get(); - retrieveConnectionPassword(cc); - m_connectionListModel->save(connection_index, cc); - - auto w = new BackupDialog(nullptr); //new ServerWindow(this, nullptr); - w->setAttribute( Qt::WA_DeleteOnClose ); - w->setConfig(cc); - w->show(); + if (retrieveConnectionPassword(cc)) { + m_connectionListModel->save(connection_index, cc); + auto w = new BackupDialog(nullptr); //new ServerWindow(this, nullptr); + w->setAttribute( Qt::WA_DeleteOnClose ); + w->setConfig(cc); + w->show(); + } } } -void MasterController::openServerWindowForConnection(int connection_index) +void MasterController::openServerWindowForConnection(size_t connection_index) { auto res = m_connectionListModel->get(connection_index); if (res.valid()) { auto cc = res.get(); - retrieveConnectionPassword(cc); - m_connectionListModel->save(connection_index, cc); - - auto w = new ServerWindow(this, nullptr); - w->setAttribute( Qt::WA_DeleteOnClose ); - w->setConfig(cc); - w->show(); + if (retrieveConnectionPassword(cc)) { + m_connectionListModel->save(connection_index, cc); + auto w = new ServerWindow(this, nullptr); + w->setAttribute( Qt::WA_DeleteOnClose ); + w->setConfig(cc); + w->show(); + } } } bool MasterController::retrieveConnectionPassword(ConnectionConfig &cc) { - // Look at config - // - is password required, how do we know? - // - IF is password stored in pskdb - // - ask pskdb for password - // - ELSE - // - ask user for password + auto pw_state = cc.passwordState(); + if (pw_state == PasswordState::NotNeeded) { + return true; + } + else if (pw_state == PasswordState::SavedPasswordManager) { + std::string pw; + bool result = getPasswordFromPskdb(getPskId(cc), pw); + if (result) { + cc.setPassword(pw); + return true; + } + } + // Geen else hier want als voorgaande blok niet geretourneerd heeft moeten we wachtwoord + // ook aan de gebruiker vragen zoals hier gebeurd. QString str = ConnectionListModel::makeLongDescription(cc); - auto dlg = std::make_unique(nullptr); - dlg->setConnectionDescription(str); + auto dlg = std::make_unique(PasswordPromptDialog::SaveOption, nullptr); + dlg->setDescription(QString(tr("Please provide password for connection %1")).arg(str)); int exec_result = dlg->exec(); if (exec_result == QDialog::Accepted) { - cc.setPassword(dlg->password().toUtf8().data()); - // - IF user checked remember password - // - ask pskdb to store password - + std::string password = dlg->password().toUtf8().data(); + cc.setPassword(password); + if (dlg->saveChecked()) { + storePasswordInPskdb(getPskId(cc), password); + cc.setPasswordState(PasswordState::SavedPasswordManager); + } return true; } return false; @@ -112,25 +156,71 @@ bool MasterController::retrieveConnectionPassword(ConnectionConfig &cc) bool MasterController::getPasswordFromPskdb(const std::string &password_id, std::string &password) { -// func: getPasswordFromPskdb -// IF pskdb locked -// prompt user for pskdb passphrase -// unlock pskdb -// get pwd - return false; + if (!UnlockPasswordManagerIfNeeded()) + return false; + + return m_passwordManager->get(password_id, password); } bool MasterController::storePasswordInPskdb(const std::string &password_id, const std::string password) { -// func: storePasswordInPskdb -// IF pskdb not setup -// notify user and ask for passphrase -// init pskdb -// ELSE -// IF pskdb locked -// ask for passphrase -// unlock -// store pwd + if (!UnlockPasswordManagerIfNeeded()) + return false; + + m_passwordManager->set(password_id, password); + return true; +} + +bool MasterController::UnlockPasswordManagerIfNeeded() +{ + if (m_passwordManager->initialized(m_userConfigDatabase)) { + if (!m_passwordManager->locked()) + return true; + + while (true) { + // ask user for passphrase + auto dlg = std::make_unique(nullptr, nullptr); + dlg->setDescription(tr("Enter passphrase for password manager")); + int exec_result = dlg->exec(); + bool ok = (exec_result == QDialog::Accepted); + + // IF user gave OK + if (ok) { + if (m_passwordManager->openDatabase(m_userConfigDatabase, dlg->password())) { + return true; + } + } + else { + return false; + } + } + } + else { + // Ask user for passphrase + confirmation, clearly instruct this is first setup + // create + auto dlg = std::make_unique(PasswordPromptDialog::ConfirmPassword, nullptr); + dlg->setDescription(tr("Enter passphrase for password manager initialization")); + int exec_result = dlg->exec(); + if (exec_result == QDialog::Accepted) { + QString passphrase = dlg->password(); + if (m_passwordManager->createDatabase(m_userConfigDatabase, passphrase)) { + return true; + } + } + } return false; } + +std::string MasterController::getPskId(const ConnectionConfig &cc) +{ + std::string id = "dbpw/"; + id += cc.uuid().toString().toUtf8().data(); + return id; +} + +//std::shared_ptr MasterController::getUserConfigDatabase() +//{ +// return m_userConfigDatabase; +//} + diff --git a/pglab/MasterController.h b/pglab/MasterController.h index 4a10c9a..3034203 100644 --- a/pglab/MasterController.h +++ b/pglab/MasterController.h @@ -2,14 +2,21 @@ #define MASTERCONTROLLER_H #include +#include +#include #include #include +#include +//namespace Botan { +// class Sqlite3_Database; +//} class ConnectionConfig; class ConnectionList; class ConnectionListModel; class ConnectionManagerWindow; +class PasswordManager; /** \brief Controller class responsible for all things global. */ @@ -30,8 +37,10 @@ public: void showConnectionManager(); void openSqlWindowForConnection(size_t connection_index); - void openServerWindowForConnection(int connection_index); - void openBackupDlgForConnection(int connection_index); + void openServerWindowForConnection(size_t connection_index); + void openBackupDlgForConnection(size_t connection_index); + +// std::shared_ptr getUserConfigDatabase(); signals: @@ -41,6 +50,12 @@ private: ConnectionList *m_connectionList = nullptr; ConnectionListModel *m_connectionListModel = nullptr; ConnectionManagerWindow *m_connectionManagerWindow = nullptr; + //std::shared_ptr m_userConfigDatabase; + QSqlDatabase m_userConfigDatabase; + /** Using long lived object so it can remember its master password for sometime + * if the user wishes it. + */ + std::shared_ptr m_passwordManager; /** Retrieves the connection password from the user (directly or through the psk db) * @@ -50,6 +65,10 @@ private: bool getPasswordFromPskdb(const std::string &password_id, std::string &password); bool storePasswordInPskdb(const std::string &password_id, const std::string password); + + bool UnlockPasswordManagerIfNeeded(); + + static std::string getPskId(const ConnectionConfig &cc); }; #endif // MASTERCONTROLLER_H diff --git a/pglab/PassPhraseForm.cpp b/pglab/PassPhraseForm.cpp deleted file mode 100644 index 36ff2fa..0000000 --- a/pglab/PassPhraseForm.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "PassPhraseForm.h" -#include "ui_PassPhraseForm.h" - -PassPhraseForm::PassPhraseForm(QWidget *parent) : - QWidget(parent), - ui(new Ui::PassPhraseForm) -{ - ui->setupUi(this); -} - -PassPhraseForm::~PassPhraseForm() -{ - delete ui; -} - -/* - -Password strength calculation: -seperate characters in couple of groups - -For the use of characters from each group a certain value is added to the base value -which is meant to signify the size of the set of characters the password is based on. - -Some analysis of relative positions might be required! Predictable placement of special charachters and uppercase/lowercase or numbers -should be penalized. - -These calculations should result in a search space size per character - -the base to the power of the length of the password gives the resulting strength -from this result we take the 10 log to get the magnitude of the value. - -a-z 1:3 2:7 3:13 4:26 -A-Z 1:3 2:7 3:13 4:26 -0-9 1:4 2:10 -`~!@#$%^&*()_-=+[{]};:'",<.>/?\| 1:4 2:8 3:16 4:32 -space +1 - -Straf punten -alleen speciaal karakter aan eind van string -8 -alleen hoofdletter aan begin van wachtwoord -6 - -la-~ZDv4E-O*y]C -bYGWlDyeKKbcZBjoWX - -*/ diff --git a/pglab/PassPhraseForm.h b/pglab/PassPhraseForm.h deleted file mode 100644 index c0c0f8d..0000000 --- a/pglab/PassPhraseForm.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef PASSPHRASEFORM_H -#define PASSPHRASEFORM_H - -#include - -namespace Ui { -class PassPhraseForm; -} - -class PassPhraseForm : public QWidget -{ - Q_OBJECT - -public: - explicit PassPhraseForm(QWidget *parent = nullptr); - ~PassPhraseForm(); - -private: - Ui::PassPhraseForm *ui; -}; - -#endif // PASSPHRASEFORM_H diff --git a/pglab/PassPhraseForm.ui b/pglab/PassPhraseForm.ui deleted file mode 100644 index 89bd22f..0000000 --- a/pglab/PassPhraseForm.ui +++ /dev/null @@ -1,75 +0,0 @@ - - - PassPhraseForm - - - - 0 - 0 - 397 - 228 - - - - Form - - - false - - - - - - 20 - - - 20 - - - - - Enter passphrase: - - - - - - - 32767 - - - QLineEdit::NoEcho - - - - - - - Repeat passphrase: - - - - - - - QLineEdit::Password - - - - - - - 67 - - - - - - - - - - - - - diff --git a/pglab/PasswordPromptDialog.cpp b/pglab/PasswordPromptDialog.cpp index 0b11759..e089a6c 100644 --- a/pglab/PasswordPromptDialog.cpp +++ b/pglab/PasswordPromptDialog.cpp @@ -5,66 +5,96 @@ #include #include #include +#include -PasswordPromptDialog::PasswordPromptDialog(QWidget *parent) +Q_DECLARE_OPERATORS_FOR_FLAGS(PasswordPromptDialog::Flags) + +PasswordPromptDialog::PasswordPromptDialog(Flags flags, QWidget *parent) : QDialog(parent) + , m_Flags(flags) { m_connectionLabel = new QLabel(this); - auto dialog_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); - - m_passwordLabel = new QLabel(this); - m_passwordInput = new QLineEdit(this); - m_passwordInput->setEchoMode(QLineEdit::Password); - - m_saveCheck = new QCheckBox(this); + m_DialogButtons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); + const size_t inputFieldCount = flags.testFlag(ConfirmPassword) ? 2 : 1; auto mainLayout = new QGridLayout; int row = 0; mainLayout->addWidget(m_connectionLabel, row, 0, 1, 2); ++row; - mainLayout->addWidget(m_passwordLabel, row, 0); - mainLayout->addWidget(m_passwordInput, row, 1); - ++row; - mainLayout->addWidget(m_saveCheck, row, 1); - ++row; - mainLayout->addWidget(dialog_buttons, row, 0, 1 ,2); + for (size_t idx = 0; idx < inputFieldCount; ++idx) { + auto lbl = new QLabel(this); + auto input = new QLineEdit(this); + input->setEchoMode(QLineEdit::Password); + mainLayout->addWidget(lbl, row, 0); + mainLayout->addWidget(input, row, 1); + m_passwordLabel[idx] = lbl; + m_passwordInput[idx] = input; + ++row; + } + if (m_Flags.testFlag(SaveOption)) { + m_saveCheck = new QCheckBox(this); + mainLayout->addWidget(m_saveCheck, row, 1); + ++row; + } + mainLayout->addWidget(m_DialogButtons, row, 0, 1 ,2); setLayout(mainLayout); - m_passwordInput->setFocus(); + m_passwordInput[0]->setFocus(); retranslateUi(); // QMetaObject::connectSlotsByName(BackupDialog); - connect(dialog_buttons, &QDialogButtonBox::accepted, this, &PasswordPromptDialog::accept); - connect(dialog_buttons, &QDialogButtonBox::rejected, this, &PasswordPromptDialog::reject); + connect(m_DialogButtons, &QDialogButtonBox::accepted, this, &PasswordPromptDialog::accept); + connect(m_DialogButtons, &QDialogButtonBox::rejected, this, &PasswordPromptDialog::reject); + connect(m_passwordInput[0], &QLineEdit::textChanged, this, &PasswordPromptDialog::passwordChanged); + if (m_passwordInput[1]) + connect(m_passwordInput[1], &QLineEdit::textChanged, this, &PasswordPromptDialog::passwordChanged); } void PasswordPromptDialog::retranslateUi() { const char * context = "PasswordPromptDialog"; setWindowTitle(QApplication::translate(context, "Connection password", nullptr)); - m_passwordLabel->setText(QApplication::translate(context, "Password", nullptr)); - m_passwordInput->setPlaceholderText(QApplication::translate(context, "Enter password", nullptr)); - m_saveCheck->setText(QApplication::translate(context, "Save password", nullptr)); + m_passwordLabel[0]->setText(QApplication::translate(context, "Password", nullptr)); + m_passwordInput[0]->setPlaceholderText(QApplication::translate(context, "Enter password", nullptr)); + if (m_passwordLabel[1]) + m_passwordLabel[1]->setText(QApplication::translate(context, "Confirm password", nullptr)); + if (m_passwordInput[1]) + m_passwordInput[1]->setPlaceholderText(QApplication::translate(context, "Reenter same password for confirmation", nullptr)); + if (m_saveCheck) + m_saveCheck->setText(QApplication::translate(context, "Save password", nullptr)); } -void PasswordPromptDialog::setConnectionDescription(const QString &description) +void PasswordPromptDialog::updateOkEnabled() { - m_connectionLabel->setText(QString(tr("Please provide password for connection %1")).arg(description)); + bool enabled = true; + if (m_passwordInput[1]) + enabled = m_passwordInput[0]->text() == m_passwordInput[1]->text(); + + auto btn = m_DialogButtons->button(QDialogButtonBox::Ok); + btn->setEnabled(enabled); +} + +void PasswordPromptDialog::passwordChanged(const QString &) +{ + updateOkEnabled(); +} + +void PasswordPromptDialog::setDescription(const QString &description) +{ + m_connectionLabel->setText(QString("%1").arg(description)); } QString PasswordPromptDialog::password() const { - return m_passwordInput->text(); + return m_passwordInput[0]->text(); } -//void PasswordPromptDialog::accept() -//{ +bool PasswordPromptDialog::saveChecked() const +{ + if (m_saveCheck) + return m_saveCheck->checkState() == Qt::Checked; -//} - -//void PasswordPromptDialog::reject() -//{ - -//} + return false; +} diff --git a/pglab/PasswordPromptDialog.h b/pglab/PasswordPromptDialog.h index f394a9e..a1c31f5 100644 --- a/pglab/PasswordPromptDialog.h +++ b/pglab/PasswordPromptDialog.h @@ -4,6 +4,7 @@ #include class QCheckBox; +class QDialogButtonBox; class QLabel; class QLineEdit; @@ -11,22 +12,33 @@ class PasswordPromptDialog : public QDialog { Q_OBJECT public: - explicit PasswordPromptDialog(QWidget *parent = nullptr); + enum Flag { + ConfirmPassword = 1, + SaveOption = 2 + }; - void setConnectionDescription(const QString &description); + Q_DECLARE_FLAGS(Flags, Flag) + //Q_FLAG(Flags) + + explicit PasswordPromptDialog(Flags flags, QWidget *parent = nullptr); + + void setDescription(const QString &description); QString password() const; + bool saveChecked() const; private: + Flags m_Flags; QLabel *m_connectionLabel = nullptr; - QLabel *m_passwordLabel = nullptr; - QLineEdit *m_passwordInput = nullptr; + QLabel *m_passwordLabel[2] = { nullptr, nullptr }; + QLineEdit *m_passwordInput[2] = { nullptr, nullptr }; QCheckBox *m_saveCheck = nullptr; + QDialogButtonBox *m_DialogButtons = nullptr; void retranslateUi(); + void updateOkEnabled(); private slots: -// void accept(); -// void reject(); + void passwordChanged(const QString &text); }; #endif // PASSWORDPROMPTDIALOG_H diff --git a/pglab/pglab.pro b/pglab/pglab.pro index 605fece..08070e0 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -84,7 +84,6 @@ PropertyProxyModel.cpp \ SqlCodePreview.cpp \ CustomFilterSortModel.cpp \ PropertiesPage.cpp \ - PassPhraseForm.cpp \ PasswordPromptDialog.cpp HEADERS += \ @@ -140,7 +139,6 @@ CustomDataRole.h \ SqlCodePreview.h \ CustomFilterSortModel.h \ PropertiesPage.h \ - PassPhraseForm.h \ PasswordPromptDialog.h FORMS += mainwindow.ui \ @@ -155,8 +153,7 @@ FORMS += mainwindow.ui \ NamespaceFilterWidget.ui \ ApplicationWindow.ui \ CrudTab.ui \ - CodeGenerator.ui \ - PassPhraseForm.ui + CodeGenerator.ui RESOURCES += \ resources.qrc diff --git a/pglablib/ConnectionConfig.cpp b/pglablib/ConnectionConfig.cpp index f6c21e8..1dd47b7 100644 --- a/pglablib/ConnectionConfig.cpp +++ b/pglablib/ConnectionConfig.cpp @@ -258,6 +258,19 @@ const char * const * ConnectionConfig::getValues() const return m_values.data(); } +PasswordState ConnectionConfig::passwordState() const +{ + return m_passwordState; +} + +void ConnectionConfig::setPasswordState(PasswordState password_state) +{ + if (m_passwordState != password_state) { + m_dirty = true; + m_passwordState = password_state; + } +} + bool ConnectionConfig::isSameDatabase(const ConnectionConfig &rhs) const { return m_host == rhs.m_host diff --git a/pglablib/ConnectionConfig.h b/pglablib/ConnectionConfig.h index ce9191e..1652bcf 100644 --- a/pglablib/ConnectionConfig.h +++ b/pglablib/ConnectionConfig.h @@ -15,10 +15,10 @@ enum class SslMode { verify_full=5 }; -enum class PasswordMode { - Unsave, - Encrypted, - DontSave +enum class PasswordState { + NotNeeded, ///< the Connection doesn't require a password + NotStored, ///< password needed but we do not know it + SavedPasswordManager, ///< Saved in the password manager }; class QProcessEnvironment; @@ -70,6 +70,9 @@ public: const char * const * getKeywords() const; const char * const * getValues() const; + PasswordState passwordState() const; + void setPasswordState(PasswordState password_state); + bool isSameDatabase(const ConnectionConfig &rhs) const; void writeToEnvironment(QProcessEnvironment &env) const; @@ -84,7 +87,7 @@ private: std::string m_port = "5432"; std::string m_user; - std::string m_password; + std::string m_password; ///< TODO do we want to keep this here or should we remember it seperatly? std::string m_dbname; std::string m_sslMode; @@ -94,9 +97,11 @@ private: std::string m_sslCrl; std::string m_applicationName; + PasswordState m_passwordState = PasswordState::NotStored; bool m_dirty = false; + static void strToEnv(QProcessEnvironment &env, const QString &var, const std::string &val); static std::vector s_keywords; diff --git a/pglablib/PgConstraintContainer.cpp b/pglablib/PgConstraintContainer.cpp index 0f2d448..ae59f4a 100644 --- a/pglablib/PgConstraintContainer.cpp +++ b/pglablib/PgConstraintContainer.cpp @@ -74,9 +74,9 @@ std::vector PgConstraintContainer::getConstraintsForRelation(Oid r return result; } -boost::optional PgConstraintContainer::getPrimaryForRelation(Oid relid) const +std::optional PgConstraintContainer::getPrimaryForRelation(Oid relid) const { - boost::optional result; + std::optional result; for (const auto &e : m_container) { if (e.relid == relid && e.type == ConstraintType::PrimaryKey) { result = e; diff --git a/pglablib/PgConstraintContainer.h b/pglablib/PgConstraintContainer.h index 5910942..2b87350 100644 --- a/pglablib/PgConstraintContainer.h +++ b/pglablib/PgConstraintContainer.h @@ -5,7 +5,7 @@ #include "PgConstraint.h" #include "Pgsql_declare.h" #include -#include +#include class PgConstraintContainer : public PgContainer { public: @@ -16,7 +16,7 @@ public: const PgConstraint* getFKeyForTableColumn(Oid relid, int16_t attnum) const; std::vector getConstraintsForRelation(Oid relid) const; - boost::optional getPrimaryForRelation(Oid relid) const; + std::optional getPrimaryForRelation(Oid relid) const; protected: virtual PgConstraint loadElem(const Pgsql::Row &row) override; }; diff --git a/pglablib/codebuilder/CodeBuilder.cpp b/pglablib/codebuilder/CodeBuilder.cpp index 763c567..ac0527f 100644 --- a/pglablib/codebuilder/CodeBuilder.cpp +++ b/pglablib/codebuilder/CodeBuilder.cpp @@ -69,7 +69,7 @@ void CodeBuilder::genStructFields(QTextStream &q, const ColumnDataList &columns) // Any way at generation time we might want to be able to specify the null handle // - exception/error return // - magic value - // - boost::optional + // - std::optional // - boolean flags // - null pointer (useful for languages where this has no cost, other cases boolean flags will be more performant) } diff --git a/pglablib/codebuilder/StringEscapeRule.h b/pglablib/codebuilder/StringEscapeRule.h index a0ea1f5..a8c9822 100644 --- a/pglablib/codebuilder/StringEscapeRule.h +++ b/pglablib/codebuilder/StringEscapeRule.h @@ -41,7 +41,7 @@ public: * field often provides enough flexibility. */ QString m_prefixWith; -// boost::optional m_numericConversion; +// std::optional m_numericConversion; }; #endif // STRINGESCAPERULE_H diff --git a/pgsql/Pgsql_Params.h b/pgsql/Pgsql_Params.h index 0bb5a69..7ecbe60 100644 --- a/pgsql/Pgsql_Params.h +++ b/pgsql/Pgsql_Params.h @@ -6,7 +6,7 @@ #include #include "Pgsql_declare.h" #include "Pgsql_oids.h" -#include +#include namespace Pgsql { @@ -37,7 +37,7 @@ namespace Pgsql { Param add(const QString &s, Oid oid=varchar_oid); Param add(const char *data, Oid oid=varchar_oid); - Param add(boost::optional s, Oid oid=varchar_oid) + Param add(std::optional s, Oid oid=varchar_oid) { return add(s ? s->c_str() : nullptr, oid); } diff --git a/pgsql/Pgsql_Value.h b/pgsql/Pgsql_Value.h index 21e6211..c2ad771 100644 --- a/pgsql/Pgsql_Value.h +++ b/pgsql/Pgsql_Value.h @@ -8,7 +8,7 @@ #include #include #include -#include +#include namespace Pgsql { @@ -154,10 +154,10 @@ namespace Pgsql { }; template - void operator<<(boost::optional &s, const Value &v) + void operator<<(std::optional &s, const Value &v) { if (v.null()) - s = boost::optional(); + s = std::optional(); else *s << v; } diff --git a/tests/pglabtests/tst_PasswordManager.cpp b/tests/pglabtests/tst_PasswordManager.cpp index ab3fa74..677c8bd 100644 --- a/tests/pglabtests/tst_PasswordManager.cpp +++ b/tests/pglabtests/tst_PasswordManager.cpp @@ -1,6 +1,6 @@ #include #include -#include "PasswordManager.h" +//#include "PasswordManager.h" #include "PrintTo_Qt.h" using namespace testing;