Passwords are now saved in a password manager.

The password manager uses strong encryption using a key derived from the passphrase using
scrypt key strengthening algorithm. This ensures encryption is performed using a strong key
and that brute forcing the passphrase is time consuming.

If the user loses his passphrase no recovery is possible.
This commit is contained in:
eelke 2018-11-08 21:50:49 +01:00
parent 2230a4bd61
commit e36924c087
27 changed files with 605 additions and 346 deletions

View file

@ -1,22 +1,128 @@
#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 <botan/hash.h> #include <QVariant>
//#include <botan/hmac.h> #include <botan/hash.h>
//#include <botan/pbkdf2.h>
//#include <botan/rng.h>
//#include <botan/base64.h>
//#include <botan/loadstor.h>
//#include <botan/mem_ops.h>
#include <botan/auto_rng.h> #include <botan/auto_rng.h>
#include <botan/base64.h> #include <botan/base64.h>
#include <botan/psk_db_sql.h> //#include <botan/psk_db_sql.h>
#include <botan/sqlite3.h> #include <botan/psk_db.h>
//#include <botan/sqlite3.h>
#include <botan/scrypt.h> #include <botan/scrypt.h>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
namespace {
class SqlException : public std::runtime_error {
public:
QSqlError error;
SqlException(const QSqlError &err)
: std::runtime_error(err.text().toUtf8().data())
, error(err)
{}
};
class QPSK_Database : public Botan::Encrypted_PSK_Database
{
public:
/**
* @param master_key specifies the master key used to encrypt all
* keys and value. It can be of any length, but should be at least 256 bits.
*
* Subkeys for the cryptographic algorithms used are derived from this
* master key. No key stretching is performed; if encrypting a PSK database
* using a password, it is recommended to use PBKDF2 to derive the database
* master key.
*/
QPSK_Database(const Botan::secure_vector<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;
};
}
Botan::secure_vector<uint8_t> PasswordManager::KeyStrengthener::derive(const std::string &passphrase) Botan::secure_vector<uint8_t> PasswordManager::KeyStrengthener::derive(const std::string &passphrase)
@ -27,69 +133,129 @@ Botan::secure_vector<uint8_t> PasswordManager::KeyStrengthener::derive(const std
return master_key; return master_key;
} }
void PasswordManager::KeyStrengthener::saveParams(std::shared_ptr<Botan::Sqlite3_Database> db, const std::string &table_name) void PasswordManager::KeyStrengthener::saveParams(QSqlDatabase &db, const QString &table_name)
{ {
auto sc = dynamic_cast<Botan::Scrypt*>(m_hasher.get()); auto sc = dynamic_cast<Botan::Scrypt*>(m_hasher.get());
size_t i1 = sc->N(); size_t i1 = sc->N();
size_t i2 = sc->r(); size_t i2 = sc->r();
size_t i3 = sc->p(); size_t i3 = sc->p();
auto salt_str = QString::fromUtf8(Botan::base64_encode(m_salt).c_str());
// SAVE parameters in database // SAVE parameters in database
auto stmt = db->new_statement("INSERT INTO " + table_name + "(id, algo, i1, i2, i3, ks, salt) VALUES(?1, ?2, ?3, ?4, ?5)"); QSqlQuery insert_statement(db);
stmt->bind(1, 1); insert_statement.prepare("INSERT OR REPLACE INTO " + table_name + "(id, algo, i1, i2, i3, ks, salt) "
stmt->bind(2, "Scrypt"); + "VALUES(:id, :algo, :i1, :i2, :i3, :ks, :salt)");
stmt->bind(3, i1); insert_statement.bindValue(":id", 1);
stmt->bind(4, i2); insert_statement.bindValue(":algo", "Scrypt");
stmt->bind(5, i3); insert_statement.bindValue(":i1", i1);
stmt->bind(6, m_keySize); insert_statement.bindValue(":i2", i2);
stmt->bind(7, Botan::base64_encode(m_salt)); insert_statement.bindValue(":i3", i3);
stmt->spin(); insert_statement.bindValue(":ks", m_keySize);
insert_statement.bindValue(":salt", salt_str);
if (!insert_statement.exec()) {
//throw std::runtime_error("PasswordManager::KeyStrengthener::saveParams failed");
auto err = insert_statement.lastError();
throw SqlException(err);
}
} }
// ------------------------- // -------------------------
void PasswordManager::openDatabase(std::shared_ptr<Botan::Sqlite3_Database> db, std::string passphrase) PasswordManager::PasswordManager() = default;
PasswordManager::~PasswordManager() = default;
bool PasswordManager::initialized(QSqlDatabase& db)
{ {
// std::string psk_db_file_name; return isPskStoreInitialized(db);
// auto db = std::make_shared<Botan::Sqlite3_Database>(psk_db_file_name);
KeyStrengthener ks;
// if (database exists)
if (isPskStoreInitialized(db)) {
ks = getKeyStrengthener(db);
}
else {
initializeNewPskStore(db);
ks = createKeyStrengthener();
ks.saveParams(db, m_secretAlgoTableName);
}
Botan::secure_vector<uint8_t> master_key = ks.derive(passphrase);
m_pskDatabase = std::make_unique<Botan::Encrypted_PSK_Database_SQL>(master_key, db, m_passwordTableName);
} }
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() void PasswordManager::closeDatabase()
{ {
m_pskDatabase.reset(); m_pskDatabase.reset();
} }
bool PasswordManager::locked() const
{
return m_pskDatabase == nullptr;
}
void PasswordManager::set(const std::string &id, const std::string &passwd) void PasswordManager::set(const std::string &id, const std::string &passwd)
{ {
if (m_pskDatabase) { if (m_pskDatabase) {
m_pskDatabase->set_str(id, passwd);
} }
else { else {
throw PasswordManagerLockedException(); throw PasswordManagerLockedException();
} }
} }
std::string PasswordManager::get(const std::string &id, const std::string &passwd) bool PasswordManager::get(const std::string &id, std::string &password)
{ {
if (m_pskDatabase) { if (m_pskDatabase) {
try {
password = m_pskDatabase->get_str(id);
return true;
}
catch (const Botan::Invalid_Argument &) {
// not present
return false;
}
} }
else { else {
throw PasswordManagerLockedException(); throw PasswordManagerLockedException();
@ -99,7 +265,7 @@ std::string PasswordManager::get(const std::string &id, const std::string &passw
void PasswordManager::remove(const std::string &id) void PasswordManager::remove(const std::string &id)
{ {
if (m_pskDatabase) { if (m_pskDatabase) {
m_pskDatabase->remove(id);
} }
else { else {
throw PasswordManagerLockedException(); throw PasswordManagerLockedException();
@ -107,57 +273,100 @@ void PasswordManager::remove(const std::string &id)
} }
void PasswordManager::initializeNewPskStore(std::shared_ptr<Botan::Sqlite3_Database> db) void PasswordManager::initializeNewPskStore(QSqlDatabase &db)
{ {
// Create tables // // Create tables
// - psk_masterkey_algo // // - psk_masterkey_algo
// - psk_passwd // // - psk_passwd
std::string create_statement = {
QSqlQuery create_tbl(db);
create_tbl.prepare(
"CREATE TABLE IF NOT EXISTS " + m_secretAlgoTableName + "( \n" "CREATE TABLE IF NOT EXISTS " + m_secretAlgoTableName + "( \n"
" id INTEGER PRIMARY KEY \n" " id INTEGER PRIMARY KEY, \n"
" algo TEXT \n" " algo TEXT, \n"
" i1 INTEGER \n" " i1 INTEGER, \n"
" i2 INTEGER \n" " i2 INTEGER, \n"
" i3 INTEGER \n" " i3 INTEGER, \n"
" ks INTEGER \n" " ks INTEGER, \n"
" salt TEXT \n" " salt TEXT \n"
");"; ");");
db->create_table(create_statement); if (!create_tbl.exec()) {
// auto sql_error = create_tbl.lastError();
// throw std::runtime_error("create table failed");
auto err = create_tbl.lastError();
throw SqlException(err);
}
}
// db->create_table(
QSqlQuery create_tbl(db);
create_tbl.prepare(
"CREATE TABLE IF NOT EXISTS " + m_secretHashTableName + "( \n"
" id INTEGER PRIMARY KEY, \n"
" hash TEXT \n"
");");
if (!create_tbl.exec()) {
auto err = create_tbl.lastError();
throw SqlException(err);
}
} }
bool PasswordManager::isPskStoreInitialized(std::shared_ptr<Botan::Sqlite3_Database> db) bool PasswordManager::isPskStoreInitialized(QSqlDatabase& db)
{ {
// Is the table with the secret data present and filled? // Is the table with the secret data present and filled?
auto stmt = db->new_statement("SELECT name FROM sqlite_master WHERE type='table' AND name=?1"); QSqlQuery query(db);
stmt->bind(1, m_secretAlgoTableName); query.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=:name");
bool ok = stmt->step(); query.bindValue(":name", m_secretAlgoTableName);
if (ok) { if (!query.exec()) {
auto stmt = db->new_statement("SELECT algo FROM " + m_secretAlgoTableName + " WHERE id=1"); auto err = query.lastError();
return stmt->step(); throw SqlException(err);
} }
return false; if (!query.next()) {
return false;
}
query.bindValue(":name", m_secretHashTableName);
if (!query.exec()) {
auto err = query.lastError();
throw SqlException(err);
}
if (!query.next()) {
return false;
}
QSqlQuery sel_algo("SELECT algo FROM " + m_secretAlgoTableName + " WHERE id=1", db);
if (!sel_algo.next()) {
return false;
}
QSqlQuery sel_hash("SELECT hash FROM " + m_secretHashTableName + " WHERE id=1", db);
if (!sel_hash.next()) {
return false;
}
return true;
} }
PasswordManager::KeyStrengthener PasswordManager::getKeyStrengthener(std::shared_ptr<Botan::Sqlite3_Database> db) PasswordManager::KeyStrengthener PasswordManager::getKeyStrengthener(QSqlDatabase &db)
{ {
auto stmt = db->new_statement("SELECT algo, i1, i2, i3, ks, salt FROM " + m_secretAlgoTableName + " WHERE id=1"); QSqlQuery query("SELECT algo, i1, i2, i3, ks, salt FROM " + m_secretAlgoTableName + " WHERE id=1", db);
if (stmt->step()) { if (query.next()) {
std::string algo = stmt->get_str(0); std::string algo = query.value(0).toString().toUtf8().data();
size_t i1 = boost::lexical_cast<size_t>(stmt->get_str(1)); size_t i1 = query.value(1).toUInt();
size_t i2 = boost::lexical_cast<size_t>(stmt->get_str(2)); size_t i2 = query.value(2).toUInt();
size_t i3 = boost::lexical_cast<size_t>(stmt->get_str(3)); size_t i3 = query.value(3).toUInt();
size_t ks = boost::lexical_cast<size_t>(stmt->get_str(4)); size_t ks = query.value(4).toUInt();
auto salt = query.value(5).toString().toUtf8();
auto pwh_fam = Botan::PasswordHashFamily::create(algo); auto pwh_fam = Botan::PasswordHashFamily::create(algo);
return KeyStrengthener( return KeyStrengthener(
pwh_fam->from_params(i1, i2, i3), pwh_fam->from_params(i1, i2, i3),
Botan::base64_decode(stmt->get_str(5)), Botan::base64_decode(salt.data(), static_cast<size_t>(salt.size())),
ks ks
); );
} }
else { else {
throw std::runtime_error("fail");
} }
} }

View file

@ -2,6 +2,7 @@
#define PASSWORDMANAGER_H #define PASSWORDMANAGER_H
#include "Expected.h" #include "Expected.h"
#include <QSqlDatabase>
#include <botan/secmem.h> #include <botan/secmem.h>
#include <string> #include <string>
#include <memory> #include <memory>
@ -16,8 +17,8 @@
namespace Botan { namespace Botan {
class Encrypted_PSK_Database_SQL; class Encrypted_PSK_Database;
class Sqlite3_Database; //class Sqlite3_Database;
class PasswordHash; class PasswordHash;
} }
@ -41,21 +42,31 @@ public:
Error Error
}; };
PasswordManager() = default; PasswordManager();
~PasswordManager();
void openDatabase(std::shared_ptr<Botan::Sqlite3_Database> db, std::string passphrase); /** Check if it has been initialized before.
*
* If returns false then use createDatabase to set it up
* else use openDatabase to get access.
*/
bool initialized(QSqlDatabase &db);
bool createDatabase(QSqlDatabase &db, QString passphrase);
bool openDatabase(QSqlDatabase &db, QString passphrase);
void closeDatabase(); void closeDatabase();
bool locked() const;
void set(const std::string &id, const std::string &passwd); void set(const std::string &id, const std::string &passwd);
std::string get(const std::string &id, const std::string &passwd); bool get(const std::string &id, std::string &password);
void remove(const std::string &id); void remove(const std::string &id);
private: private:
std::string m_passwordTableName = "psk_passwd"; QString m_passwordTableName = "psk_passwd";
std::string m_secretAlgoTableName = "psk_masterkey_algo"; QString m_secretAlgoTableName = "psk_masterkey_algo";
std::unique_ptr<Botan::Encrypted_PSK_Database_SQL> m_pskDatabase; QString m_secretHashTableName = "psk_masterkey_hash";
std::unique_ptr<Botan::Encrypted_PSK_Database> m_pskDatabase;
bool isPskStoreInitialized(std::shared_ptr<Botan::Sqlite3_Database> db); bool isPskStoreInitialized(QSqlDatabase& db);
void initializeNewPskStore(std::shared_ptr<Botan::Sqlite3_Database> db); void initializeNewPskStore(QSqlDatabase &db);
class KeyStrengthener { class KeyStrengthener {
public: public:
@ -74,6 +85,7 @@ private:
, m_salt (std::move(rhs.m_salt)) , m_salt (std::move(rhs.m_salt))
, m_keySize(rhs.m_keySize) , m_keySize(rhs.m_keySize)
{} {}
KeyStrengthener& operator=(KeyStrengthener &&rhs) KeyStrengthener& operator=(KeyStrengthener &&rhs)
{ {
if (&rhs != this) { if (&rhs != this) {
@ -85,7 +97,7 @@ private:
} }
Botan::secure_vector<uint8_t> derive(const std::string &passphrase); Botan::secure_vector<uint8_t> derive(const std::string &passphrase);
void saveParams(std::shared_ptr<Botan::Sqlite3_Database> db, const std::string &table_name); void saveParams(QSqlDatabase &db, const QString &table_name);
private: private:
std::unique_ptr<Botan::PasswordHash> m_hasher; std::unique_ptr<Botan::PasswordHash> m_hasher;
Botan::secure_vector<uint8_t> m_salt; Botan::secure_vector<uint8_t> m_salt;
@ -93,7 +105,7 @@ private:
}; };
/// Get PasswordHash from parameters in database /// Get PasswordHash from parameters in database
KeyStrengthener getKeyStrengthener(std::shared_ptr<Botan::Sqlite3_Database> db); KeyStrengthener getKeyStrengthener(QSqlDatabase &db);
KeyStrengthener createKeyStrengthener(); KeyStrengthener createKeyStrengthener();
}; };

View file

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

View file

@ -27,6 +27,7 @@ namespace {
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> template <typename S, typename T>
@ -56,6 +57,12 @@ namespace {
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);
} }

View file

@ -19,6 +19,11 @@ public:
return m_connections.at(idx); return m_connections.at(idx);
} }
void setConfigByIdx(size_t idx, const ConnectionConfig &cc)
{
m_connections[idx] = cc;
}
size_t createNew(); size_t createNew();
void remove(size_t idx, size_t count); void remove(size_t idx, size_t count);

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 {
@ -204,5 +204,6 @@ void ConnectionListModel::save(size_t index)
void ConnectionListModel::save(size_t index, const ConnectionConfig &cc) 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,7 +30,7 @@ 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();

View file

@ -2,7 +2,7 @@
#define CONNECTIONMANAGERWINDOW_H #define CONNECTIONMANAGERWINDOW_H
#include <QMainWindow> #include <QMainWindow>
#include <boost/optional.hpp> #include <optional>
namespace Ui { namespace Ui {
class ConnectionManagerWindow; class ConnectionManagerWindow;
@ -19,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:
@ -36,7 +36,7 @@ private:
QDataWidgetMapper *m_mapper = nullptr; QDataWidgetMapper *m_mapper = nullptr;
MasterController *m_masterController; MasterController *m_masterController;
boost::optional<size_t> prevSelection; std::optional<size_t> prevSelection;
void setupWidgetMappings(); void setupWidgetMappings();
}; };

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

@ -2,11 +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 "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)
{} {}
@ -20,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);
@ -41,8 +75,8 @@ void MasterController::openSqlWindowForConnection(size_t connection_index)
if (res.valid()) { if (res.valid()) {
auto cc = res.get(); auto cc = res.get();
m_connectionListModel->save(connection_index, cc);
if (retrieveConnectionPassword(cc)) { if (retrieveConnectionPassword(cc)) {
m_connectionListModel->save(connection_index, cc);
// TODO instead of directly openening the mainwindow // TODO instead of directly openening the mainwindow
// do async connect and only open window when we have // do async connect and only open window when we have
// working connection // working connection
@ -55,55 +89,65 @@ void MasterController::openSqlWindowForConnection(size_t connection_index)
} }
void MasterController::openBackupDlgForConnection(int connection_index) void MasterController::openBackupDlgForConnection(size_t connection_index)
{ {
auto res = m_connectionListModel->get(connection_index); auto res = m_connectionListModel->get(connection_index);
if (res.valid()) { if (res.valid()) {
auto cc = res.get(); auto cc = res.get();
retrieveConnectionPassword(cc); if (retrieveConnectionPassword(cc)) {
m_connectionListModel->save(connection_index, 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);
w->setConfig(cc); w->show();
w->show(); }
} }
} }
void MasterController::openServerWindowForConnection(int connection_index) void MasterController::openServerWindowForConnection(size_t connection_index)
{ {
auto res = m_connectionListModel->get(connection_index); auto res = m_connectionListModel->get(connection_index);
if (res.valid()) { if (res.valid()) {
auto cc = res.get(); auto cc = res.get();
retrieveConnectionPassword(cc); if (retrieveConnectionPassword(cc)) {
m_connectionListModel->save(connection_index, 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);
w->setConfig(cc); w->show();
w->show(); }
} }
} }
bool MasterController::retrieveConnectionPassword(ConnectionConfig &cc) bool MasterController::retrieveConnectionPassword(ConnectionConfig &cc)
{ {
// Look at config auto pw_state = cc.passwordState();
// - is password required, how do we know? if (pw_state == PasswordState::NotNeeded) {
// - IF is password stored in pskdb return true;
// - ask pskdb for password }
// - ELSE else if (pw_state == PasswordState::SavedPasswordManager) {
// - ask user for password 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); QString str = ConnectionListModel::makeLongDescription(cc);
auto dlg = std::make_unique<PasswordPromptDialog>(nullptr); auto dlg = std::make_unique<PasswordPromptDialog>(PasswordPromptDialog::SaveOption, nullptr);
dlg->setConnectionDescription(str); dlg->setDescription(QString(tr("Please provide password for connection %1")).arg(str));
int exec_result = dlg->exec(); int exec_result = dlg->exec();
if (exec_result == QDialog::Accepted) { if (exec_result == QDialog::Accepted) {
cc.setPassword(dlg->password().toUtf8().data()); std::string password = dlg->password().toUtf8().data();
// - IF user checked remember password cc.setPassword(password);
// - ask pskdb to store password if (dlg->saveChecked()) {
storePasswordInPskdb(getPskId(cc), password);
cc.setPasswordState(PasswordState::SavedPasswordManager);
}
return true; return true;
} }
return false; return false;
@ -112,25 +156,71 @@ bool MasterController::retrieveConnectionPassword(ConnectionConfig &cc)
bool MasterController::getPasswordFromPskdb(const std::string &password_id, std::string &password) bool MasterController::getPasswordFromPskdb(const std::string &password_id, std::string &password)
{ {
// func: getPasswordFromPskdb if (!UnlockPasswordManagerIfNeeded())
// IF pskdb locked return false;
// prompt user for pskdb passphrase
// unlock pskdb return m_passwordManager->get(password_id, password);
// get pwd
return false;
} }
bool MasterController::storePasswordInPskdb(const std::string &password_id, const std::string password) bool MasterController::storePasswordInPskdb(const std::string &password_id, const std::string password)
{ {
// func: storePasswordInPskdb if (!UnlockPasswordManagerIfNeeded())
// IF pskdb not setup return false;
// notify user and ask for passphrase
// init pskdb m_passwordManager->set(password_id, password);
// ELSE return true;
// IF pskdb locked }
// ask for passphrase
// unlock bool MasterController::UnlockPasswordManagerIfNeeded()
// store pwd {
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; 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,14 +2,21 @@
#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.
*/ */
@ -30,8 +37,10 @@ public:
void showConnectionManager(); void showConnectionManager();
void openSqlWindowForConnection(size_t 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,6 +50,12 @@ 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) /** Retrieves the connection password from the user (directly or through the psk db)
* *
@ -50,6 +65,10 @@ private:
bool getPasswordFromPskdb(const std::string &password_id, std::string &password); bool getPasswordFromPskdb(const std::string &password_id, std::string &password);
bool storePasswordInPskdb(const std::string &password_id, const 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

@ -1,45 +0,0 @@
#include "PassPhraseForm.h"
#include "ui_PassPhraseForm.h"
PassPhraseForm::PassPhraseForm(QWidget *parent) :
QWidget(parent),
ui(new Ui::PassPhraseForm)
{
ui->setupUi(this);
}
PassPhraseForm::~PassPhraseForm()
{
delete ui;
}
/*
Password strength calculation:
seperate characters in couple of groups
For the use of characters from each group a certain value is added to the base value
which is meant to signify the size of the set of characters the password is based on.
Some analysis of relative positions might be required! Predictable placement of special charachters and uppercase/lowercase or numbers
should be penalized.
These calculations should result in a search space size per character
the base to the power of the length of the password gives the resulting strength
from this result we take the 10 log to get the magnitude of the value.
a-z 1:3 2:7 3:13 4:26
A-Z 1:3 2:7 3:13 4:26
0-9 1:4 2:10
`~!@#$%^&*()_-=+[{]};:'",<.>/?\| 1:4 2:8 3:16 4:32
space +1
Straf punten
alleen speciaal karakter aan eind van string -8
alleen hoofdletter aan begin van wachtwoord -6
la-~ZDv4E-O*y]C
bYGWlDyeKKbcZBjoWX
*/

View file

@ -1,22 +0,0 @@
#ifndef PASSPHRASEFORM_H
#define PASSPHRASEFORM_H
#include <QWidget>
namespace Ui {
class PassPhraseForm;
}
class PassPhraseForm : public QWidget
{
Q_OBJECT
public:
explicit PassPhraseForm(QWidget *parent = nullptr);
~PassPhraseForm();
private:
Ui::PassPhraseForm *ui;
};
#endif // PASSPHRASEFORM_H

View file

@ -1,75 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PassPhraseForm</class>
<widget class="QWidget" name="PassPhraseForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>397</width>
<height>228</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="horizontalSpacing">
<number>20</number>
</property>
<property name="verticalSpacing">
<number>20</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Enter passphrase:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit">
<property name="maxLength">
<number>32767</number>
</property>
<property name="echoMode">
<enum>QLineEdit::NoEcho</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Repeat passphrase:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_2">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>67</number>
</property>
<property name="format">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -5,66 +5,96 @@
#include <QLabel> #include <QLabel>
#include <QLayout> #include <QLayout>
#include <QLineEdit> #include <QLineEdit>
#include <QPushButton>
PasswordPromptDialog::PasswordPromptDialog(QWidget *parent) Q_DECLARE_OPERATORS_FOR_FLAGS(PasswordPromptDialog::Flags)
PasswordPromptDialog::PasswordPromptDialog(Flags flags, QWidget *parent)
: QDialog(parent) : QDialog(parent)
, m_Flags(flags)
{ {
m_connectionLabel = new QLabel(this); m_connectionLabel = new QLabel(this);
auto dialog_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); m_DialogButtons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
m_passwordLabel = new QLabel(this);
m_passwordInput = new QLineEdit(this);
m_passwordInput->setEchoMode(QLineEdit::Password);
m_saveCheck = new QCheckBox(this);
const size_t inputFieldCount = flags.testFlag(ConfirmPassword) ? 2 : 1;
auto mainLayout = new QGridLayout; auto mainLayout = new QGridLayout;
int row = 0; int row = 0;
mainLayout->addWidget(m_connectionLabel, row, 0, 1, 2); mainLayout->addWidget(m_connectionLabel, row, 0, 1, 2);
++row; ++row;
mainLayout->addWidget(m_passwordLabel, row, 0); for (size_t idx = 0; idx < inputFieldCount; ++idx) {
mainLayout->addWidget(m_passwordInput, row, 1); auto lbl = new QLabel(this);
++row; auto input = new QLineEdit(this);
mainLayout->addWidget(m_saveCheck, row, 1); input->setEchoMode(QLineEdit::Password);
++row; mainLayout->addWidget(lbl, row, 0);
mainLayout->addWidget(dialog_buttons, row, 0, 1 ,2); 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); setLayout(mainLayout);
m_passwordInput->setFocus(); m_passwordInput[0]->setFocus();
retranslateUi(); retranslateUi();
// QMetaObject::connectSlotsByName(BackupDialog); // QMetaObject::connectSlotsByName(BackupDialog);
connect(dialog_buttons, &QDialogButtonBox::accepted, this, &PasswordPromptDialog::accept); connect(m_DialogButtons, &QDialogButtonBox::accepted, this, &PasswordPromptDialog::accept);
connect(dialog_buttons, &QDialogButtonBox::rejected, this, &PasswordPromptDialog::reject); 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() void PasswordPromptDialog::retranslateUi()
{ {
const char * context = "PasswordPromptDialog"; const char * context = "PasswordPromptDialog";
setWindowTitle(QApplication::translate(context, "Connection password", nullptr)); setWindowTitle(QApplication::translate(context, "Connection password", nullptr));
m_passwordLabel->setText(QApplication::translate(context, "Password", nullptr)); m_passwordLabel[0]->setText(QApplication::translate(context, "Password", nullptr));
m_passwordInput->setPlaceholderText(QApplication::translate(context, "Enter password", nullptr)); m_passwordInput[0]->setPlaceholderText(QApplication::translate(context, "Enter password", nullptr));
m_saveCheck->setText(QApplication::translate(context, "Save password", nullptr)); if (m_passwordLabel[1])
m_passwordLabel[1]->setText(QApplication::translate(context, "Confirm password", nullptr));
if (m_passwordInput[1])
m_passwordInput[1]->setPlaceholderText(QApplication::translate(context, "Reenter same password for confirmation", nullptr));
if (m_saveCheck)
m_saveCheck->setText(QApplication::translate(context, "Save password", nullptr));
} }
void PasswordPromptDialog::setConnectionDescription(const QString &description) void PasswordPromptDialog::updateOkEnabled()
{ {
m_connectionLabel->setText(QString(tr("Please provide password for connection %1")).arg(description)); bool enabled = true;
if (m_passwordInput[1])
enabled = m_passwordInput[0]->text() == m_passwordInput[1]->text();
auto btn = m_DialogButtons->button(QDialogButtonBox::Ok);
btn->setEnabled(enabled);
}
void PasswordPromptDialog::passwordChanged(const QString &)
{
updateOkEnabled();
}
void PasswordPromptDialog::setDescription(const QString &description)
{
m_connectionLabel->setText(QString("%1").arg(description));
} }
QString PasswordPromptDialog::password() const QString PasswordPromptDialog::password() const
{ {
return m_passwordInput->text(); return m_passwordInput[0]->text();
} }
//void PasswordPromptDialog::accept() bool PasswordPromptDialog::saveChecked() const
//{ {
if (m_saveCheck)
return m_saveCheck->checkState() == Qt::Checked;
//} return false;
}
//void PasswordPromptDialog::reject()
//{
//}

View file

@ -4,6 +4,7 @@
#include <QDialog> #include <QDialog>
class QCheckBox; class QCheckBox;
class QDialogButtonBox;
class QLabel; class QLabel;
class QLineEdit; class QLineEdit;
@ -11,22 +12,33 @@ class PasswordPromptDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit PasswordPromptDialog(QWidget *parent = nullptr); enum Flag {
ConfirmPassword = 1,
SaveOption = 2
};
void setConnectionDescription(const QString &description); Q_DECLARE_FLAGS(Flags, Flag)
//Q_FLAG(Flags)
explicit PasswordPromptDialog(Flags flags, QWidget *parent = nullptr);
void setDescription(const QString &description);
QString password() const; QString password() const;
bool saveChecked() const;
private: private:
Flags m_Flags;
QLabel *m_connectionLabel = nullptr; QLabel *m_connectionLabel = nullptr;
QLabel *m_passwordLabel = nullptr; QLabel *m_passwordLabel[2] = { nullptr, nullptr };
QLineEdit *m_passwordInput = nullptr; QLineEdit *m_passwordInput[2] = { nullptr, nullptr };
QCheckBox *m_saveCheck = nullptr; QCheckBox *m_saveCheck = nullptr;
QDialogButtonBox *m_DialogButtons = nullptr;
void retranslateUi(); void retranslateUi();
void updateOkEnabled();
private slots: private slots:
// void accept(); void passwordChanged(const QString &text);
// void reject();
}; };
#endif // PASSWORDPROMPTDIALOG_H #endif // PASSWORDPROMPTDIALOG_H

View file

@ -84,7 +84,6 @@ PropertyProxyModel.cpp \
SqlCodePreview.cpp \ SqlCodePreview.cpp \
CustomFilterSortModel.cpp \ CustomFilterSortModel.cpp \
PropertiesPage.cpp \ PropertiesPage.cpp \
PassPhraseForm.cpp \
PasswordPromptDialog.cpp PasswordPromptDialog.cpp
HEADERS += \ HEADERS += \
@ -140,7 +139,6 @@ CustomDataRole.h \
SqlCodePreview.h \ SqlCodePreview.h \
CustomFilterSortModel.h \ CustomFilterSortModel.h \
PropertiesPage.h \ PropertiesPage.h \
PassPhraseForm.h \
PasswordPromptDialog.h PasswordPromptDialog.h
FORMS += mainwindow.ui \ FORMS += mainwindow.ui \
@ -155,8 +153,7 @@ FORMS += mainwindow.ui \
NamespaceFilterWidget.ui \ NamespaceFilterWidget.ui \
ApplicationWindow.ui \ ApplicationWindow.ui \
CrudTab.ui \ CrudTab.ui \
CodeGenerator.ui \ CodeGenerator.ui
PassPhraseForm.ui
RESOURCES += \ RESOURCES += \
resources.qrc resources.qrc

View file

@ -258,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

@ -15,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;
@ -70,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;
@ -84,7 +87,7 @@ private:
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;
@ -94,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

@ -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

@ -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;
} }

View file

@ -1,6 +1,6 @@
#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;