Merge branch 'safely-store-passwords' into 'master'

Safely store passwords

See merge request eelke/pgLab!4
This commit is contained in:
Eelke Klein 2018-11-10 06:50:10 +00:00
commit b0718258c9
46 changed files with 1234 additions and 598 deletions

View file

@ -1,7 +1,6 @@
#include "CsvWriter.h" #include "CsvWriter.h"
CsvWriter::CsvWriter() CsvWriter::CsvWriter() = default;
{}
CsvWriter::CsvWriter(QTextStream *output) CsvWriter::CsvWriter(QTextStream *output)
: m_output(output) : m_output(output)

View file

@ -71,7 +71,7 @@ ExplainRoot::SPtr ExplainRoot::createFromJson(Json::Value &json)
auto res = std::make_shared<ExplainRoot>(); auto res = std::make_shared<ExplainRoot>();
// Explain always seems to be an array with one element // Explain always seems to be an array with one element
if (json.isArray()) { if (json.isArray()) {
if (json.size() > 0) { if (!json.empty()) {
Json::Value &explain = json[0]; Json::Value &explain = json[0];
Json::Value &plan = explain["Plan"]; Json::Value &plan = explain["Plan"];
@ -89,7 +89,7 @@ ExplainTreeModelItem::ExplainTreeModelItem() = default;
ExplainTreeModelItem::~ExplainTreeModelItem() = default; ExplainTreeModelItem::~ExplainTreeModelItem() = default;
void ExplainTreeModelItem::appendChild(ItemPtr child) void ExplainTreeModelItem::appendChild(const ItemPtr &child)
{ {
child->setParent(shared_from_this()); child->setParent(shared_from_this());
m_childItems.push_back(child); m_childItems.push_back(child);
@ -132,7 +132,7 @@ int ExplainTreeModelItem::row() const
return idx; return idx;
} }
void ExplainTreeModelItem::setParent(ItemPtr parent) void ExplainTreeModelItem::setParent(const ItemPtr &parent)
{ {
m_parentItem = parent; m_parentItem = parent;
} }
@ -306,7 +306,7 @@ ExplainTreeModelItemPtr ExplainTreeModelItem::parent()
float ExplainTreeModelItem::exclusiveTime() const float ExplainTreeModelItem::exclusiveTime() const
{ {
float tt = inclusiveTime(); float tt = inclusiveTime();
for (auto c : m_childItems) { for (auto&& c : m_childItems) {
tt -= c->inclusiveTime(); tt -= c->inclusiveTime();
} }
return tt; return tt;

View file

@ -56,14 +56,14 @@ public:
ExplainTreeModelItem(const ExplainTreeModelItem &rhs) = delete; ExplainTreeModelItem(const ExplainTreeModelItem &rhs) = delete;
ExplainTreeModelItem &operator=(const ExplainTreeModelItem &rhs) = delete; ExplainTreeModelItem &operator=(const ExplainTreeModelItem &rhs) = delete;
void appendChild(ItemPtr child); void appendChild(const ItemPtr &child);
ExplainTreeModelItemPtr child(int row); ExplainTreeModelItemPtr child(int row);
int childCount() const; int childCount() const;
// int columnCount() const; // int columnCount() const;
// QVariant data(int column) const; // QVariant data(int column) const;
int row() const; int row() const;
void setParent(ItemPtr parent); void setParent(const ItemPtr &parent);
ItemPtr parent(); ItemPtr parent();
@ -112,7 +112,7 @@ public:
std::weak_ptr<ExplainTreeModelItem> m_parentItem; std::weak_ptr<ExplainTreeModelItem> m_parentItem;
QString nodeType; QString nodeType;
bool parallelAware; // 9.6 bool parallelAware = false; // 9.6
QString strategy; QString strategy;
QString joinType; QString joinType;
float startupCost = 0.f; float startupCost = 0.f;

View file

@ -1,245 +1,389 @@
#include "PasswordManager.h" #include "PasswordManager.h"
#include <botan/filters.h> #include <QSqlQuery>
#include <botan/pipe.h> #include <QSqlError>
#include <botan/sha2_64.h> #include <QDebug>
#include <QVariant>
#include <botan/hash.h> #include <botan/hash.h>
#include <botan/hmac.h> #include <botan/auto_rng.h>
#include <botan/pbkdf2.h>
#include <botan/rng.h>
#include <botan/base64.h> #include <botan/base64.h>
#include <botan/loadstor.h> //#include <botan/psk_db_sql.h>
#include <botan/mem_ops.h> #include <botan/psk_db.h>
#include <boost/assert.hpp> //#include <botan/sqlite3.h>
#include <botan/scrypt.h>
#include <boost/lexical_cast.hpp>
using namespace Botan;
namespace { namespace {
/* class SqlException : public std::runtime_error {
First 24 bits of SHA-256("Botan Cryptobox"), followed by 8 0 bits public:
for later use as flags, etc if needed QSqlError error;
*/ SqlException(const QSqlError &err)
const uint8_t c_PasswordVersionCode = 0x10; : 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<uint8_t> 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<uint8_t> 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<uint8_t> 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 = { class QPSK_Database : public Botan::Encrypted_PSK_Database
10 {
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<uint8_t>& 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<std::string> kv_get_all() const override
{
QSqlQuery q(m_db);
q.prepare("SELECT psk_name FROM " + m_tableName);
std::set<std::string> 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;
}; };
} // 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<bool> PasswordManager::unlock(const std::string &master_password)
Botan::secure_vector<uint8_t> PasswordManager::KeyStrengthener::derive(const std::string &passphrase)
{ {
try { Botan::secure_vector<uint8_t> master_key(m_keySize);
bool result = false; m_hasher->derive_key(master_key.data(), master_key.size(), passphrase.c_str(), passphrase.length(), m_salt.data(), m_salt.size());
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?"); return master_key;
if (same_mem(m_masterHash.begin(), hash.begin(), hash.length())) {
result = true;
m_masterKey = key;
}
}
return result;
} catch (...) {
return Expected<bool>::fromException();
}
} }
Expected<bool> PasswordManager::changeMasterPassword(const std::string &old_master_password, void PasswordManager::KeyStrengthener::saveParams(QSqlDatabase &db, const QString &table_name)
const std::string &new_master_password)
{ {
try { auto sc = dynamic_cast<Botan::Scrypt*>(m_hasher.get());
bool result = false; size_t i1 = sc->N();
if (m_masterHash.length() == 0 && old_master_password.empty()) { size_t i2 = sc->r();
// Nothing set yet so we initialize for first use size_t i3 = sc->p();
m_keySalt = OctetString(m_rng, v1_consts.pbkdf_salt_len);
m_masterKey = generateKey(new_master_password, m_keySalt.begin(), m_keySalt.length(), m_iterations);
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);
m_hashSalt = OctetString(m_rng, v1_consts.pbkdf_salt_len);
m_masterHash = hashStrengthenedKey(m_masterKey, m_hashSalt);
result = true;
}
return result;
} catch (...) {
return Expected<bool>::fromException();
} }
} }
void PasswordManager::lock() // -------------------------
PasswordManager::PasswordManager() = default;
PasswordManager::~PasswordManager() = default;
bool PasswordManager::initialized(QSqlDatabase& db)
{ {
m_masterKey = StrengthenedKey(); 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<Botan::HashFunction> 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<QPSK_Database>(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<Botan::HashFunction> 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<size_t>(hash_b64.size()));
if (hash_bin == mkh_bin) {
m_pskDatabase = std::make_unique<QPSK_Database>(master_key, db, m_passwordTableName);
return true;
}
}
}
return false;
}
void PasswordManager::closeDatabase()
{
m_pskDatabase.reset();
} }
bool PasswordManager::locked() const bool PasswordManager::locked() const
{ {
return m_masterKey.cipher_key.size() == 0; return m_pskDatabase == nullptr;
} }
Expected<void> PasswordManager::savePassword(const std::string &key, const std::string &password) void PasswordManager::set(const std::string &id, const std::string &passwd)
{ {
if (locked()) { if (m_pskDatabase) {
return Expected<void>::fromException(std::logic_error("Need to unlock the password manager first")); m_pskDatabase->set_str(id, passwd);
}
else {
throw PasswordManagerLockedException();
} }
std::string epw = encrypt(password, m_masterKey);
m_store.emplace(key, epw);
return Expected<void>();
} }
Expected<bool> PasswordManager::getPassword(const std::string &key, std::string &out) bool PasswordManager::get(const std::string &id, std::string &password)
{ {
if (locked()) { if (m_pskDatabase) {
return Expected<bool>::fromException(std::logic_error("Need to unlock the password manager first")); try {
password = m_pskDatabase->get_str(id);
return true;
} }
auto fi = m_store.find(key); catch (const Botan::Invalid_Argument &) {
// not present
return false;
}
}
else {
throw PasswordManagerLockedException();
}
}
bool result = false; void PasswordManager::remove(const std::string &id)
if (fi != m_store.end()) { {
out = decrypt(fi->second, m_masterKey); if (m_pskDatabase) {
result = true; 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<Botan::HashFunction> hash3(Botan::HashFunction::create("SHA-3")); // Is the table with the secret data present and filled?
BOOST_ASSERT_MSG(hash3 != nullptr, "SHA-3 algorithm not available"); QSqlQuery query(db);
hash3->update(salt.begin(), salt.length()); query.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=:name");
hash3->update(key.cipher_key.begin(), key.cipher_key.length()); query.bindValue(":name", m_secretAlgoTableName);
hash3->update(key.mac_key.begin(), key.mac_key.length()); if (!query.exec()) {
hash3->update(key.iv.begin(), key.iv.length()); auto err = query.lastError();
return hash3->final(); 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<size_t>(salt.size())),
ks
);
}
else {
throw std::runtime_error("fail");
}
}
PasswordManager::KeyStrengthener PasswordManager::createKeyStrengthener()
{
// std::unique_ptr<Botan::PasswordHash> pwh;
size_t key_size = 64;
Botan::secure_vector<uint8_t> 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
);
} }

View file

@ -2,65 +2,112 @@
#define PASSWORDMANAGER_H #define PASSWORDMANAGER_H
#include "Expected.h" #include "Expected.h"
#include <QSqlDatabase>
#include <botan/secmem.h>
#include <string> #include <string>
#include <memory>
#include <botan/botan.h> #include <botan/pwdhash.h>
#include <botan/symkey.h>
//#include <botan/botan.h>
//#include <botan/symkey.h>
#include <map> #include <map>
struct StrengthenedKey { namespace Botan {
Botan::SymmetricKey cipher_key;
Botan::SymmetricKey mac_key;
Botan::InitializationVector iv;
StrengthenedKey() {} class Encrypted_PSK_Database;
StrengthenedKey(const Botan::SymmetricKey &ck, const Botan::SymmetricKey &mk, //class Sqlite3_Database;
const Botan::InitializationVector &i) class PasswordHash;
: cipher_key(ck)
, mac_key(mk) }
, iv(i)
{} class PasswordManagerException: public std::exception {
public:
using std::exception::exception; //(char const* const _Message);
}; };
class PasswordManagerLockedException: public PasswordManagerException {
public:
using PasswordManagerException::PasswordManagerException;
};
class PasswordManager { class PasswordManager {
public: public:
enum Result {
Ok,
Locked,
Error
};
// static PasswordManager create(const std::string &file_name); PasswordManager();
~PasswordManager();
explicit PasswordManager(int iterations = 8192); /** Check if it has been initialized before.
/** Unlocks the passwords of the connections.
* *
* \return Normally it return a bool specifying if the password was accepted. * If returns false then use createDatabase to set it up
* on rare occasions it could return an error. * else use openDatabase to get access.
*/ */
Expected<bool> unlock(const std::string &master_password); bool initialized(QSqlDatabase &db);
bool createDatabase(QSqlDatabase &db, QString passphrase);
Expected<bool> changeMasterPassword(const std::string &master_password, bool openDatabase(QSqlDatabase &db, QString passphrase);
const std::string &new_master_password); void closeDatabase();
/** Forget master password
*/
void lock();
bool locked() const; bool locked() const;
Expected<void> savePassword(const std::string &key, const std::string &password); void set(const std::string &id, const std::string &passwd);
Expected<bool> getPassword(const std::string &key, std::string &out); bool get(const std::string &id, std::string &password);
void remove(const std::string &id);
private: private:
int m_iterations; QString m_passwordTableName = "psk_passwd";
Botan::AutoSeeded_RNG m_rng; QString m_secretAlgoTableName = "psk_masterkey_algo";
Botan::OctetString m_keySalt; // salt for generating crypto key QString m_secretHashTableName = "psk_masterkey_hash";
StrengthenedKey m_masterKey; // crypto key std::unique_ptr<Botan::Encrypted_PSK_Database> m_pskDatabase;
Botan::OctetString m_hashSalt; // salt of the hash of the passphrase
Botan::OctetString m_masterHash; // hash for checking the passphrase
using t_KeyPasswords = std::map<std::string, std::string>; bool isPskStoreInitialized(QSqlDatabase& db);
void initializeNewPskStore(QSqlDatabase &db);
t_KeyPasswords m_store; class KeyStrengthener {
public:
KeyStrengthener() = default;
KeyStrengthener(std::unique_ptr<Botan::PasswordHash> hasher, Botan::secure_vector<uint8_t> 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<uint8_t> derive(const std::string &passphrase);
void saveParams(QSqlDatabase &db, const QString &table_name);
private:
std::unique_ptr<Botan::PasswordHash> m_hasher;
Botan::secure_vector<uint8_t> 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);
}; };

View file

@ -2,7 +2,4 @@
using namespace SqlAst; using namespace SqlAst;
Node::Node() Node::Node() = default;
{
}

View file

@ -5,6 +5,7 @@
#------------------------------------------------- #-------------------------------------------------
QT -= gui QT -= gui
QT += sql
TARGET = core TARGET = core
TEMPLATE = lib TEMPLATE = lib

View file

@ -16,7 +16,7 @@ class BackupDialog : public QDialog
Q_OBJECT Q_OBJECT
public: public:
explicit BackupDialog(QWidget *parent = 0); explicit BackupDialog(QWidget *parent = nullptr);
~BackupDialog(); ~BackupDialog();
void ConnectTo(QProcess *process); void ConnectTo(QProcess *process);

View file

@ -19,6 +19,8 @@ CodeEditor::CodeEditor(QWidget *parent)
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine())); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
connect(this, SIGNAL(textChanged()), this, SLOT(onTextChanged())); connect(this, SIGNAL(textChanged()), this, SLOT(onTextChanged()));
setWordWrapMode(QTextOption::NoWrap);
updateGutterAreaWidth(0); updateGutterAreaWidth(0);
highlightCurrentLine(); highlightCurrentLine();
} }

