diff --git a/core/CsvWriter.cpp b/core/CsvWriter.cpp index b51ace3..ec37f86 100644 --- a/core/CsvWriter.cpp +++ b/core/CsvWriter.cpp @@ -1,7 +1,6 @@ #include "CsvWriter.h" -CsvWriter::CsvWriter() -{} +CsvWriter::CsvWriter() = default; CsvWriter::CsvWriter(QTextStream *output) : m_output(output) diff --git a/core/ExplainTreeModelItem.cpp b/core/ExplainTreeModelItem.cpp index 418eba7..5f71489 100644 --- a/core/ExplainTreeModelItem.cpp +++ b/core/ExplainTreeModelItem.cpp @@ -71,7 +71,7 @@ ExplainRoot::SPtr ExplainRoot::createFromJson(Json::Value &json) auto res = std::make_shared(); // Explain always seems to be an array with one element if (json.isArray()) { - if (json.size() > 0) { + if (!json.empty()) { Json::Value &explain = json[0]; Json::Value &plan = explain["Plan"]; @@ -89,7 +89,7 @@ ExplainTreeModelItem::ExplainTreeModelItem() = default; ExplainTreeModelItem::~ExplainTreeModelItem() = default; -void ExplainTreeModelItem::appendChild(ItemPtr child) +void ExplainTreeModelItem::appendChild(const ItemPtr &child) { child->setParent(shared_from_this()); m_childItems.push_back(child); @@ -132,7 +132,7 @@ int ExplainTreeModelItem::row() const return idx; } -void ExplainTreeModelItem::setParent(ItemPtr parent) +void ExplainTreeModelItem::setParent(const ItemPtr &parent) { m_parentItem = parent; } @@ -306,7 +306,7 @@ ExplainTreeModelItemPtr ExplainTreeModelItem::parent() float ExplainTreeModelItem::exclusiveTime() const { float tt = inclusiveTime(); - for (auto c : m_childItems) { + for (auto&& c : m_childItems) { tt -= c->inclusiveTime(); } return tt; diff --git a/core/ExplainTreeModelItem.h b/core/ExplainTreeModelItem.h index 30cdb7f..f800874 100644 --- a/core/ExplainTreeModelItem.h +++ b/core/ExplainTreeModelItem.h @@ -56,14 +56,14 @@ public: ExplainTreeModelItem(const ExplainTreeModelItem &rhs) = delete; ExplainTreeModelItem &operator=(const ExplainTreeModelItem &rhs) = delete; - void appendChild(ItemPtr child); + void appendChild(const ItemPtr &child); ExplainTreeModelItemPtr child(int row); int childCount() const; // int columnCount() const; // QVariant data(int column) const; int row() const; - void setParent(ItemPtr parent); + void setParent(const ItemPtr &parent); ItemPtr parent(); @@ -112,7 +112,7 @@ public: std::weak_ptr m_parentItem; QString nodeType; - bool parallelAware; // 9.6 + bool parallelAware = false; // 9.6 QString strategy; QString joinType; float startupCost = 0.f; diff --git a/core/PasswordManager.cpp b/core/PasswordManager.cpp index cf60e9d..b57b972 100644 --- a/core/PasswordManager.cpp +++ b/core/PasswordManager.cpp @@ -1,245 +1,389 @@ #include "PasswordManager.h" -#include -#include -#include +#include +#include +#include +#include #include -#include -#include -#include +#include #include -#include -#include +//#include +#include -#include - - -using namespace Botan; +//#include +#include +#include namespace { - /* - First 24 bits of SHA-256("Botan Cryptobox"), followed by 8 0 bits - for later use as flags, etc if needed - */ - const uint8_t c_PasswordVersionCode = 0x10; + class SqlException : public std::runtime_error { + public: + QSqlError error; + SqlException(const QSqlError &err) + : std::runtime_error(err.text().toUtf8().data()) + , error(err) + {} - const size_t c_VersionCodeLen = 1; - - const size_t CIPHER_KEY_LEN = 32; - const size_t CIPHER_IV_LEN = 16; - const size_t MAC_KEY_LEN = 32; - const size_t MAC_OUTPUT_LEN = 20; - const size_t PBKDF_SALT_LEN = 10; - //const size_t PBKDF_ITERATIONS = 8 * 1024; - - const size_t PBKDF_OUTPUT_LEN = CIPHER_KEY_LEN + CIPHER_IV_LEN + MAC_KEY_LEN; - - const char * const c_Cipher = "Serpent/CTR-BE"; - - const char * const c_IniGroupSecurity = "Security"; - - - - StrengthenedKey generateKey(const std::string &passphrase, const uint8_t *salt, - int saltlength, int iterations) - { - PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512)); - OctetString master_key = pbkdf.derive_key( - PBKDF_OUTPUT_LEN, - passphrase, - salt, saltlength, - iterations); - - const uint8_t* mk = master_key.begin(); - - return StrengthenedKey( - SymmetricKey(mk, CIPHER_KEY_LEN), - SymmetricKey(mk + CIPHER_KEY_LEN, MAC_KEY_LEN), - InitializationVector(mk + CIPHER_KEY_LEN + MAC_KEY_LEN, CIPHER_IV_LEN)); - } - -// secure_vector pbkdf_salt(PBKDF_SALT_LEN); -// rng.randomize( pbkdf_salt.data(), pbkdf_salt.size()); -// StrengthenedKey strengthened_key = generateKey(passphrase, pbkdf_salt.data(), pbkdf_salt.size()); - - - std::string encrypt(const std::string &input, - const StrengthenedKey &strengthened_key) - { - - Pipe pipe(get_cipher(c_Cipher, strengthened_key.cipher_key, - strengthened_key.iv, ENCRYPTION), - new Fork( - nullptr, - new MAC_Filter(new HMAC(new SHA_512), - strengthened_key.mac_key, MAC_OUTPUT_LEN))); - - pipe.process_msg((const uint8_t*)input.data(), input.length()); - - /* - Output format is: - mac (20 bytes) - ciphertext - */ - const size_t ciphertext_len = pipe.remaining(0); - std::vector out_buf(MAC_OUTPUT_LEN + ciphertext_len); - - BOTAN_ASSERT_EQUAL( - pipe.read(&out_buf[0], MAC_OUTPUT_LEN, 1), - MAC_OUTPUT_LEN, "MAC output"); - BOTAN_ASSERT_EQUAL( - pipe.read(&out_buf[MAC_OUTPUT_LEN], ciphertext_len, 0), - ciphertext_len, "Ciphertext size"); - - return base64_encode(out_buf.data(), out_buf.size()); - } - - std::string decrypt(const std::string &input, const StrengthenedKey &strengthened_key) - { - secure_vector ciphertext = base64_decode(input); - - if(ciphertext.size() < (MAC_OUTPUT_LEN)) { - throw Decoding_Error("Invalid encrypted password input"); - } - - Pipe pipe(new Fork( - get_cipher(c_Cipher, strengthened_key.cipher_key, strengthened_key.iv, DECRYPTION), - new MAC_Filter(new HMAC(new SHA_512), strengthened_key.mac_key, MAC_OUTPUT_LEN) - )); - - const size_t ciphertext_offset = MAC_OUTPUT_LEN; - pipe.process_msg(&ciphertext[ciphertext_offset], ciphertext.size() - ciphertext_offset); - - uint8_t computed_mac[MAC_OUTPUT_LEN]; - BOTAN_ASSERT_EQUAL(MAC_OUTPUT_LEN, pipe.read(computed_mac, MAC_OUTPUT_LEN, 1), "MAC size"); - - if(!same_mem(computed_mac, &ciphertext[0], MAC_OUTPUT_LEN)) { - throw Decoding_Error("Encrypted password integrity failure"); - } - - return pipe.read_all_as_string(0); - } - - struct constants { - const int pbkdf_salt_len; }; - constants v1_consts = { - 10 - }; - -} // end of unnamed namespace - -/* - * File layout: - * - * Header - * version - * key_salt - * hash_salt - * master_hash - * - * - * Passwords - * key = pw - */ - - - -PasswordManager::PasswordManager(int iterations) - : m_iterations(iterations) -{ -} - -Expected PasswordManager::unlock(const std::string &master_password) -{ - try { - bool result = false; - if (m_masterHash.length() == 0 && master_password.empty()) { - result = true; - } else { - StrengthenedKey key = generateKey(master_password, m_keySalt.begin(), - m_keySalt.length(), m_iterations); - OctetString hash = hashStrengthenedKey(key, m_hashSalt); - - BOOST_ASSERT_MSG(hash.length() == m_masterHash.length(), "Both hashes should have the same length! Versioning error?"); - - if (same_mem(m_masterHash.begin(), hash.begin(), hash.length())) { - result = true; - m_masterKey = key; + 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); } } - return result; - } catch (...) { - return Expected::fromException(); - } -} -Expected PasswordManager::changeMasterPassword(const std::string &old_master_password, - const std::string &new_master_password) -{ - try { - bool result = false; - if (m_masterHash.length() == 0 && old_master_password.empty()) { - // Nothing set yet so we initialize for first use - m_keySalt = OctetString(m_rng, v1_consts.pbkdf_salt_len); - m_masterKey = generateKey(new_master_password, m_keySalt.begin(), m_keySalt.length(), m_iterations); - - m_hashSalt = OctetString(m_rng, v1_consts.pbkdf_salt_len); - m_masterHash = hashStrengthenedKey(m_masterKey, m_hashSalt); - result = true; + 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); + } } - return result; - } catch (...) { - return Expected::fromException(); - } + + /// 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; + }; + + } -void PasswordManager::lock() + +Botan::secure_vector PasswordManager::KeyStrengthener::derive(const std::string &passphrase) { - m_masterKey = StrengthenedKey(); + Botan::secure_vector master_key(m_keySize); + m_hasher->derive_key(master_key.data(), master_key.size(), passphrase.c_str(), passphrase.length(), m_salt.data(), m_salt.size()); + + return master_key; +} + +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 + 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); + + } + +} + +// ------------------------- + +PasswordManager::PasswordManager() = default; +PasswordManager::~PasswordManager() = default; + + +bool PasswordManager::initialized(QSqlDatabase& db) +{ + 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_masterKey.cipher_key.size() == 0; + return m_pskDatabase == nullptr; } -Expected PasswordManager::savePassword(const std::string &key, const std::string &password) +void PasswordManager::set(const std::string &id, const std::string &passwd) { - if (locked()) { - return Expected::fromException(std::logic_error("Need to unlock the password manager first")); + if (m_pskDatabase) { + m_pskDatabase->set_str(id, passwd); + } + else { + throw PasswordManagerLockedException(); } - std::string epw = encrypt(password, m_masterKey); - m_store.emplace(key, epw); - - return Expected(); } -Expected PasswordManager::getPassword(const std::string &key, std::string &out) +bool PasswordManager::get(const std::string &id, std::string &password) { - if (locked()) { - return Expected::fromException(std::logic_error("Need to unlock the password manager first")); + if (m_pskDatabase) { + try { + password = m_pskDatabase->get_str(id); + return true; + } + catch (const Botan::Invalid_Argument &) { + // not present + return false; + } } - auto fi = m_store.find(key); + else { + throw PasswordManagerLockedException(); + } +} - bool result = false; - if (fi != m_store.end()) { - out = decrypt(fi->second, m_masterKey); - result = true; +void PasswordManager::remove(const std::string &id) +{ + if (m_pskDatabase) { + m_pskDatabase->remove(id); + } + else { + throw PasswordManagerLockedException(); + } +} + + +void PasswordManager::initializeNewPskStore(QSqlDatabase &db) +{ +// // 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" + " salt TEXT \n" + ");"); + 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); + } } - return result; +// 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); + } } -Botan::OctetString PasswordManager::hashStrengthenedKey(const StrengthenedKey &key, const OctetString &salt) +bool PasswordManager::isPskStoreInitialized(QSqlDatabase& db) { - std::unique_ptr hash3(Botan::HashFunction::create("SHA-3")); - BOOST_ASSERT_MSG(hash3 != nullptr, "SHA-3 algorithm not available"); - hash3->update(salt.begin(), salt.length()); - hash3->update(key.cipher_key.begin(), key.cipher_key.length()); - hash3->update(key.mac_key.begin(), key.mac_key.length()); - hash3->update(key.iv.begin(), key.iv.length()); - return hash3->final(); + // Is the table with the secret data present and filled? + 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); + } + 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(QSqlDatabase &db) +{ + 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(salt.data(), static_cast(salt.size())), + ks + ); + } + else { + throw std::runtime_error("fail"); + } +} + +PasswordManager::KeyStrengthener PasswordManager::createKeyStrengthener() +{ +// std::unique_ptr pwh; + + size_t key_size = 64; + Botan::secure_vector salt(key_size); + Botan::AutoSeeded_RNG rng; + rng.randomize(salt.data(), salt.size()); + + const std::string algo = "Scrypt"; + auto pwh_fam = Botan::PasswordHashFamily::create(algo); + return KeyStrengthener( + pwh_fam->tune(key_size, std::chrono::seconds(2), 130), + salt, + key_size + ); } diff --git a/core/PasswordManager.h b/core/PasswordManager.h index 5ddd021..fc918e6 100644 --- a/core/PasswordManager.h +++ b/core/PasswordManager.h @@ -2,65 +2,112 @@ #define PASSWORDMANAGER_H #include "Expected.h" +#include +#include #include +#include -#include -#include +#include + + +//#include +//#include #include -struct StrengthenedKey { - Botan::SymmetricKey cipher_key; - Botan::SymmetricKey mac_key; - Botan::InitializationVector iv; +namespace Botan { - StrengthenedKey() {} - StrengthenedKey(const Botan::SymmetricKey &ck, const Botan::SymmetricKey &mk, - const Botan::InitializationVector &i) - : cipher_key(ck) - , mac_key(mk) - , iv(i) - {} + class Encrypted_PSK_Database; + //class Sqlite3_Database; + class PasswordHash; + +} + +class PasswordManagerException: public std::exception { +public: + using std::exception::exception; //(char const* const _Message); }; +class PasswordManagerLockedException: public PasswordManagerException { +public: + using PasswordManagerException::PasswordManagerException; + +}; class PasswordManager { public: + enum Result { + Ok, + Locked, + Error + }; -// static PasswordManager create(const std::string &file_name); + PasswordManager(); + ~PasswordManager(); - explicit PasswordManager(int iterations = 8192); - /** Unlocks the passwords of the connections. + /** Check if it has been initialized before. * - * \return Normally it return a bool specifying if the password was accepted. - * on rare occasions it could return an error. + * If returns false then use createDatabase to set it up + * else use openDatabase to get access. */ - Expected unlock(const std::string &master_password); - - Expected changeMasterPassword(const std::string &master_password, - const std::string &new_master_password); - - /** Forget master password - */ - void lock(); + bool initialized(QSqlDatabase &db); + bool createDatabase(QSqlDatabase &db, QString passphrase); + bool openDatabase(QSqlDatabase &db, QString passphrase); + void closeDatabase(); bool locked() const; - Expected savePassword(const std::string &key, const std::string &password); - Expected getPassword(const std::string &key, std::string &out); - + void set(const std::string &id, const std::string &passwd); + bool get(const std::string &id, std::string &password); + void remove(const std::string &id); private: - int m_iterations; - Botan::AutoSeeded_RNG m_rng; - Botan::OctetString m_keySalt; // salt for generating crypto key - StrengthenedKey m_masterKey; // crypto key - Botan::OctetString m_hashSalt; // salt of the hash of the passphrase - Botan::OctetString m_masterHash; // hash for checking the passphrase + QString m_passwordTableName = "psk_passwd"; + QString m_secretAlgoTableName = "psk_masterkey_algo"; + QString m_secretHashTableName = "psk_masterkey_hash"; + std::unique_ptr m_pskDatabase; - using t_KeyPasswords = std::map; + bool isPskStoreInitialized(QSqlDatabase& db); + void initializeNewPskStore(QSqlDatabase &db); - t_KeyPasswords m_store; + class KeyStrengthener { + public: + KeyStrengthener() = default; + KeyStrengthener(std::unique_ptr hasher, Botan::secure_vector salt, size_t keysize) + : m_hasher (std::move(hasher)) + , m_salt (std::move(salt)) + , m_keySize(keysize) + {} + + KeyStrengthener(const KeyStrengthener&) = delete; + KeyStrengthener& operator=(const KeyStrengthener &) = delete; + + KeyStrengthener(KeyStrengthener &&rhs) + : m_hasher (std::move(rhs.m_hasher)) + , m_salt (std::move(rhs.m_salt)) + , m_keySize(rhs.m_keySize) + {} + + KeyStrengthener& operator=(KeyStrengthener &&rhs) + { + if (&rhs != this) { + m_hasher = std::move(rhs.m_hasher); + m_salt = std::move(rhs.m_salt); + m_keySize = rhs.m_keySize; + } + return *this; + } + + Botan::secure_vector derive(const std::string &passphrase); + void saveParams(QSqlDatabase &db, const QString &table_name); + private: + std::unique_ptr m_hasher; + Botan::secure_vector m_salt; + size_t m_keySize; + }; + + /// Get PasswordHash from parameters in database + KeyStrengthener getKeyStrengthener(QSqlDatabase &db); + KeyStrengthener createKeyStrengthener(); - static Botan::OctetString hashStrengthenedKey(const StrengthenedKey &key, const Botan::OctetString &salt); }; diff --git a/core/SqlAstNode.cpp b/core/SqlAstNode.cpp index 19c59b4..a6a1c5d 100644 --- a/core/SqlAstNode.cpp +++ b/core/SqlAstNode.cpp @@ -2,7 +2,4 @@ using namespace SqlAst; -Node::Node() -{ - -} +Node::Node() = default; 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/BackupDialog.h b/pglab/BackupDialog.h index 6ce44dd..d35e82a 100644 --- a/pglab/BackupDialog.h +++ b/pglab/BackupDialog.h @@ -16,7 +16,7 @@ class BackupDialog : public QDialog Q_OBJECT public: - explicit BackupDialog(QWidget *parent = 0); + explicit BackupDialog(QWidget *parent = nullptr); ~BackupDialog(); void ConnectTo(QProcess *process); diff --git a/pglab/CodeEditor.cpp b/pglab/CodeEditor.cpp index 64a1df8..02d1c4e 100644 --- a/pglab/CodeEditor.cpp +++ b/pglab/CodeEditor.cpp @@ -19,6 +19,8 @@ CodeEditor::CodeEditor(QWidget *parent) connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine())); connect(this, SIGNAL(textChanged()), this, SLOT(onTextChanged())); + setWordWrapMode(QTextOption::NoWrap); + updateGutterAreaWidth(0); highlightCurrentLine(); } diff --git a/pglab/ConnectionList.cpp b/pglab/ConnectionList.cpp index c373474..9187697 100644 --- a/pglab/ConnectionList.cpp +++ b/pglab/ConnectionList.cpp @@ -1,6 +1,7 @@ #include "ConnectionList.h" #include "ScopeGuard.h" #include "util.h" +#include "PasswordManager.h" #include #include #include @@ -19,13 +20,20 @@ namespace { settings.setValue("hostaddr", stdStrToQ(cc.hostAddr())); settings.setValue("port", cc.port()); settings.setValue("user", stdStrToQ(cc.user())); - settings.setValue("password", stdStrToQ(cc.password())); + //settings.setValue("password", stdStrToQ(cc.password())); settings.setValue("dbname", stdStrToQ(cc.dbname())); - settings.setValue("sslmode", (int)cc.sslMode()); + settings.setValue("sslmode", static_cast(cc.sslMode())); settings.setValue("sslcert", stdStrToQ(cc.sslCert())); 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 + bool in_range(T value) + { + return value >= std::numeric_limits::min() && value <= std::numeric_limits::max(); } void LoadConnectionConfig(QSettings &settings, ConnectionConfig &cc) @@ -33,21 +41,28 @@ namespace { cc.setName(qvarToStdStr(settings.value("name"))); cc.setHost(qvarToStdStr(settings.value("host"))); cc.setHostAddr(qvarToStdStr(settings.value("hostaddr"))); - cc.setPort(settings.value("port", 5432).toInt()); + int p = settings.value("port", 5432).toInt(); + if (!in_range(p)) { + p = 0; // let the user re-enter a valid value + } + + cc.setPort(static_cast(p)); cc.setUser(qvarToStdStr(settings.value("user"))); - // std::string encpw = qvarToStdStr(settings.value("encryptedpw")); - // if (encpw.empty()) { - cc.setPassword(qvarToStdStr(settings.value("password"))); - // } - // else { - // cc.setEncryptedPassword(encpw); - // } + + //cc.setPassword(qvarToStdStr(settings.value("password"))); + cc.setDbname(qvarToStdStr(settings.value("dbname"))); - cc.setSslMode((SslMode)settings.value("sslmode").toInt()); + cc.setSslMode(static_cast(settings.value("sslmode").toInt())); cc.setSslCert(qvarToStdStr(settings.value("sslcert"))); 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); } @@ -69,19 +84,20 @@ QString ConnectionList::iniFileName() ConnectionList::ConnectionList() { - } -int ConnectionList::createNew() +size_t ConnectionList::createNew() { - m_connections.emplace_back(QUuid::createUuid(), ConnectionConfig()); + ConnectionConfig cc; + cc.setUuid(QUuid::createUuid()); + m_connections.push_back(cc); return m_connections.size()-1; } -void ConnectionList::remove(int idx, int count) +void ConnectionList::remove(size_t idx, size_t count) { - auto f = m_connections.begin() + idx; - auto l = f + count; + auto f = m_connections.begin() + static_cast(idx); + auto l = f + static_cast(count); deleteFromIni(f, l); m_connections.erase(f, l); } @@ -91,7 +107,7 @@ void ConnectionList::deleteFromIni(t_Connections::iterator begin, t_Connections: QString file_name = iniFileName(); QSettings settings(file_name, QSettings::IniFormat); for (auto i = begin; i != end; ++i) { - settings.remove(i->m_uuid.toString()); + settings.remove(i->uuid().toString()); } } @@ -111,8 +127,9 @@ void ConnectionList::load() SCOPE_EXIT { settings.endGroup(); }; ConnectionConfig cc; + cc.setUuid(uuid); LoadConnectionConfig(settings, cc); - m_connections.emplace_back(uuid, cc); + m_connections.push_back(cc); } } } @@ -123,26 +140,24 @@ void ConnectionList::save() QString file_name = iniFileName(); QSettings settings(file_name, QSettings::IniFormat); for (auto& e : m_connections) { - settings.beginGroup(e.m_uuid.toString()); + settings.beginGroup(e.uuid().toString()); SCOPE_EXIT { settings.endGroup(); }; - SaveConnectionConfig(settings, e.m_config); - e.m_config.clean(); + SaveConnectionConfig(settings, e); + e.clean(); } settings.sync(); } -void ConnectionList::save(int index) +void ConnectionList::save(size_t index) { - if (index >= 0 && index < (int)m_connections.size()) { - auto& e = m_connections[index]; - if (e.m_config.dirty()) { - QString file_name = iniFileName(); - QSettings settings(file_name, QSettings::IniFormat); - settings.beginGroup(e.m_uuid.toString()); - SaveConnectionConfig(settings, e.m_config); - e.m_config.clean(); - settings.sync(); - } + auto& e = m_connections.at(index); + if (e.dirty()) { + QString file_name = iniFileName(); + QSettings settings(file_name, QSettings::IniFormat); + settings.beginGroup(e.uuid().toString()); + SaveConnectionConfig(settings, e); + e.clean(); + settings.sync(); } } diff --git a/pglab/ConnectionList.h b/pglab/ConnectionList.h index f9ba131..38e974e 100644 --- a/pglab/ConnectionList.h +++ b/pglab/ConnectionList.h @@ -8,47 +8,47 @@ #include #include "Expected.h" - class ConnectionList { -private: - static QString iniFileName(); - public: ConnectionList(); - int size() const { return m_connections.size(); } + size_t size() const { return m_connections.size(); } - ConnectionConfig& getConfigByIdx(int idx) + ConnectionConfig& getConfigByIdx(size_t idx) { - return m_connections.at(idx).m_config; + return m_connections.at(idx); } - int createNew(); + void setConfigByIdx(size_t idx, const ConnectionConfig &cc) + { + m_connections[idx] = cc; + } - void remove(int idx, int count); + size_t createNew(); + + void remove(size_t idx, size_t count); void load(); void save(); - void save(int index); - + void save(size_t index); private: - class LijstElem { - public: - QUuid m_uuid; - ConnectionConfig m_config; +// class LijstElem { +// public: +// QUuid m_uuid; ///< Unique identifier, used as a key for storing password in psk db. +// ConnectionConfig m_config; - LijstElem(const QUuid id, const ConnectionConfig &cfg) - : m_uuid(id), m_config(cfg) - {} - }; +// LijstElem(const QUuid id, const ConnectionConfig &cfg) +// : m_uuid(id), m_config(cfg) +// {} +// }; - using t_Connections = std::vector; + using t_Connections = std::vector; t_Connections m_connections; void deleteFromIni(t_Connections::iterator begin, t_Connections::iterator end); - + static QString iniFileName(); }; #endif // CONNECTIONLIST_H diff --git a/pglab/ConnectionListModel.cpp b/pglab/ConnectionListModel.cpp index dfb87c2..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 { @@ -150,6 +150,27 @@ Expected ConnectionListModel::get(int row) } } + +#include + +template +size_t as_size_t(T t); + +template <> +size_t as_size_t(int t) +{ + BOOST_ASSERT(t >= 0); + return static_cast(t); +} + +template <> +size_t as_size_t(long t) +{ + BOOST_ASSERT(t >= 0); + return static_cast(t); +} + + //void ConnectionListModel::del(const int idx) bool ConnectionListModel::removeRows(int row, int count, const QModelIndex &parent) { @@ -159,7 +180,7 @@ bool ConnectionListModel::removeRows(int row, int count, const QModelIndex &pare beginRemoveRows(parent, row, row + count -1); SCOPE_EXIT { endRemoveRows(); }; - m_connections->remove(row, count); + m_connections->remove(as_size_t(row), as_size_t(count)); result = true; } return result; @@ -176,7 +197,13 @@ void ConnectionListModel::save() m_connections->save(); } -void ConnectionListModel::save(int index) +void ConnectionListModel::save(size_t index) { m_connections->save(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 78ce96a..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,17 +30,18 @@ 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(); - void save(int index); + void save(size_t index); + void save(size_t index, const ConnectionConfig &cc); + static QString makeLongDescription(const ConnectionConfig &cfg); private: ConnectionList *m_connections; - static QString makeLongDescription(const ConnectionConfig &cfg); }; #endif // CONNECTIONLISTMODEL_H diff --git a/pglab/ConnectionManagerWindow.cpp b/pglab/ConnectionManagerWindow.cpp index 2883c40..42c2e03 100644 --- a/pglab/ConnectionManagerWindow.cpp +++ b/pglab/ConnectionManagerWindow.cpp @@ -7,6 +7,21 @@ #include #include "ConnectionListModel.h" + +#include +#include + +QString pskFileName() +{ + QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + QDir dir(path); + if (!dir.exists()) { + dir.mkpath("."); + } + path += "/psk.ini"; + return path; +} + ConnectionManagerWindow::ConnectionManagerWindow(MasterController *master, QWidget *parent) : QMainWindow(parent) , ui(new Ui::ConnectionManagerWindow) @@ -53,9 +68,13 @@ void ConnectionManagerWindow::on_currentChanged(const QModelIndex ¤t, { int currow = current.row(); auto clm = m_masterController->getConnectionListModel(); - clm->save(prevSelection); + if (prevSelection) + clm->save(*prevSelection); m_mapper->setCurrentIndex(currow); - prevSelection = currow; + if (currow >= 0) + prevSelection = static_cast(currow); + else + prevSelection.reset(); } void ConnectionManagerWindow::on_actionDelete_connection_triggered() @@ -82,7 +101,6 @@ void ConnectionManagerWindow::setupWidgetMappings() m_mapper->addMapping(ui->edtHost, 2); m_mapper->addMapping(ui->spinPort, 3); m_mapper->addMapping(ui->edtUser, 4); - m_mapper->addMapping(ui->edtPassword, 5); m_mapper->addMapping(ui->edtDbname, 6); m_mapper->toFirst(); } @@ -90,7 +108,13 @@ void ConnectionManagerWindow::setupWidgetMappings() void ConnectionManagerWindow::on_actionConnect_triggered() { auto ci = ui->listView->selectionModel()->currentIndex(); - m_masterController->openSqlWindowForConnection(ci.row()); + if (ci.isValid()) { + auto r = static_cast(ci.row()); + m_masterController->openSqlWindowForConnection(r); + } + else { + // TODO can we give unobtrusive message why it didn't work? + } } void ConnectionManagerWindow::on_actionQuit_application_triggered() @@ -117,68 +141,3 @@ void ConnectionManagerWindow::on_actionManage_server_triggered() } - -#include -//#include -//#include -//#include -//#include -#include - - -void ConnectionManagerWindow::on_testButton_clicked() -{ - std::string error = Botan::runtime_version_check(BOTAN_VERSION_MAJOR, - BOTAN_VERSION_MINOR, - BOTAN_VERSION_PATCH); - if (error.empty()) { -// Botan::AutoSeeded_RNG rng; -// Botan::secure_vector salt = -// //{ 0x3f, 0x0a, 0xb0, 0x11, 0x44, 0xfe, 0x9d, 0xf7, 0x85, 0xd3, 0x11, 0x38, 0xe2, 0xdf, 0x31, 0x42 }; -// rng.random_vec(16); -// // salt should be random and saved with encrypted data so it can be used when we decrypt - -// std::string password = "Hello kitty"; -// std::unique_ptr pbkdf(Botan::get_pbkdf("PBKDF2(SHA-256)")); -// Botan::OctetString aes256_key = pbkdf->derive_key(32, password, salt.data(), salt.size(), 10000); - -// std::string plaintext("Your great-grandfather gave this watch to your granddad for good luck. Unfortunately, Dane's luck wasn't as good as his old man's."); -// Botan::secure_vector pt(plaintext.data(),plaintext.data()+plaintext.length()); - -// std::unique_ptr enc(Botan::get_cipher_mode("AES-256/CBC/PKCS7", Botan::ENCRYPTION)); -// enc->set_key(aes256_key); - -// //generate fresh nonce (IV) -// //std::unique_ptr rng(new Botan::AutoSeeded_RNG); -// std::vector iv(enc->default_nonce_length()); -// rng.randomize(iv.data(), iv.size()); -// enc->start(iv); -// enc->finish(pt); -// //std::cout << std::endl << enc->name() << " with iv " << Botan::hex_encode(iv) << std::endl << Botan::hex_encode(pt); - - - //std::string s = aes256_key.as_string();// + "\n" + t.format_string(); - - std::string passphrase = "my passphrase"; - std::string plaintext("password1234"); - try { - Botan::AutoSeeded_RNG rng; - std::string encrypted = Botan::CryptoBox::encrypt((const uint8_t*)plaintext.data(), plaintext.length(), passphrase, rng); - - std::string decrypted = Botan::CryptoBox::decrypt(encrypted, passphrase); - - std::string s = encrypted + "\n" + decrypted; - QMessageBox::information(this, "pglab", - QString::fromUtf8(s.c_str()), QMessageBox::Yes); - } - catch (Botan::Decoding_Error &/*e*/) { - QMessageBox::information(this, "pglab", - tr("Failure to decrypt"), QMessageBox::Yes); - - } - } - else { - QMessageBox ::information(this, "pglab", - QString::fromUtf8(error.c_str()), QMessageBox::Yes); - } -} diff --git a/pglab/ConnectionManagerWindow.h b/pglab/ConnectionManagerWindow.h index 7bad733..0bdf9ad 100644 --- a/pglab/ConnectionManagerWindow.h +++ b/pglab/ConnectionManagerWindow.h @@ -2,6 +2,7 @@ #define CONNECTIONMANAGERWINDOW_H #include +#include namespace Ui { class ConnectionManagerWindow; @@ -18,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: @@ -30,14 +31,12 @@ private slots: void on_actionBackup_database_triggered(); void on_actionManage_server_triggered(); - void on_testButton_clicked(); - private: Ui::ConnectionManagerWindow *ui; QDataWidgetMapper *m_mapper = nullptr; MasterController *m_masterController; - int prevSelection = -1; + std::optional prevSelection; void setupWidgetMappings(); }; diff --git a/pglab/ConnectionManagerWindow.ui b/pglab/ConnectionManagerWindow.ui index ea3fd3d..839e9db 100644 --- a/pglab/ConnectionManagerWindow.ui +++ b/pglab/ConnectionManagerWindow.ui @@ -75,30 +75,23 @@ - - - Password - - - - - - - QLineEdit::Password - - - - Database - + - + + + + SSL + + + + 2 @@ -136,53 +129,46 @@ - - - SSL - - - - Certificate - + - + Key - + - + Root cert. - + - + Revocation list - + - + PushButton @@ -201,7 +187,7 @@ 0 0 800 - 25 + 20 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/IconColumnDelegate.cpp b/pglab/IconColumnDelegate.cpp index c73adb5..c095e96 100644 --- a/pglab/IconColumnDelegate.cpp +++ b/pglab/IconColumnDelegate.cpp @@ -42,8 +42,7 @@ QSize IconColumnDelegate::sizeHint(const QStyleOptionViewItem &, void IconColumnDelegate::clearCache() { for (auto &e : m_Icons) - delete e. - second; + delete e.second; m_Icons.clear(); } diff --git a/pglab/MasterController.cpp b/pglab/MasterController.cpp index 05b1319..4dc03a2 100644 --- a/pglab/MasterController.cpp +++ b/pglab/MasterController.cpp @@ -2,10 +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) {} @@ -19,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); @@ -33,39 +68,159 @@ void MasterController::showConnectionManager() m_connectionManagerWindow->show(); } -void MasterController::openSqlWindowForConnection(int connection_index) +void MasterController::openSqlWindowForConnection(size_t connection_index) { - auto cc = m_connectionListModel->get(connection_index); - m_connectionListModel->save(connection_index); - if (cc.valid()) { - auto w = new MainWindow(this, nullptr); - w->setAttribute( Qt::WA_DeleteOnClose ); - w->setConfig(cc.get()); - w->show(); + + auto res = m_connectionListModel->get(connection_index); + if (res.valid()) { + auto cc = res.get(); + + 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 + auto w = new MainWindow(this, nullptr); + w->setAttribute( Qt::WA_DeleteOnClose ); + w->setConfig(cc); + w->show(); + } } } -void MasterController::openBackupDlgForConnection(int connection_index) +void MasterController::openBackupDlgForConnection(size_t connection_index) { - auto cc = m_connectionListModel->get(connection_index); - m_connectionListModel->save(connection_index); - if (cc.valid()) { - auto w = new BackupDialog(nullptr); //new ServerWindow(this, nullptr); - w->setAttribute( Qt::WA_DeleteOnClose ); - w->setConfig(cc.get()); - w->show(); + auto res = m_connectionListModel->get(connection_index); + if (res.valid()) { + auto cc = res.get(); + 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 cc = m_connectionListModel->get(connection_index); - m_connectionListModel->save(connection_index); - if (cc.valid()) { - auto w = new ServerWindow(this, nullptr); - w->setAttribute( Qt::WA_DeleteOnClose ); - w->setConfig(cc.get()); - w->show(); + auto res = m_connectionListModel->get(connection_index); + if (res.valid()) { + auto cc = res.get(); + 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) +{ + 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(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) { + 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; +} + + +bool MasterController::getPasswordFromPskdb(const std::string &password_id, std::string &password) +{ + if (!UnlockPasswordManagerIfNeeded()) + return false; + + return m_passwordManager->get(password_id, password); +} + + +bool MasterController::storePasswordInPskdb(const std::string &password_id, const std::string password) +{ + 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 562e9ee..3034203 100644 --- a/pglab/MasterController.h +++ b/pglab/MasterController.h @@ -2,21 +2,28 @@ #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. */ class MasterController : public QObject { Q_OBJECT public: - explicit MasterController(QObject *parent = 0); + explicit MasterController(QObject *parent = nullptr); MasterController(const MasterController&) = delete; MasterController &operator=(const MasterController&) = delete; ~MasterController(); @@ -29,9 +36,11 @@ public: } void showConnectionManager(); - void openSqlWindowForConnection(int connection_index); - void openServerWindowForConnection(int connection_index); - void openBackupDlgForConnection(int connection_index); + void openSqlWindowForConnection(size_t connection_index); + void openServerWindowForConnection(size_t connection_index); + void openBackupDlgForConnection(size_t connection_index); + +// std::shared_ptr getUserConfigDatabase(); signals: @@ -41,7 +50,25 @@ 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) + * + */ + bool retrieveConnectionPassword(ConnectionConfig &cc); + + 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/PasswordPromptDialog.cpp b/pglab/PasswordPromptDialog.cpp new file mode 100644 index 0000000..e089a6c --- /dev/null +++ b/pglab/PasswordPromptDialog.cpp @@ -0,0 +1,100 @@ +#include "PasswordPromptDialog.h" +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_OPERATORS_FOR_FLAGS(PasswordPromptDialog::Flags) + +PasswordPromptDialog::PasswordPromptDialog(Flags flags, QWidget *parent) + : QDialog(parent) + , m_Flags(flags) +{ + m_connectionLabel = new QLabel(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; + 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[0]->setFocus(); + retranslateUi(); + + // QMetaObject::connectSlotsByName(BackupDialog); + 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[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::updateOkEnabled() +{ + 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[0]->text(); +} + +bool PasswordPromptDialog::saveChecked() const +{ + if (m_saveCheck) + return m_saveCheck->checkState() == Qt::Checked; + + return false; +} diff --git a/pglab/PasswordPromptDialog.h b/pglab/PasswordPromptDialog.h new file mode 100644 index 0000000..a1c31f5 --- /dev/null +++ b/pglab/PasswordPromptDialog.h @@ -0,0 +1,44 @@ +#ifndef PASSWORDPROMPTDIALOG_H +#define PASSWORDPROMPTDIALOG_H + +#include + +class QCheckBox; +class QDialogButtonBox; +class QLabel; +class QLineEdit; + +class PasswordPromptDialog : public QDialog +{ + Q_OBJECT +public: + enum Flag { + ConfirmPassword = 1, + SaveOption = 2 + }; + + 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[2] = { nullptr, nullptr }; + QLineEdit *m_passwordInput[2] = { nullptr, nullptr }; + QCheckBox *m_saveCheck = nullptr; + QDialogButtonBox *m_DialogButtons = nullptr; + + void retranslateUi(); + void updateOkEnabled(); + +private slots: + void passwordChanged(const QString &text); +}; + +#endif // PASSWORDPROMPTDIALOG_H diff --git a/pglab/PgLabItemDelegate.cpp b/pglab/PgLabItemDelegate.cpp index 73ea803..52afeaf 100644 --- a/pglab/PgLabItemDelegate.cpp +++ b/pglab/PgLabItemDelegate.cpp @@ -3,6 +3,7 @@ #include "Pgsql_oids.h" #include "ResultTableModelUtil.h" +#include "CustomDataRole.h" PgLabItemDelegate::PgLabItemDelegate(QObject *parent) : QStyledItemDelegate(parent) @@ -81,7 +82,7 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option, // } Oid oid = InvalidOid; - value = index.data(Qt::UserRole); // get OID + value = index.data(CustomDataTypeRole); // get OID if (value.isValid()) oid = value.toUInt(); //getType(index.column()); diff --git a/pglab/PropertiesPage.cpp b/pglab/PropertiesPage.cpp new file mode 100644 index 0000000..e565e76 --- /dev/null +++ b/pglab/PropertiesPage.cpp @@ -0,0 +1,35 @@ +#include "PropertiesPage.h" +#include "PgLabItemDelegate.h" +#include "PropertyProxyModel.h" +#include "ResultTableModelUtil.h" +#include "SqlCodePreview.h" +#include + +PropertiesPage::PropertiesPage(QWidget *parent) : QSplitter(parent) +{ + m_tableView = new QTableView(this); +// m_definitionView = new SqlCodePreview(this); + addWidget(m_tableView); +// addWidget(m_definitionView); + + SetTableViewDefault(m_tableView); + + m_propertyProxyModel = new PropertyProxyModel(this); + m_tableView->setModel(m_propertyProxyModel); + + auto item_delegate = new PgLabItemDelegate(this); + m_tableView->setItemDelegate(item_delegate); + m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); +} + + +void PropertiesPage::setSourceModel(QAbstractItemModel *model) +{ + m_propertyProxyModel->setSourceModel(model); +} + + +void PropertiesPage::setActiveRow(const QModelIndex &row) +{ + m_propertyProxyModel->setActiveRow(row); +} diff --git a/pglab/PropertiesPage.h b/pglab/PropertiesPage.h new file mode 100644 index 0000000..5686d31 --- /dev/null +++ b/pglab/PropertiesPage.h @@ -0,0 +1,36 @@ +#ifndef PROPERTIESPAGE_H +#define PROPERTIESPAGE_H + +#include + +class QTableView; +class SqlCodePreview; +class PgDatabaseCatalog; +class PropertyProxyModel; +class QAbstractItemModel; + +class PropertiesPage : public QSplitter +{ + Q_OBJECT +public: + explicit PropertiesPage(QWidget *parent = nullptr); + + void setSourceModel(QAbstractItemModel *model); +signals: + +public slots: + /** Updates the model (and view) to show the values for row + * + * The column part of the index is not used QModelIndex is used to make is eacy to connect to + * QItemSelectionModel::currentRowChanged + */ + void setActiveRow(const QModelIndex &row); + +private: + QTableView *m_tableView = nullptr; +// SqlCodePreview *m_definitionView = nullptr; + PropertyProxyModel *m_propertyProxyModel = nullptr; + +}; + +#endif // PROPERTIESPAGE_H diff --git a/pglab/SqlCodePreview.cpp b/pglab/SqlCodePreview.cpp index 3a65be1..c7eb62a 100644 --- a/pglab/SqlCodePreview.cpp +++ b/pglab/SqlCodePreview.cpp @@ -8,6 +8,7 @@ SqlCodePreview::SqlCodePreview(QWidget *parent) auto&& config = UserConfiguration::instance(); setFont(config->codeFont()); setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + setWordWrapMode(QTextOption::NoWrap); m_highlighter = new SqlSyntaxHighlighter(document()); } diff --git a/pglab/TablesPage.cpp b/pglab/TablesPage.cpp index 97bfb23..a622e07 100644 --- a/pglab/TablesPage.cpp +++ b/pglab/TablesPage.cpp @@ -9,7 +9,7 @@ #include "IndexModel.h" #include "MainWindow.h" #include "PgLabItemDelegate.h" -#include "PropertyProxyModel.h" +#include "PropertiesPage.h" #include "ResultTableModelUtil.h" #include "SqlFormattingUtils.h" #include "SqlSyntaxHighlighter.h" @@ -26,15 +26,13 @@ TablesPage::TablesPage(MainWindow *parent) { ui->setupUi(this); - auto pglab_delegate = new PgLabItemDelegate(this); - auto icon_delegate = new IconColumnDelegate(this); - SetTableViewDefault(ui->tableListTable); m_tablesModel = new TablesTableModel(this); ui->tableListTable->setModel(m_tablesModel); - ui->tableListTable->setItemDelegate(pglab_delegate); + ui->tableListTable->setItemDelegate(new PgLabItemDelegate(this)); ui->tableListTable->setSortingEnabled(true); ui->tableListTable->sortByColumn(0, Qt::AscendingOrder); + ui->tableListTable->setSelectionBehavior(QAbstractItemView::SelectRows); // Columns SetTableViewDefault(ui->columnsTable); @@ -45,22 +43,14 @@ TablesPage::TablesPage(MainWindow *parent) SetTableViewDefault(ui->constraintsTable); m_constraintModel = new ConstraintModel(this); ui->constraintsTable->setModel(m_constraintModel); - ui->constraintsTable->setItemDelegateForColumn(0, icon_delegate); + ui->constraintsTable->setItemDelegateForColumn(0, new IconColumnDelegate(this)); // Indexes SetTableViewDefault(ui->indexesTable); m_indexModel = new IndexModel(this); ui->indexesTable->setModel(m_indexModel); - ui->indexesTable->setItemDelegate(pglab_delegate); - ui->indexesTable->setItemDelegateForColumn(0, icon_delegate); - - // Properties - PropertyProxyModel* property_model = new PropertyProxyModel(this); - property_model->setSourceModel(m_tablesModel); - SetTableViewDefault(ui->tablePropertiesTable); - ui->tablePropertiesTable->setModel(property_model); - ui->tablePropertiesTable->setItemDelegate(pglab_delegate); - + ui->indexesTable->setItemDelegate(new PgLabItemDelegate(this)); + ui->indexesTable->setItemDelegateForColumn(0, new IconColumnDelegate(this)); // Set code editor fonts QFont code_font = UserConfiguration::instance()->codeFont(); @@ -71,13 +61,13 @@ TablesPage::TablesPage(MainWindow *parent) // Connect signals // --------------- // Table selection - connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged, - property_model, &PropertyProxyModel::setActiveRow); connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &TablesPage::tableListTable_currentRowChanged); -// connect(ui->constraintsTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, -// &TablesPage::constraintsTable_currentRowChanged); + connect(m_tablesModel, &QAbstractItemModel::layoutChanged, + this, &TablesPage::tableListTable_layoutChanged); + + //layoutChanged(const QList &parents = ..., QAbstractItemModel::LayoutChangeHint hint = ...) connect(ui->constraintsTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TablesPage::constraintsTable_selectionChanged); @@ -92,8 +82,17 @@ TablesPage::TablesPage(MainWindow *parent) &TablesPage::indexesTable_modelReset); // Non designer based code + // - Properties page + m_propertiesPage = new PropertiesPage(this); + m_propertiesPage->setSourceModel(m_tablesModel); + m_propertiesTab = addDetailTab(m_propertiesPage); + connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged, + m_propertiesPage, &PropertiesPage::setActiveRow); + + // - Trigger page m_triggerPage = new TriggerPage(this); - m_triggerTab = addDetailTab(m_triggerPage, tr("Triggers")); + m_triggerTab = addDetailTab(m_triggerPage); + retranslateUi(false); } @@ -103,16 +102,17 @@ void TablesPage::retranslateUi(bool all) if (all) ui->retranslateUi(this); + ui->twDetails->setTabText(ui->twDetails->indexOf(m_propertiesTab), QApplication::translate("TablesPage", "Properties", nullptr)); ui->twDetails->setTabText(ui->twDetails->indexOf(m_triggerTab), QApplication::translate("TablesPage", "Triggers", nullptr)); } -QWidget* TablesPage::addDetailTab(QWidget *contents, QString caption) +QWidget* TablesPage::addDetailTab(QWidget *contents) { auto tab = new QWidget(); auto verticalLayout = new QVBoxLayout(tab); verticalLayout->addWidget(contents); - ui->twDetails->addTab(tab, caption); + ui->twDetails->addTab(tab, ""); return tab; } @@ -128,7 +128,6 @@ void TablesPage::setCatalog(std::shared_ptr cat) ui->tableListTable->resizeColumnsToContents(); m_triggerPage->setCatalog(cat); -// m_namespaceFilterWidget->init(cat->namespaces()); auto highlighter = new SqlSyntaxHighlighter(ui->constraintSqlEdit->document()); highlighter->setTypes(*cat->types()); @@ -136,36 +135,39 @@ void TablesPage::setCatalog(std::shared_ptr cat) highlighter->setTypes(*cat->types()); } + void TablesPage::tableListTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous) { if (current.row() != previous.row()) { PgClass table = m_tablesModel->getTable(current.row()); - m_columnsModel->setData(m_catalog, table); - ui->columnsTable->resizeColumnsToContents(); - - m_constraintModel->setData(m_catalog, table); - ui->constraintsTable->resizeColumnsToContents(); - ui->constraintsTable->selectionModel()->reset(); - - m_indexModel->setData(m_catalog, table); - ui->indexesTable->resizeColumnsToContents(); - - m_triggerPage->setFilter(table); + selectedTableChanged(table); } } -//void TablesPage::constraintsTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous) -//{ -// if (current.row() != previous.row()) { -//// QString drop_definition = m_constraintModel->dropDefinition(current.row()); -//// QString create_definition = m_constraintModel->createDefinition(current.row()); -// const PgConstraint& constraint = m_constraintModel->constraint(current.row()); -// QString drop = getDropConstraintDefinition(*m_catalog, constraint); -// QString add = getConstraintDefinition(*m_catalog, constraint); -// ui->constraintSqlEdit->setPlainText(drop % QString::fromUtf16(u"\n") % add); -// } -//} +void TablesPage::tableListTable_layoutChanged(const QList &, QAbstractItemModel::LayoutChangeHint ) +{ + auto&& index = ui->tableListTable->selectionModel()->currentIndex(); + PgClass table = m_tablesModel->getTable(index.row()); + selectedTableChanged(table); +} + + +void TablesPage::selectedTableChanged(const PgClass & table) +{ + m_columnsModel->setData(m_catalog, table); + ui->columnsTable->resizeColumnsToContents(); + + m_constraintModel->setData(m_catalog, table); + ui->constraintsTable->resizeColumnsToContents(); + ui->constraintsTable->selectionModel()->reset(); + + m_indexModel->setData(m_catalog, table); + ui->indexesTable->resizeColumnsToContents(); + + m_triggerPage->setFilter(table); +} + void TablesPage::constraintsTable_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) { diff --git a/pglab/TablesPage.h b/pglab/TablesPage.h index 81c437a..08284be 100644 --- a/pglab/TablesPage.h +++ b/pglab/TablesPage.h @@ -16,7 +16,9 @@ class PgDatabaseCatalog; class NamespaceFilterWidget; class IndexModel; class MainWindow; +class PropertiesPage; class TriggerPage; +class PgClass; class TablesPage : public QWidget { @@ -30,6 +32,8 @@ public: private: Ui::TablesPage *ui; MainWindow *m_window; + QWidget *m_propertiesTab; + PropertiesPage *m_propertiesPage; QWidget *m_triggerTab; TriggerPage *m_triggerPage; std::shared_ptr m_catalog; @@ -40,10 +44,13 @@ private: //NamespaceFilterWidget* m_namespaceFilterWidget; void retranslateUi(bool all = true); - QWidget* addDetailTab(QWidget *contents, QString caption); + QWidget* addDetailTab(QWidget *contents); + + void selectedTableChanged(const PgClass & table); private slots: void tableListTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); + void tableListTable_layoutChanged(const QList &parents, QAbstractItemModel::LayoutChangeHint hint); // void constraintsTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); void constraintsTable_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void constraintsTable_modelReset(); diff --git a/pglab/TablesPage.ui b/pglab/TablesPage.ui index 93c8b26..b562cda 100644 --- a/pglab/TablesPage.ui +++ b/pglab/TablesPage.ui @@ -38,7 +38,7 @@ - 3 + 2 @@ -86,16 +86,6 @@ - - - Properties - - - - - - - diff --git a/pglab/TriggerPage.cpp b/pglab/TriggerPage.cpp index 3eecfff..d2705a3 100644 --- a/pglab/TriggerPage.cpp +++ b/pglab/TriggerPage.cpp @@ -21,14 +21,12 @@ TriggerPage::TriggerPage(QWidget *parent) SetTableViewDefault(m_tableView); - QFont code_font = UserConfiguration::instance()->codeFont(); - m_definitionView->setFont(code_font); - m_model = new TriggerTableModel(this); m_sortFilterProxy = new CustomFilterSortModel(this); m_sortFilterProxy->setSourceModel(m_model); m_tableView->setModel(m_sortFilterProxy); m_tableView->setSortingEnabled(true); + m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); auto item_delegate = new PgLabItemDelegate(this); m_tableView->setItemDelegate(item_delegate); diff --git a/pglab/TriggerPage.h b/pglab/TriggerPage.h index 35cc4e3..00bb9ee 100644 --- a/pglab/TriggerPage.h +++ b/pglab/TriggerPage.h @@ -1,11 +1,9 @@ #ifndef TRIGGERPAGE_H #define TRIGGERPAGE_H -#include #include #include -class QSplitter; class QTableView; class SqlCodePreview; class PgDatabaseCatalog; diff --git a/pglab/pglab.pro b/pglab/pglab.pro index afb2036..08070e0 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -23,7 +23,7 @@ DEFINES += _WIN32_WINNT=0x0501 #LIBS += -LC:\VSproj\boost32\lib -LC:/PROG/LIB -lws2_32 -llibpq #debug { -LIBS += c:/prog/lib/botand_imp.lib +LIBS += c:/prog/lib/botan_imp.lib #} #release { @@ -82,7 +82,9 @@ PropertyProxyModel.cpp \ TriggerTableModel.cpp \ TriggerPage.cpp \ SqlCodePreview.cpp \ - CustomFilterSortModel.cpp + CustomFilterSortModel.cpp \ + PropertiesPage.cpp \ + PasswordPromptDialog.cpp HEADERS += \ QueryResultModel.h \ @@ -135,7 +137,9 @@ CustomDataRole.h \ TriggerTableModel.h \ TriggerPage.h \ SqlCodePreview.h \ - CustomFilterSortModel.h + CustomFilterSortModel.h \ + PropertiesPage.h \ + PasswordPromptDialog.h FORMS += mainwindow.ui \ ConnectionManagerWindow.ui \ diff --git a/pglablib/ConnectionConfig.cpp b/pglablib/ConnectionConfig.cpp index d6fcb21..1dd47b7 100644 --- a/pglablib/ConnectionConfig.cpp +++ b/pglablib/ConnectionConfig.cpp @@ -57,6 +57,22 @@ ConnectionConfig::ConnectionConfig() : m_applicationName(QCoreApplication::applicationName().toUtf8().data()) {} + +void ConnectionConfig::setUuid(const QUuid &uuid) +{ + if (uuid != m_uuid) { + m_dirty = true; + m_uuid = uuid; + } +} + + +const QUuid &ConnectionConfig::uuid() const +{ + return m_uuid; +} + + void ConnectionConfig::setName(std::string desc) { if (m_name != desc) { @@ -242,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 b451a6a..1652bcf 100644 --- a/pglablib/ConnectionConfig.h +++ b/pglablib/ConnectionConfig.h @@ -1,6 +1,7 @@ #ifndef CONNECTION_H #define CONNECTION_H +#include #include #include @@ -14,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; @@ -27,6 +28,9 @@ class ConnectionConfig { public: ConnectionConfig(); + void setUuid(const QUuid &uuid); + const QUuid &uuid() const; + void setName(std::string desc); const std::string& name() const; @@ -66,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; @@ -73,13 +80,14 @@ public: bool dirty() const; void clean(); private: + QUuid m_uuid; std::string m_name; std::string m_host; std::string m_hostaddr; 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; @@ -89,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/PgDatabaseCatalog.cpp b/pglablib/PgDatabaseCatalog.cpp index 9e8a754..a90b96a 100644 --- a/pglablib/PgDatabaseCatalog.cpp +++ b/pglablib/PgDatabaseCatalog.cpp @@ -75,9 +75,13 @@ QString getTablespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid) if (oid == 0) { auto dbname = cat.getDBName(); oid = cat.databases()->getByName(dbname).tablespace; + auto ts = cat.tablespaces()->getByKey(oid); + return ts.name + " (inherited)"; + } + else { + auto ts = cat.tablespaces()->getByKey(oid); + return ts.name; } - auto ts = cat.tablespaces()->getByKey(oid); - return ts.name; } QString getTypeDisplayString(const PgDatabaseCatalog &cat, Oid oid, int32_t typmod) 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/pglablib/pglablib.pro b/pglablib/pglablib.pro index f678cd8..34c3dba 100644 --- a/pglablib/pglablib.pro +++ b/pglablib/pglablib.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += widgets +QT += widgets core TARGET = pglablib TEMPLATE = lib 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/readme.md b/readme.md new file mode 100644 index 0000000..e69de29 diff --git a/tests/pglabtests/pglabtests.pro b/tests/pglabtests/pglabtests.pro index eff3fce..3a9ad20 100644 --- a/tests/pglabtests/pglabtests.pro +++ b/tests/pglabtests/pglabtests.pro @@ -39,7 +39,7 @@ C:\VSproj\boost32\include INCLUDEPATH += $$PWD/../../core DEPENDPATH += $$PWD/../../core -LIBS += c:\prog\lib\botand_imp.lib +LIBS += c:\prog\lib\botan_imp.lib win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/release/libcore.a else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/debug/libcore.a diff --git a/tests/pglabtests/tst_PasswordManager.cpp b/tests/pglabtests/tst_PasswordManager.cpp index 566dbf0..677c8bd 100644 --- a/tests/pglabtests/tst_PasswordManager.cpp +++ b/tests/pglabtests/tst_PasswordManager.cpp @@ -1,74 +1,93 @@ #include #include -#include "PasswordManager.h" +//#include "PasswordManager.h" #include "PrintTo_Qt.h" using namespace testing; -TEST(PasswordManager, initial_changeMasterPassword_returns_true) -{ - PasswordManager pwm(10); +#include +#include - auto res = pwm.changeMasterPassword("", "my test passphrase"); - ASSERT_NO_THROW( res.get() ); - ASSERT_THAT( res.get(), Eq(true) ); -} +//TEST(Botan, recreate) +//{ +// auto phf = Botan::PasswordHashFamily::create("Scrypt"); +// size_t N = 65536, r = 10, p = 2; +// auto ph = phf->from_params(N, r, p); +// auto sc = dynamic_cast(ph.get()); -TEST(PasswordManager, unlock_succeeds) -{ - PasswordManager pwm(10); +// ASSERT_EQ(N, sc->N()); +// ASSERT_EQ(r, sc->r()); +// ASSERT_EQ(p, sc->p()); - std::string passphrase = "my test passphrase"; +// auto phf2 = phf->create(ph->to_string()); +// phf2->default_params() - auto res = pwm.changeMasterPassword("", passphrase); - ASSERT_NO_THROW( res.get() ); - ASSERT_THAT( res.get(), Eq(true) ); +//} - auto res2 = pwm.unlock(passphrase); - ASSERT_NO_THROW( res2.get() ); - ASSERT_THAT( res2.get(), Eq(true) ); -} +//TEST(PasswordManager, initial_changeMasterPassword_returns_true) +//{ +// PasswordManager pwm(10); -TEST(PasswordManager, unlock_fails) -{ - PasswordManager pwm(10); +// auto res = pwm.changeMasterPassword("", "my test passphrase"); +// ASSERT_NO_THROW( res.get() ); +// ASSERT_THAT( res.get(), Eq(true) ); +//} - std::string passphrase = "my test passphrase"; +//TEST(PasswordManager, unlock_succeeds) +//{ +// PasswordManager pwm(10); - auto res = pwm.changeMasterPassword("", passphrase); - ASSERT_NO_THROW( res.get() ); - ASSERT_THAT( res.get(), Eq(true) ); +// std::string passphrase = "my test passphrase"; - auto res2 = pwm.unlock(passphrase + "2"); - ASSERT_NO_THROW( res2.get() ); - ASSERT_THAT( res2.get(), Eq(false) ); -} +// auto res = pwm.changeMasterPassword("", passphrase); +// ASSERT_NO_THROW( res.get() ); +// ASSERT_THAT( res.get(), Eq(true) ); -TEST(PasswordManager, test_save_get) -{ - PasswordManager pwm(10); +// auto res2 = pwm.unlock(passphrase); +// ASSERT_NO_THROW( res2.get() ); +// ASSERT_THAT( res2.get(), Eq(true) ); +//} - std::string passphrase = "my test passphrase"; +//TEST(PasswordManager, unlock_fails) +//{ +// PasswordManager pwm(10); - auto res = pwm.changeMasterPassword("", passphrase); - ASSERT_NO_THROW( res.get() ); - ASSERT_THAT( res.get(), Eq(true) ); +// std::string passphrase = "my test passphrase"; + +// auto res = pwm.changeMasterPassword("", passphrase); +// ASSERT_NO_THROW( res.get() ); +// ASSERT_THAT( res.get(), Eq(true) ); // auto res2 = pwm.unlock(passphrase + "2"); // ASSERT_NO_THROW( res2.get() ); // ASSERT_THAT( res2.get(), Eq(false) ); +//} - const std::string password = "password123"; - const std::string key = "abc"; +//TEST(PasswordManager, test_save_get) +//{ +// PasswordManager pwm(10); - auto res2 = pwm.savePassword(key, password); - ASSERT_THAT( res2.valid(), Eq(true) ); +// std::string passphrase = "my test passphrase"; - std::string result; - auto res3 = pwm.getPassword(key, result); - ASSERT_THAT( res3.valid(), Eq(true) ); - ASSERT_THAT( res3.get(), Eq(true) ); - ASSERT_THAT( result, Eq(password) ); +// auto res = pwm.changeMasterPassword("", passphrase); +// ASSERT_NO_THROW( res.get() ); +// ASSERT_THAT( res.get(), Eq(true) ); -} +//// auto res2 = pwm.unlock(passphrase + "2"); +//// ASSERT_NO_THROW( res2.get() ); +//// ASSERT_THAT( res2.get(), Eq(false) ); + +// const std::string password = "password123"; +// const std::string key = "abc"; + +// auto res2 = pwm.savePassword(key, password); +// ASSERT_THAT( res2.valid(), Eq(true) ); + +// std::string result; +// auto res3 = pwm.getPassword(key, result); +// ASSERT_THAT( res3.valid(), Eq(true) ); +// ASSERT_THAT( res3.get(), Eq(true) ); +// ASSERT_THAT( result, Eq(password) ); + +//}