View file

@ -1,6 +1,7 @@
#include "ConnectionList.h" #include "ConnectionList.h"
#include "ScopeGuard.h" #include "ScopeGuard.h"
#include "util.h" #include "util.h"
#include "PasswordManager.h"
#include <QDir> #include <QDir>
#include <QStandardPaths> #include <QStandardPaths>
#include <QSettings> #include <QSettings>
@ -19,13 +20,20 @@ namespace {
settings.setValue("hostaddr", stdStrToQ(cc.hostAddr())); settings.setValue("hostaddr", stdStrToQ(cc.hostAddr()));
settings.setValue("port", cc.port()); settings.setValue("port", cc.port());
settings.setValue("user", stdStrToQ(cc.user())); 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("dbname", stdStrToQ(cc.dbname()));
settings.setValue("sslmode", (int)cc.sslMode()); settings.setValue("sslmode", static_cast<int>(cc.sslMode()));
settings.setValue("sslcert", stdStrToQ(cc.sslCert())); settings.setValue("sslcert", stdStrToQ(cc.sslCert()));
settings.setValue("sslkey", stdStrToQ(cc.sslKey())); settings.setValue("sslkey", stdStrToQ(cc.sslKey()));
settings.setValue("sslrootcert", stdStrToQ(cc.sslRootCert())); settings.setValue("sslrootcert", stdStrToQ(cc.sslRootCert()));
settings.setValue("sslcrl", stdStrToQ(cc.sslCrl())); settings.setValue("sslcrl", stdStrToQ(cc.sslCrl()));
settings.setValue("passwordState", static_cast<int>(cc.passwordState()));
}
template <typename S, typename T>
bool in_range(T value)
{
return value >= std::numeric_limits<S>::min() && value <= std::numeric_limits<S>::max();
} }
void LoadConnectionConfig(QSettings &settings, ConnectionConfig &cc) void LoadConnectionConfig(QSettings &settings, ConnectionConfig &cc)
@ -33,21 +41,28 @@ namespace {
cc.setName(qvarToStdStr(settings.value("name"))); cc.setName(qvarToStdStr(settings.value("name")));
cc.setHost(qvarToStdStr(settings.value("host"))); cc.setHost(qvarToStdStr(settings.value("host")));
cc.setHostAddr(qvarToStdStr(settings.value("hostaddr"))); cc.setHostAddr(qvarToStdStr(settings.value("hostaddr")));
cc.setPort(settings.value("port", 5432).toInt()); int p = settings.value("port", 5432).toInt();
if (!in_range<uint16_t>(p)) {
p = 0; // let the user re-enter a valid value
}
cc.setPort(static_cast<uint16_t>(p));
cc.setUser(qvarToStdStr(settings.value("user"))); cc.setUser(qvarToStdStr(settings.value("user")));
// std::string encpw = qvarToStdStr(settings.value("encryptedpw"));
// if (encpw.empty()) { //cc.setPassword(qvarToStdStr(settings.value("password")));
cc.setPassword(qvarToStdStr(settings.value("password")));
// }
// else {
// cc.setEncryptedPassword(encpw);
// }
cc.setDbname(qvarToStdStr(settings.value("dbname"))); cc.setDbname(qvarToStdStr(settings.value("dbname")));
cc.setSslMode((SslMode)settings.value("sslmode").toInt()); cc.setSslMode(static_cast<SslMode>(settings.value("sslmode").toInt()));
cc.setSslCert(qvarToStdStr(settings.value("sslcert"))); cc.setSslCert(qvarToStdStr(settings.value("sslcert")));
cc.setSslKey(qvarToStdStr(settings.value("sslkey"))); cc.setSslKey(qvarToStdStr(settings.value("sslkey")));
cc.setSslRootCert(qvarToStdStr(settings.value("sslrootcert"))); cc.setSslRootCert(qvarToStdStr(settings.value("sslrootcert")));
cc.setSslCrl(qvarToStdStr(settings.value("sslcrl"))); cc.setSslCrl(qvarToStdStr(settings.value("sslcrl")));
PasswordState pwstate;
QVariant v = settings.value("passwordState");
if (v.isNull()) pwstate = PasswordState::NotStored;
else pwstate = static_cast<PasswordState>(v.toInt());
cc.setPasswordState(pwstate);
} }
@ -69,19 +84,20 @@ QString ConnectionList::iniFileName()
ConnectionList::ConnectionList() 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; 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 f = m_connections.begin() + static_cast<int>(idx);
auto l = f + count; auto l = f + static_cast<int>(count);
deleteFromIni(f, l); deleteFromIni(f, l);
m_connections.erase(f, l); m_connections.erase(f, l);
} }
@ -91,7 +107,7 @@ void ConnectionList::deleteFromIni(t_Connections::iterator begin, t_Connections:
QString file_name = iniFileName(); QString file_name = iniFileName();
QSettings settings(file_name, QSettings::IniFormat); QSettings settings(file_name, QSettings::IniFormat);
for (auto i = begin; i != end; ++i) { 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(); }; SCOPE_EXIT { settings.endGroup(); };
ConnectionConfig cc; ConnectionConfig cc;
cc.setUuid(uuid);
LoadConnectionConfig(settings, cc); 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(); QString file_name = iniFileName();
QSettings settings(file_name, QSettings::IniFormat); QSettings settings(file_name, QSettings::IniFormat);
for (auto& e : m_connections) { for (auto& e : m_connections) {
settings.beginGroup(e.m_uuid.toString()); settings.beginGroup(e.uuid().toString());
SCOPE_EXIT { settings.endGroup(); }; SCOPE_EXIT { settings.endGroup(); };
SaveConnectionConfig(settings, e.m_config); SaveConnectionConfig(settings, e);
e.m_config.clean(); e.clean();
} }
settings.sync(); 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.at(index);
auto& e = m_connections[index]; if (e.dirty()) {
if (e.m_config.dirty()) {
QString file_name = iniFileName(); QString file_name = iniFileName();
QSettings settings(file_name, QSettings::IniFormat); QSettings settings(file_name, QSettings::IniFormat);
settings.beginGroup(e.m_uuid.toString()); settings.beginGroup(e.uuid().toString());
SaveConnectionConfig(settings, e.m_config); SaveConnectionConfig(settings, e);
e.m_config.clean(); e.clean();
settings.sync(); settings.sync();
} }
}
} }

View file

@ -8,47 +8,47 @@
#include <vector> #include <vector>
#include "Expected.h" #include "Expected.h"
class ConnectionList { class ConnectionList {
private:
static QString iniFileName();
public: public:
ConnectionList(); 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 load();
void save(); void save();
void save(int index); void save(size_t index);
private: private:
class LijstElem { // class LijstElem {
public: // public:
QUuid m_uuid; // QUuid m_uuid; ///< Unique identifier, used as a key for storing password in psk db.
ConnectionConfig m_config; // ConnectionConfig m_config;
LijstElem(const QUuid id, const ConnectionConfig &cfg) // LijstElem(const QUuid id, const ConnectionConfig &cfg)
: m_uuid(id), m_config(cfg) // : m_uuid(id), m_config(cfg)
{} // {}
}; // };
using t_Connections = std::vector<LijstElem>; using t_Connections = std::vector<ConnectionConfig>;
t_Connections m_connections; t_Connections m_connections;
void deleteFromIni(t_Connections::iterator begin, t_Connections::iterator end); void deleteFromIni(t_Connections::iterator begin, t_Connections::iterator end);
static QString iniFileName();
}; };
#endif // CONNECTIONLIST_H #endif // CONNECTIONLIST_H

View file

@ -140,9 +140,9 @@ void ConnectionListModel::newItem()
emit dataChanged(idx, idx); emit dataChanged(idx, idx);
} }
Expected<ConnectionConfig> ConnectionListModel::get(int row) Expected<ConnectionConfig> ConnectionListModel::get(size_t row)
{ {
if (row >= 0 && row < (int)m_connections->size()) { if (row < m_connections->size()) {
return m_connections->getConfigByIdx(row); return m_connections->getConfigByIdx(row);
} }
else { else {
@ -150,6 +150,27 @@ Expected<ConnectionConfig> ConnectionListModel::get(int row)
} }
} }
#include <boost/assert.hpp>
template <typename T>
size_t as_size_t(T t);
template <>
size_t as_size_t(int t)
{
BOOST_ASSERT(t >= 0);
return static_cast<size_t>(t);
}
template <>
size_t as_size_t(long t)
{
BOOST_ASSERT(t >= 0);
return static_cast<size_t>(t);
}
//void ConnectionListModel::del(const int idx) //void ConnectionListModel::del(const int idx)
bool ConnectionListModel::removeRows(int row, int count, const QModelIndex &parent) 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); beginRemoveRows(parent, row, row + count -1);
SCOPE_EXIT { endRemoveRows(); }; SCOPE_EXIT { endRemoveRows(); };
m_connections->remove(row, count); m_connections->remove(as_size_t(row), as_size_t(count));
result = true; result = true;
} }
return result; return result;
@ -176,7 +197,13 @@ void ConnectionListModel::save()
m_connections->save(); m_connections->save();
} }
void ConnectionListModel::save(int index) void ConnectionListModel::save(size_t index)
{ {
m_connections->save(index); m_connections->save(index);
} }
void ConnectionListModel::save(size_t index, const ConnectionConfig &cc)
{
m_connections->setConfigByIdx(index, cc);
m_connections->save(index);
}

View file

@ -20,7 +20,7 @@ class ConnectionListModel : public QAbstractListModel {
public: public:
ConnectionListModel(ConnectionList *conns, QObject *parent); ConnectionListModel(ConnectionList *conns, QObject *parent);
ConnectionListModel(const ConnectionListModel&) = delete; ConnectionListModel(const ConnectionListModel&) = delete;
~ConnectionListModel(); ~ConnectionListModel() override;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
virtual int columnCount(const QModelIndex &/*parent*/) const override; virtual int columnCount(const QModelIndex &/*parent*/) const override;
@ -30,17 +30,18 @@ public:
virtual Qt::ItemFlags flags(const QModelIndex &index) const override; virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
void newItem(); void newItem();
Expected<ConnectionConfig> get(int row); Expected<ConnectionConfig> get(size_t row);
virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
void save(); 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: private:
ConnectionList *m_connections; ConnectionList *m_connections;
static QString makeLongDescription(const ConnectionConfig &cfg);
}; };
#endif // CONNECTIONLISTMODEL_H #endif // CONNECTIONLISTMODEL_H

View file

@ -7,6 +7,21 @@
#include <QStandardItemModel> #include <QStandardItemModel>
#include "ConnectionListModel.h" #include "ConnectionListModel.h"
#include <QDir>
#include <QStandardPaths>
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) ConnectionManagerWindow::ConnectionManagerWindow(MasterController *master, QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
, ui(new Ui::ConnectionManagerWindow) , ui(new Ui::ConnectionManagerWindow)
@ -53,9 +68,13 @@ void ConnectionManagerWindow::on_currentChanged(const QModelIndex &current,
{ {
int currow = current.row(); int currow = current.row();
auto clm = m_masterController->getConnectionListModel(); auto clm = m_masterController->getConnectionListModel();
clm->save(prevSelection); if (prevSelection)
clm->save(*prevSelection);
m_mapper->setCurrentIndex(currow); m_mapper->setCurrentIndex(currow);
prevSelection = currow; if (currow >= 0)
prevSelection = static_cast<size_t>(currow);
else
prevSelection.reset();
} }
void ConnectionManagerWindow::on_actionDelete_connection_triggered() void ConnectionManagerWindow::on_actionDelete_connection_triggered()
@ -82,7 +101,6 @@ void ConnectionManagerWindow::setupWidgetMappings()
m_mapper->addMapping(ui->edtHost, 2); m_mapper->addMapping(ui->edtHost, 2);
m_mapper->addMapping(ui->spinPort, 3); m_mapper->addMapping(ui->spinPort, 3);
m_mapper->addMapping(ui->edtUser, 4); m_mapper->addMapping(ui->edtUser, 4);
m_mapper->addMapping(ui->edtPassword, 5);
m_mapper->addMapping(ui->edtDbname, 6); m_mapper->addMapping(ui->edtDbname, 6);
m_mapper->toFirst(); m_mapper->toFirst();
} }
@ -90,7 +108,13 @@ void ConnectionManagerWindow::setupWidgetMappings()
void ConnectionManagerWindow::on_actionConnect_triggered() void ConnectionManagerWindow::on_actionConnect_triggered()
{ {
auto ci = ui->listView->selectionModel()->currentIndex(); auto ci = ui->listView->selectionModel()->currentIndex();
m_masterController->openSqlWindowForConnection(ci.row()); if (ci.isValid()) {
auto r = static_cast<size_t>(ci.row());
m_masterController->openSqlWindowForConnection(r);
}
else {
// TODO can we give unobtrusive message why it didn't work?
}
} }
void ConnectionManagerWindow::on_actionQuit_application_triggered() void ConnectionManagerWindow::on_actionQuit_application_triggered()
@ -117,68 +141,3 @@ void ConnectionManagerWindow::on_actionManage_server_triggered()
} }
#include <botan/botan.h>
//#include <botan/base64.h>
//#include <botan/pbkdf.h>
//#include <botan/block_cipher.h>
//#include <botan/hex.h>
#include <botan/cryptobox.h>
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<Botan::byte> 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<Botan::PBKDF> 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<uint8_t> pt(plaintext.data(),plaintext.data()+plaintext.length());
// std::unique_ptr<Botan::Cipher_Mode> enc(Botan::get_cipher_mode("AES-256/CBC/PKCS7", Botan::ENCRYPTION));
// enc->set_key(aes256_key);
// //generate fresh nonce (IV)
// //std::unique_ptr<Botan::RandomNumberGenerator> rng(new Botan::AutoSeeded_RNG);
// std::vector<uint8_t> 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);
}
}

View file

@ -2,6 +2,7 @@
#define CONNECTIONMANAGERWINDOW_H #define CONNECTIONMANAGERWINDOW_H
#include <QMainWindow> #include <QMainWindow>
#include <optional>
namespace Ui { namespace Ui {
class ConnectionManagerWindow; class ConnectionManagerWindow;
@ -18,7 +19,7 @@ class QStandardItemModel;
class ConnectionManagerWindow : public QMainWindow { class ConnectionManagerWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
public: public:
explicit ConnectionManagerWindow(MasterController *master, QWidget *parent = 0); explicit ConnectionManagerWindow(MasterController *master, QWidget *parent = nullptr);
~ConnectionManagerWindow(); ~ConnectionManagerWindow();
private slots: private slots:
@ -30,14 +31,12 @@ private slots:
void on_actionBackup_database_triggered(); void on_actionBackup_database_triggered();
void on_actionManage_server_triggered(); void on_actionManage_server_triggered();
void on_testButton_clicked();
private: private:
Ui::ConnectionManagerWindow *ui; Ui::ConnectionManagerWindow *ui;
QDataWidgetMapper *m_mapper = nullptr; QDataWidgetMapper *m_mapper = nullptr;
MasterController *m_masterController; MasterController *m_masterController;
int prevSelection = -1; std::optional<size_t> prevSelection;
void setupWidgetMappings(); void setupWidgetMappings();
}; };

View file

@ -75,30 +75,23 @@
<widget class="QLineEdit" name="edtUser"/> <widget class="QLineEdit" name="edtUser"/>
</item> </item>
<item row="4" column="0"> <item row="4" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="edtPassword">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>Database</string> <string>Database</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="4" column="1">
<widget class="QLineEdit" name="edtDbname"/> <widget class="QLineEdit" name="edtDbname"/>
</item> </item>
<item row="6" column="1"> <item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>SSL</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="cmbbxSsl"> <widget class="QComboBox" name="cmbbxSsl">
<property name="currentIndex"> <property name="currentIndex">
<number>2</number> <number>2</number>
@ -136,53 +129,46 @@
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>SSL</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_8"> <widget class="QLabel" name="label_8">
<property name="text"> <property name="text">
<string>Certificate</string> <string>Certificate</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="6" column="1">
<widget class="QLineEdit" name="edtCert"/> <widget class="QLineEdit" name="edtCert"/>
</item> </item>
<item row="8" column="0"> <item row="7" column="0">
<widget class="QLabel" name="label_9"> <widget class="QLabel" name="label_9">
<property name="text"> <property name="text">
<string>Key</string> <string>Key</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="1"> <item row="7" column="1">
<widget class="QLineEdit" name="edtKey"/> <widget class="QLineEdit" name="edtKey"/>
</item> </item>
<item row="9" column="0"> <item row="8" column="0">
<widget class="QLabel" name="label_10"> <widget class="QLabel" name="label_10">
<property name="text"> <property name="text">
<string>Root cert.</string> <string>Root cert.</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="1"> <item row="8" column="1">
<widget class="QLineEdit" name="edtRootCert"/> <widget class="QLineEdit" name="edtRootCert"/>
</item> </item>
<item row="10" column="0"> <item row="9" column="0">
<widget class="QLabel" name="label_11"> <widget class="QLabel" name="label_11">
<property name="text"> <property name="text">
<string>Revocation list</string> <string>Revocation list</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="1"> <item row="9" column="1">
<widget class="QLineEdit" name="edtCrl"/> <widget class="QLineEdit" name="edtCrl"/>
</item> </item>
<item row="11" column="0"> <item row="10" column="0">
<widget class="QPushButton" name="testButton"> <widget class="QPushButton" name="testButton">
<property name="text"> <property name="text">
<string>PushButton</string> <string>PushButton</string>
@ -201,7 +187,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>800</width> <width>800</width>
<height>25</height> <height>20</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">

View file

@ -98,7 +98,7 @@ CrudModel::Value CrudModel::getData(const QModelIndex &index) const
//Oid o = m_roData->type(col); //Oid o = m_roData->type(col);
// First see if we have buffered editted values that still need saving // First see if we have buffered editted values that still need saving
boost::optional<Value> val; std::optional<Value> val;
if (row_mapping.pending) { if (row_mapping.pending) {
val = m_pendingRowList.getValue(col, row_mapping.rowKey); val = m_pendingRowList.getValue(col, row_mapping.rowKey);
} }

View file

@ -12,7 +12,7 @@
#include <memory> #include <memory>
#include <tuple> #include <tuple>
#include <vector> #include <vector>
#include <boost/optional.hpp> #include <optional>
class PgConstraint; class PgConstraint;
class OpenDatabase; class OpenDatabase;
@ -121,7 +121,7 @@ private:
// std::shared_ptr<RowList> resultToRowList(std::shared_ptr<Pgsql::Result> result); // std::shared_ptr<RowList> resultToRowList(std::shared_ptr<Pgsql::Result> result);
using Value = boost::optional<std::string>; using Value = std::optional<std::string>;
/** Used to remember the changes that have been made to rows. /** Used to remember the changes that have been made to rows.
* *
@ -194,7 +194,7 @@ private:
iter->second.setValue(col, value); iter->second.setValue(col, value);
} }
boost::optional<Value> getValue(int col, int row) const std::optional<Value> getValue(int col, int row) const
{ {
auto iter = m_rows.find(row); auto iter = m_rows.find(row);
if (iter != m_rows.end()) { if (iter != m_rows.end()) {
@ -204,7 +204,7 @@ private:
return cell->second; return cell->second;
} }
return boost::none; return std::nullopt;
} }
auto begin() { return m_rows.begin(); } auto begin() { return m_rows.begin(); }
@ -233,7 +233,7 @@ private:
ASyncWindow * m_asyncWindow; ASyncWindow * m_asyncWindow;
std::shared_ptr<OpenDatabase> m_database; std::shared_ptr<OpenDatabase> m_database;
PgClass m_table; PgClass m_table;
boost::optional<PgConstraint> m_primaryKey; std::optional<PgConstraint> m_primaryKey;
ASyncDBConnection m_dbConn; ASyncDBConnection m_dbConn;
bool callLoadData = false; bool callLoadData = false;

View file

@ -42,8 +42,7 @@ QSize IconColumnDelegate::sizeHint(const QStyleOptionViewItem &,
void IconColumnDelegate::clearCache() void IconColumnDelegate::clearCache()
{ {
for (auto &e : m_Icons) for (auto &e : m_Icons)
delete e. delete e.second;
second;
m_Icons.clear(); m_Icons.clear();
} }

View file

@ -2,10 +2,30 @@
#include "ConnectionManagerWindow.h" #include "ConnectionManagerWindow.h"
#include "ConnectionList.h" #include "ConnectionList.h"
#include "ConnectionListModel.h" #include "ConnectionListModel.h"
#include "PasswordManager.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "ServerWindow.h" #include "ServerWindow.h"
#include "BackupDialog.h" #include "BackupDialog.h"
#include "PasswordPromptDialog.h"
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
#include <botan/sqlite3.h>
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) MasterController::MasterController(QObject *parent) : QObject(parent)
{} {}
@ -19,6 +39,21 @@ MasterController::~MasterController()
void MasterController::init() void MasterController::init()
{ {
//std::string dbfilename = QDir::toNativeSeparators(GetUserConfigDatabaseName()).toUtf8().data();
//m_userConfigDatabase = std::make_shared<Botan::Sqlite3_Database>(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<PasswordManager>();
m_connectionList = new ConnectionList; m_connectionList = new ConnectionList;
m_connectionList->load(); m_connectionList->load();
m_connectionListModel = new ConnectionListModel(m_connectionList, this); m_connectionListModel = new ConnectionListModel(m_connectionList, this);
@ -33,39 +68,159 @@ void MasterController::showConnectionManager()
m_connectionManagerWindow->show(); 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); auto res = m_connectionListModel->get(connection_index);
if (cc.valid()) { 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); auto w = new MainWindow(this, nullptr);
w->setAttribute( Qt::WA_DeleteOnClose ); w->setAttribute( Qt::WA_DeleteOnClose );
w->setConfig(cc.get()); w->setConfig(cc);
w->show(); w->show();
} }
}
} }
void MasterController::openBackupDlgForConnection(int connection_index) void MasterController::openBackupDlgForConnection(size_t connection_index)
{ {
auto cc = m_connectionListModel->get(connection_index); auto res = m_connectionListModel->get(connection_index);
m_connectionListModel->save(connection_index); if (res.valid()) {
if (cc.valid()) { auto cc = res.get();
if (retrieveConnectionPassword(cc)) {
m_connectionListModel->save(connection_index, cc);
auto w = new BackupDialog(nullptr); //new ServerWindow(this, nullptr); auto w = new BackupDialog(nullptr); //new ServerWindow(this, nullptr);
w->setAttribute( Qt::WA_DeleteOnClose ); w->setAttribute( Qt::WA_DeleteOnClose );
w->setConfig(cc.get()); w->setConfig(cc);
w->show(); w->show();
} }
}
} }
void MasterController::openServerWindowForConnection(int connection_index) void MasterController::openServerWindowForConnection(size_t connection_index)
{ {
auto cc = m_connectionListModel->get(connection_index); auto res = m_connectionListModel->get(connection_index);
m_connectionListModel->save(connection_index); if (res.valid()) {
if (cc.valid()) { auto cc = res.get();
if (retrieveConnectionPassword(cc)) {
m_connectionListModel->save(connection_index, cc);
auto w = new ServerWindow(this, nullptr); auto w = new ServerWindow(this, nullptr);
w->setAttribute( Qt::WA_DeleteOnClose ); w->setAttribute( Qt::WA_DeleteOnClose );
w->setConfig(cc.get()); w->setConfig(cc);
w->show(); 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>(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<PasswordPromptDialog>(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>(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<Botan::Sqlite3_Database> MasterController::getUserConfigDatabase()
//{
// return m_userConfigDatabase;
//}

View file

@ -2,21 +2,28 @@
#define MASTERCONTROLLER_H #define MASTERCONTROLLER_H
#include <QObject> #include <QObject>
#include <QSqlDatabase>
#include <atomic>
#include <future> #include <future>
#include <map> #include <map>
#include <memory>
//namespace Botan {
// class Sqlite3_Database;
//}
class ConnectionConfig; class ConnectionConfig;
class ConnectionList; class ConnectionList;
class ConnectionListModel; class ConnectionListModel;
class ConnectionManagerWindow; class ConnectionManagerWindow;
class PasswordManager;
/** \brief Controller class responsible for all things global. /** \brief Controller class responsible for all things global.
*/ */
class MasterController : public QObject { class MasterController : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit MasterController(QObject *parent = 0); explicit MasterController(QObject *parent = nullptr);
MasterController(const MasterController&) = delete; MasterController(const MasterController&) = delete;
MasterController &operator=(const MasterController&) = delete; MasterController &operator=(const MasterController&) = delete;
~MasterController(); ~MasterController();
@ -29,9 +36,11 @@ public:
} }
void showConnectionManager(); void showConnectionManager();
void openSqlWindowForConnection(int connection_index); void openSqlWindowForConnection(size_t connection_index);
void openServerWindowForConnection(int connection_index); void openServerWindowForConnection(size_t connection_index);
void openBackupDlgForConnection(int connection_index); void openBackupDlgForConnection(size_t connection_index);
// std::shared_ptr<Botan::Sqlite3_Database> getUserConfigDatabase();
signals: signals:
@ -41,7 +50,25 @@ private:
ConnectionList *m_connectionList = nullptr; ConnectionList *m_connectionList = nullptr;
ConnectionListModel *m_connectionListModel = nullptr; ConnectionListModel *m_connectionListModel = nullptr;
ConnectionManagerWindow *m_connectionManagerWindow = nullptr; ConnectionManagerWindow *m_connectionManagerWindow = nullptr;
//std::shared_ptr<Botan::Sqlite3_Database> 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<PasswordManager> 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 #endif // MASTERCONTROLLER_H

View file

@ -0,0 +1,100 @@
#include "PasswordPromptDialog.h"
#include <QtWidgets/QApplication>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QLabel>
#include <QLayout>
#include <QLineEdit>
#include <QPushButton>
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;
}

View file

@ -0,0 +1,44 @@
#ifndef PASSWORDPROMPTDIALOG_H
#define PASSWORDPROMPTDIALOG_H
#include <QDialog>
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

View file

@ -3,6 +3,7 @@
#include "Pgsql_oids.h" #include "Pgsql_oids.h"
#include "ResultTableModelUtil.h" #include "ResultTableModelUtil.h"
#include "CustomDataRole.h"
PgLabItemDelegate::PgLabItemDelegate(QObject *parent) PgLabItemDelegate::PgLabItemDelegate(QObject *parent)
: QStyledItemDelegate(parent) : QStyledItemDelegate(parent)
@ -81,7 +82,7 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
// } // }
Oid oid = InvalidOid; Oid oid = InvalidOid;
value = index.data(Qt::UserRole); // get OID value = index.data(CustomDataTypeRole); // get OID
if (value.isValid()) if (value.isValid())
oid = value.toUInt(); //getType(index.column()); oid = value.toUInt(); //getType(index.column());

35
pglab/PropertiesPage.cpp Normal file
View file

@ -0,0 +1,35 @@
#include "PropertiesPage.h"
#include "PgLabItemDelegate.h"
#include "PropertyProxyModel.h"
#include "ResultTableModelUtil.h"
#include "SqlCodePreview.h"
#include <QTableView>
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);
}

36
pglab/PropertiesPage.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef PROPERTIESPAGE_H
#define PROPERTIESPAGE_H
#include <QSplitter>
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

View file

@ -8,6 +8,7 @@ SqlCodePreview::SqlCodePreview(QWidget *parent)
auto&& config = UserConfiguration::instance(); auto&& config = UserConfiguration::instance();
setFont(config->codeFont()); setFont(config->codeFont());
setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
setWordWrapMode(QTextOption::NoWrap);
m_highlighter = new SqlSyntaxHighlighter(document()); m_highlighter = new SqlSyntaxHighlighter(document());
} }

View file

@ -9,7 +9,7 @@
#include "IndexModel.h" #include "IndexModel.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "PgLabItemDelegate.h" #include "PgLabItemDelegate.h"
#include "PropertyProxyModel.h" #include "PropertiesPage.h"
#include "ResultTableModelUtil.h" #include "ResultTableModelUtil.h"
#include "SqlFormattingUtils.h" #include "SqlFormattingUtils.h"
#include "SqlSyntaxHighlighter.h" #include "SqlSyntaxHighlighter.h"
@ -26,15 +26,13 @@ TablesPage::TablesPage(MainWindow *parent)
{ {
ui->setupUi(this); ui->setupUi(this);
auto pglab_delegate = new PgLabItemDelegate(this);
auto icon_delegate = new IconColumnDelegate(this);
SetTableViewDefault(ui->tableListTable); SetTableViewDefault(ui->tableListTable);
m_tablesModel = new TablesTableModel(this); m_tablesModel = new TablesTableModel(this);
ui->tableListTable->setModel(m_tablesModel); ui->tableListTable->setModel(m_tablesModel);
ui->tableListTable->setItemDelegate(pglab_delegate); ui->tableListTable->setItemDelegate(new PgLabItemDelegate(this));
ui->tableListTable->setSortingEnabled(true); ui->tableListTable->setSortingEnabled(true);
ui->tableListTable->sortByColumn(0, Qt::AscendingOrder); ui->tableListTable->sortByColumn(0, Qt::AscendingOrder);
ui->tableListTable->setSelectionBehavior(QAbstractItemView::SelectRows);
// Columns // Columns
SetTableViewDefault(ui->columnsTable); SetTableViewDefault(ui->columnsTable);
@ -45,22 +43,14 @@ TablesPage::TablesPage(MainWindow *parent)
SetTableViewDefault(ui->constraintsTable); SetTableViewDefault(ui->constraintsTable);
m_constraintModel = new ConstraintModel(this); m_constraintModel = new ConstraintModel(this);
ui->constraintsTable->setModel(m_constraintModel); ui->constraintsTable->setModel(m_constraintModel);
ui->constraintsTable->setItemDelegateForColumn(0, icon_delegate); ui->constraintsTable->setItemDelegateForColumn(0, new IconColumnDelegate(this));
// Indexes // Indexes
SetTableViewDefault(ui->indexesTable); SetTableViewDefault(ui->indexesTable);
m_indexModel = new IndexModel(this); m_indexModel = new IndexModel(this);
ui->indexesTable->setModel(m_indexModel); ui->indexesTable->setModel(m_indexModel);
ui->indexesTable->setItemDelegate(pglab_delegate); ui->indexesTable->setItemDelegate(new PgLabItemDelegate(this));
ui->indexesTable->setItemDelegateForColumn(0, icon_delegate); ui->indexesTable->setItemDelegateForColumn(0, new IconColumnDelegate(this));
// 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);
// Set code editor fonts // Set code editor fonts
QFont code_font = UserConfiguration::instance()->codeFont(); QFont code_font = UserConfiguration::instance()->codeFont();
@ -71,13 +61,13 @@ TablesPage::TablesPage(MainWindow *parent)
// Connect signals // Connect signals
// --------------- // ---------------
// Table selection // Table selection
connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged,
property_model, &PropertyProxyModel::setActiveRow);
connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this,
&TablesPage::tableListTable_currentRowChanged); &TablesPage::tableListTable_currentRowChanged);
// connect(ui->constraintsTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, connect(m_tablesModel, &QAbstractItemModel::layoutChanged,
// &TablesPage::constraintsTable_currentRowChanged); this, &TablesPage::tableListTable_layoutChanged);
//layoutChanged(const QList<QPersistentModelIndex> &parents = ..., QAbstractItemModel::LayoutChangeHint hint = ...)
connect(ui->constraintsTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, connect(ui->constraintsTable->selectionModel(), &QItemSelectionModel::selectionChanged, this,
&TablesPage::constraintsTable_selectionChanged); &TablesPage::constraintsTable_selectionChanged);
@ -92,8 +82,17 @@ TablesPage::TablesPage(MainWindow *parent)
&TablesPage::indexesTable_modelReset); &TablesPage::indexesTable_modelReset);
// Non designer based code // 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_triggerPage = new TriggerPage(this);
m_triggerTab = addDetailTab(m_triggerPage, tr("Triggers")); m_triggerTab = addDetailTab(m_triggerPage);
retranslateUi(false); retranslateUi(false);
} }
@ -103,16 +102,17 @@ void TablesPage::retranslateUi(bool all)
if (all) if (all)
ui->retranslateUi(this); 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)); 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 tab = new QWidget();
auto verticalLayout = new QVBoxLayout(tab); auto verticalLayout = new QVBoxLayout(tab);
verticalLayout->addWidget(contents); verticalLayout->addWidget(contents);
ui->twDetails->addTab(tab, caption); ui->twDetails->addTab(tab, "");
return tab; return tab;
} }
@ -128,7 +128,6 @@ void TablesPage::setCatalog(std::shared_ptr<PgDatabaseCatalog> cat)
ui->tableListTable->resizeColumnsToContents(); ui->tableListTable->resizeColumnsToContents();
m_triggerPage->setCatalog(cat); m_triggerPage->setCatalog(cat);
// m_namespaceFilterWidget->init(cat->namespaces());
auto highlighter = new SqlSyntaxHighlighter(ui->constraintSqlEdit->document()); auto highlighter = new SqlSyntaxHighlighter(ui->constraintSqlEdit->document());
highlighter->setTypes(*cat->types()); highlighter->setTypes(*cat->types());
@ -136,10 +135,26 @@ void TablesPage::setCatalog(std::shared_ptr<PgDatabaseCatalog> cat)
highlighter->setTypes(*cat->types()); highlighter->setTypes(*cat->types());
} }
void TablesPage::tableListTable_currentRowChanged(const QModelIndex &current, const QModelIndex &previous) void TablesPage::tableListTable_currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
{ {
if (current.row() != previous.row()) { if (current.row() != previous.row()) {
PgClass table = m_tablesModel->getTable(current.row()); PgClass table = m_tablesModel->getTable(current.row());
selectedTableChanged(table);
}
}
void TablesPage::tableListTable_layoutChanged(const QList<QPersistentModelIndex> &, 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); m_columnsModel->setData(m_catalog, table);
ui->columnsTable->resizeColumnsToContents(); ui->columnsTable->resizeColumnsToContents();
@ -151,21 +166,8 @@ void TablesPage::tableListTable_currentRowChanged(const QModelIndex &current, co
ui->indexesTable->resizeColumnsToContents(); ui->indexesTable->resizeColumnsToContents();
m_triggerPage->setFilter(table); m_triggerPage->setFilter(table);
}
} }
//void TablesPage::constraintsTable_currentRowChanged(const QModelIndex &current, 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::constraintsTable_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) void TablesPage::constraintsTable_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/)
{ {

View file

@ -16,7 +16,9 @@ class PgDatabaseCatalog;
class NamespaceFilterWidget; class NamespaceFilterWidget;
class IndexModel; class IndexModel;
class MainWindow; class MainWindow;
class PropertiesPage;
class TriggerPage; class TriggerPage;
class PgClass;
class TablesPage : public QWidget class TablesPage : public QWidget
{ {
@ -30,6 +32,8 @@ public:
private: private:
Ui::TablesPage *ui; Ui::TablesPage *ui;
MainWindow *m_window; MainWindow *m_window;
QWidget *m_propertiesTab;
PropertiesPage *m_propertiesPage;
QWidget *m_triggerTab; QWidget *m_triggerTab;
TriggerPage *m_triggerPage; TriggerPage *m_triggerPage;
std::shared_ptr<PgDatabaseCatalog> m_catalog; std::shared_ptr<PgDatabaseCatalog> m_catalog;
@ -40,10 +44,13 @@ private:
//NamespaceFilterWidget* m_namespaceFilterWidget; //NamespaceFilterWidget* m_namespaceFilterWidget;
void retranslateUi(bool all = true); void retranslateUi(bool all = true);
QWidget* addDetailTab(QWidget *contents, QString caption); QWidget* addDetailTab(QWidget *contents);
void selectedTableChanged(const PgClass & table);
private slots: private slots:
void tableListTable_currentRowChanged(const QModelIndex &current, const QModelIndex &previous); void tableListTable_currentRowChanged(const QModelIndex &current, const QModelIndex &previous);
void tableListTable_layoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint);
// void constraintsTable_currentRowChanged(const QModelIndex &current, const QModelIndex &previous); // void constraintsTable_currentRowChanged(const QModelIndex &current, const QModelIndex &previous);
void constraintsTable_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void constraintsTable_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
void constraintsTable_modelReset(); void constraintsTable_modelReset();

View file

@ -38,7 +38,7 @@
</widget> </widget>
<widget class="QTabWidget" name="twDetails"> <widget class="QTabWidget" name="twDetails">
<property name="currentIndex"> <property name="currentIndex">
<number>3</number> <number>2</number>
</property> </property>
<widget class="QWidget" name="columnsTab"> <widget class="QWidget" name="columnsTab">
<attribute name="title"> <attribute name="title">
@ -86,16 +86,6 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="propertiesTab">
<attribute name="title">
<string>Properties</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayoutProperties">
<item>
<widget class="QTableView" name="tablePropertiesTable"/>
</item>
</layout>
</widget>
</widget> </widget>
</widget> </widget>
</item> </item>

View file

@ -21,14 +21,12 @@ TriggerPage::TriggerPage(QWidget *parent)
SetTableViewDefault(m_tableView); SetTableViewDefault(m_tableView);
QFont code_font = UserConfiguration::instance()->codeFont();
m_definitionView->setFont(code_font);
m_model = new TriggerTableModel(this); m_model = new TriggerTableModel(this);
m_sortFilterProxy = new CustomFilterSortModel(this); m_sortFilterProxy = new CustomFilterSortModel(this);
m_sortFilterProxy->setSourceModel(m_model); m_sortFilterProxy->setSourceModel(m_model);
m_tableView->setModel(m_sortFilterProxy); m_tableView->setModel(m_sortFilterProxy);
m_tableView->setSortingEnabled(true); m_tableView->setSortingEnabled(true);
m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
auto item_delegate = new PgLabItemDelegate(this); auto item_delegate = new PgLabItemDelegate(this);
m_tableView->setItemDelegate(item_delegate); m_tableView->setItemDelegate(item_delegate);

View file

@ -1,11 +1,9 @@
#ifndef TRIGGERPAGE_H #ifndef TRIGGERPAGE_H
#define TRIGGERPAGE_H #define TRIGGERPAGE_H
#include <QObject>
#include <QSplitter> #include <QSplitter>
#include <memory> #include <memory>
class QSplitter;
class QTableView; class QTableView;
class SqlCodePreview; class SqlCodePreview;
class PgDatabaseCatalog; class PgDatabaseCatalog;

View file

@ -23,7 +23,7 @@ DEFINES += _WIN32_WINNT=0x0501
#LIBS += -LC:\VSproj\boost32\lib -LC:/PROG/LIB -lws2_32 -llibpq #LIBS += -LC:\VSproj\boost32\lib -LC:/PROG/LIB -lws2_32 -llibpq
#debug { #debug {
LIBS += c:/prog/lib/botand_imp.lib LIBS += c:/prog/lib/botan_imp.lib
#} #}
#release { #release {
@ -82,7 +82,9 @@ PropertyProxyModel.cpp \
TriggerTableModel.cpp \ TriggerTableModel.cpp \
TriggerPage.cpp \ TriggerPage.cpp \
SqlCodePreview.cpp \ SqlCodePreview.cpp \
CustomFilterSortModel.cpp CustomFilterSortModel.cpp \
PropertiesPage.cpp \
PasswordPromptDialog.cpp
HEADERS += \ HEADERS += \
QueryResultModel.h \ QueryResultModel.h \
@ -135,7 +137,9 @@ CustomDataRole.h \
TriggerTableModel.h \ TriggerTableModel.h \
TriggerPage.h \ TriggerPage.h \
SqlCodePreview.h \ SqlCodePreview.h \
CustomFilterSortModel.h CustomFilterSortModel.h \
PropertiesPage.h \
PasswordPromptDialog.h
FORMS += mainwindow.ui \ FORMS += mainwindow.ui \
ConnectionManagerWindow.ui \ ConnectionManagerWindow.ui \

View file

@ -57,6 +57,22 @@ ConnectionConfig::ConnectionConfig()
: m_applicationName(QCoreApplication::applicationName().toUtf8().data()) : 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) void ConnectionConfig::setName(std::string desc)
{ {
if (m_name != desc) { if (m_name != desc) {
@ -242,6 +258,19 @@ const char * const * ConnectionConfig::getValues() const
return m_values.data(); 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 bool ConnectionConfig::isSameDatabase(const ConnectionConfig &rhs) const
{ {
return m_host == rhs.m_host return m_host == rhs.m_host

View file

@ -1,6 +1,7 @@
#ifndef CONNECTION_H #ifndef CONNECTION_H
#define CONNECTION_H #define CONNECTION_H
#include <QUuid>
#include <vector> #include <vector>
#include <string> #include <string>
@ -14,10 +15,10 @@ enum class SslMode {
verify_full=5 verify_full=5
}; };
enum class PasswordMode { enum class PasswordState {
Unsave, NotNeeded, ///< the Connection doesn't require a password
Encrypted, NotStored, ///< password needed but we do not know it
DontSave SavedPasswordManager, ///< Saved in the password manager
}; };
class QProcessEnvironment; class QProcessEnvironment;
@ -27,6 +28,9 @@ class ConnectionConfig {
public: public:
ConnectionConfig(); ConnectionConfig();
void setUuid(const QUuid &uuid);
const QUuid &uuid() const;
void setName(std::string desc); void setName(std::string desc);
const std::string& name() const; const std::string& name() const;
@ -66,6 +70,9 @@ public:
const char * const * getKeywords() const; const char * const * getKeywords() const;
const char * const * getValues() const; const char * const * getValues() const;
PasswordState passwordState() const;
void setPasswordState(PasswordState password_state);
bool isSameDatabase(const ConnectionConfig &rhs) const; bool isSameDatabase(const ConnectionConfig &rhs) const;
void writeToEnvironment(QProcessEnvironment &env) const; void writeToEnvironment(QProcessEnvironment &env) const;
@ -73,13 +80,14 @@ public:
bool dirty() const; bool dirty() const;
void clean(); void clean();
private: private:
QUuid m_uuid;
std::string m_name; std::string m_name;
std::string m_host; std::string m_host;
std::string m_hostaddr; std::string m_hostaddr;
std::string m_port = "5432"; std::string m_port = "5432";
std::string m_user; 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_dbname;
std::string m_sslMode; std::string m_sslMode;
@ -89,9 +97,11 @@ private:
std::string m_sslCrl; std::string m_sslCrl;
std::string m_applicationName; std::string m_applicationName;
PasswordState m_passwordState = PasswordState::NotStored;
bool m_dirty = false; bool m_dirty = false;
static void strToEnv(QProcessEnvironment &env, const QString &var, const std::string &val); static void strToEnv(QProcessEnvironment &env, const QString &var, const std::string &val);
static std::vector<const char*> s_keywords; static std::vector<const char*> s_keywords;

View file

@ -74,9 +74,9 @@ std::vector<PgConstraint> PgConstraintContainer::getConstraintsForRelation(Oid r
return result; return result;
} }
boost::optional<PgConstraint> PgConstraintContainer::getPrimaryForRelation(Oid relid) const std::optional<PgConstraint> PgConstraintContainer::getPrimaryForRelation(Oid relid) const
{ {
boost::optional<PgConstraint> result; std::optional<PgConstraint> result;
for (const auto &e : m_container) { for (const auto &e : m_container) {
if (e.relid == relid && e.type == ConstraintType::PrimaryKey) { if (e.relid == relid && e.type == ConstraintType::PrimaryKey) {
result = e; result = e;

View file

@ -5,7 +5,7 @@
#include "PgConstraint.h" #include "PgConstraint.h"
#include "Pgsql_declare.h" #include "Pgsql_declare.h"
#include <vector> #include <vector>
#include <boost/optional.hpp> #include <optional>
class PgConstraintContainer : public PgContainer<PgConstraint> { class PgConstraintContainer : public PgContainer<PgConstraint> {
public: public:
@ -16,7 +16,7 @@ public:
const PgConstraint* getFKeyForTableColumn(Oid relid, int16_t attnum) const; const PgConstraint* getFKeyForTableColumn(Oid relid, int16_t attnum) const;
std::vector<PgConstraint> getConstraintsForRelation(Oid relid) const; std::vector<PgConstraint> getConstraintsForRelation(Oid relid) const;
boost::optional<PgConstraint> getPrimaryForRelation(Oid relid) const; std::optional<PgConstraint> getPrimaryForRelation(Oid relid) const;
protected: protected:
virtual PgConstraint loadElem(const Pgsql::Row &row) override; virtual PgConstraint loadElem(const Pgsql::Row &row) override;
}; };

View file

@ -75,9 +75,13 @@ QString getTablespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid)
if (oid == 0) { if (oid == 0) {
auto dbname = cat.getDBName(); auto dbname = cat.getDBName();
oid = cat.databases()->getByName(dbname).tablespace; oid = cat.databases()->getByName(dbname).tablespace;
auto ts = cat.tablespaces()->getByKey(oid);
return ts.name + " (inherited)";
} }
else {
auto ts = cat.tablespaces()->getByKey(oid); auto ts = cat.tablespaces()->getByKey(oid);
return ts.name; return ts.name;
}
} }
QString getTypeDisplayString(const PgDatabaseCatalog &cat, Oid oid, int32_t typmod) QString getTypeDisplayString(const PgDatabaseCatalog &cat, Oid oid, int32_t typmod)

View file

@ -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 // Any way at generation time we might want to be able to specify the null handle
// - exception/error return // - exception/error return
// - magic value // - magic value
// - boost::optional // - std::optional
// - boolean flags // - boolean flags
// - null pointer (useful for languages where this has no cost, other cases boolean flags will be more performant) // - null pointer (useful for languages where this has no cost, other cases boolean flags will be more performant)
} }

View file

@ -41,7 +41,7 @@ public:
* field often provides enough flexibility. * field often provides enough flexibility.
*/ */
QString m_prefixWith; QString m_prefixWith;
// boost::optional<CharToNumericConversion> m_numericConversion; // std::optional<CharToNumericConversion> m_numericConversion;
}; };
#endif // STRINGESCAPERULE_H #endif // STRINGESCAPERULE_H

View file

@ -4,7 +4,7 @@
# #
#------------------------------------------------- #-------------------------------------------------
QT += widgets QT += widgets core
TARGET = pglablib TARGET = pglablib
TEMPLATE = lib TEMPLATE = lib

View file

@ -6,7 +6,7 @@
#include <libpq-fe.h> #include <libpq-fe.h>
#include "Pgsql_declare.h" #include "Pgsql_declare.h"
#include "Pgsql_oids.h" #include "Pgsql_oids.h"
#include <boost/optional.hpp> #include <optional>
namespace Pgsql { namespace Pgsql {
@ -37,7 +37,7 @@ namespace Pgsql {
Param add(const QString &s, Oid oid=varchar_oid); Param add(const QString &s, Oid oid=varchar_oid);
Param add(const char *data, Oid oid=varchar_oid); Param add(const char *data, Oid oid=varchar_oid);
Param add(boost::optional<std::string> s, Oid oid=varchar_oid) Param add(std::optional<std::string> s, Oid oid=varchar_oid)
{ {
return add(s ? s->c_str() : nullptr, oid); return add(s ? s->c_str() : nullptr, oid);
} }

View file

@ -8,7 +8,7 @@
#include <vector> #include <vector>
#include <QString> #include <QString>
#include <QDateTime> #include <QDateTime>
#include <boost/optional.hpp> #include <optional>
namespace Pgsql { namespace Pgsql {
@ -154,10 +154,10 @@ namespace Pgsql {
}; };
template <typename T> template <typename T>
void operator<<(boost::optional<T> &s, const Value &v) void operator<<(std::optional<T> &s, const Value &v)
{ {
if (v.null()) if (v.null())
s = boost::optional<T>(); s = std::optional<T>();
else else
*s << v; *s << v;
} }

0
readme.md Normal file
View file

View file

@ -39,7 +39,7 @@ C:\VSproj\boost32\include
INCLUDEPATH += $$PWD/../../core INCLUDEPATH += $$PWD/../../core
DEPENDPATH += $$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 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 else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/debug/libcore.a

View file

@ -1,74 +1,93 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gmock/gmock-matchers.h> #include <gmock/gmock-matchers.h>
#include "PasswordManager.h" //#include "PasswordManager.h"
#include "PrintTo_Qt.h" #include "PrintTo_Qt.h"
using namespace testing; using namespace testing;
TEST(PasswordManager, initial_changeMasterPassword_returns_true) #include <botan/pwdhash.h>
{ #include <botan/scrypt.h>
PasswordManager pwm(10);
auto res = pwm.changeMasterPassword("", "my test passphrase"); //TEST(Botan, recreate)
ASSERT_NO_THROW( res.get() ); //{
ASSERT_THAT( res.get(), Eq(true) ); // 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<Botan::Scrypt*>(ph.get());
TEST(PasswordManager, unlock_succeeds) // ASSERT_EQ(N, sc->N());
{ // ASSERT_EQ(r, sc->r());
PasswordManager pwm(10); // 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); //TEST(PasswordManager, initial_changeMasterPassword_returns_true)
ASSERT_NO_THROW( res2.get() ); //{
ASSERT_THAT( res2.get(), Eq(true) ); // PasswordManager pwm(10);
}
TEST(PasswordManager, unlock_fails) // auto res = pwm.changeMasterPassword("", "my test passphrase");
{ // ASSERT_NO_THROW( res.get() );
PasswordManager pwm(10); // ASSERT_THAT( res.get(), Eq(true) );
//}
std::string passphrase = "my test passphrase"; //TEST(PasswordManager, unlock_succeeds)
//{
// PasswordManager pwm(10);
auto res = pwm.changeMasterPassword("", passphrase); // std::string passphrase = "my test passphrase";
ASSERT_NO_THROW( res.get() );
ASSERT_THAT( res.get(), Eq(true) );
auto res2 = pwm.unlock(passphrase + "2"); // auto res = pwm.changeMasterPassword("", passphrase);
ASSERT_NO_THROW( res2.get() ); // ASSERT_NO_THROW( res.get() );
ASSERT_THAT( res2.get(), Eq(false) ); // ASSERT_THAT( res.get(), Eq(true) );
}
TEST(PasswordManager, test_save_get) // auto res2 = pwm.unlock(passphrase);
{ // ASSERT_NO_THROW( res2.get() );
PasswordManager pwm(10); // ASSERT_THAT( res2.get(), Eq(true) );
//}
std::string passphrase = "my test passphrase"; //TEST(PasswordManager, unlock_fails)
//{
// PasswordManager pwm(10);
auto res = pwm.changeMasterPassword("", passphrase); // std::string passphrase = "my test passphrase";
ASSERT_NO_THROW( res.get() );
ASSERT_THAT( res.get(), Eq(true) ); // auto res = pwm.changeMasterPassword("", passphrase);
// ASSERT_NO_THROW( res.get() );
// ASSERT_THAT( res.get(), Eq(true) );
// auto res2 = pwm.unlock(passphrase + "2"); // auto res2 = pwm.unlock(passphrase + "2");
// ASSERT_NO_THROW( res2.get() ); // ASSERT_NO_THROW( res2.get() );
// ASSERT_THAT( res2.get(), Eq(false) ); // ASSERT_THAT( res2.get(), Eq(false) );
//}
const std::string password = "password123"; //TEST(PasswordManager, test_save_get)
const std::string key = "abc"; //{
// PasswordManager pwm(10);
auto res2 = pwm.savePassword(key, password); // std::string passphrase = "my test passphrase";
ASSERT_THAT( res2.valid(), Eq(true) );
std::string result; // auto res = pwm.changeMasterPassword("", passphrase);
auto res3 = pwm.getPassword(key, result); // ASSERT_NO_THROW( res.get() );
ASSERT_THAT( res3.valid(), Eq(true) ); // ASSERT_THAT( res.get(), Eq(true) );
ASSERT_THAT( res3.get(), Eq(true) );
ASSERT_THAT( result, Eq(password) );
} //// 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) );
//}