diff --git a/pglab/ConnectionConfigurationWidget.cpp b/pglab/ConnectionConfigurationWidget.cpp new file mode 100644 index 0000000..81fa6b6 --- /dev/null +++ b/pglab/ConnectionConfigurationWidget.cpp @@ -0,0 +1,181 @@ +#include "ConnectionConfigurationWidget.h" +#include "SslModeModel.h" +#include "ConnectionConfig.h" +#include "ConnectionController.h" +#include "ConnectionListModel.h" +#include "util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SET_OBJECT_NAME(var) var->setObjectName(#var) + +void ConnectionConfigurationWidget::editExistingInWindow(ConnectionController *ctrl, const ConnectionConfig &cfg) +{ + auto w = new ConnectionConfigurationWidget; + w->setData(cfg); + + + auto btn_hbox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); + //auto btn_test = btn_hbox->addButton(tr("Test"), QDialogButtonBox::ActionRole); + + auto vbox = new QVBoxLayout; + vbox->addWidget(w); + vbox->addWidget(btn_hbox); + + auto win = new QDialog; + win->setWindowTitle(tr("Edit connection configuration")); + win->setLayout(vbox); + + win->connect(btn_hbox, &QDialogButtonBox::accepted, [ctrl, w, win] () { + auto cc = w->data(); + ctrl->getConnectionListModel()->save(cc); + win->accept(); + }); + win->connect(btn_hbox, &QDialogButtonBox::rejected, [win] () { win->reject(); }); + + win->connect(win, &QDialog::finished, [win] (int) + { + delete win; + }); + + win->show(); +} + +ConnectionConfigurationWidget::ConnectionConfigurationWidget(QWidget *parent) + : QWidget(parent) +{ + lblName = new QLabel; + SET_OBJECT_NAME(lblName); + edtName = new QLineEdit ; + SET_OBJECT_NAME(edtName); + lblName->setBuddy(edtName); + + lblHost = new QLabel; + SET_OBJECT_NAME(lblHost); + edtHost = new QLineEdit; + SET_OBJECT_NAME(edtHost); + lblHost->setBuddy(edtHost); + + lblPort = new QLabel; + SET_OBJECT_NAME(lblPort); + spinPort = new QSpinBox; + SET_OBJECT_NAME(spinPort); + spinPort->setRange(1, std::numeric_limits::max()); + lblPort->setBuddy(spinPort); + + lblUser = new QLabel; + SET_OBJECT_NAME(lblUser); + edtUser = new QLineEdit; + SET_OBJECT_NAME(edtUser); + lblUser->setBuddy(edtUser); + + lblDbName = new QLabel; + SET_OBJECT_NAME(lblDbName); + edtDbname = new QLineEdit; + SET_OBJECT_NAME(edtDbname); + lblDbName->setBuddy(edtDbname); + + lblSsl = new QLabel; + SET_OBJECT_NAME(lblSsl); + cmbbxSsl = new QComboBox; + SET_OBJECT_NAME(cmbbxSsl); + cmbbxSsl->setModelColumn(0); + auto ssl_model = new SslModeModel(this); + cmbbxSsl->setModel(ssl_model); + lblSsl->setBuddy(cmbbxSsl); + + lblCert = new QLabel; + SET_OBJECT_NAME(lblCert); + edtCert = new QLineEdit; + SET_OBJECT_NAME(edtCert); + lblCert->setBuddy(edtCert); + + lblKey = new QLabel; + SET_OBJECT_NAME(lblKey); + edtKey = new QLineEdit; + SET_OBJECT_NAME(edtKey); + lblKey->setBuddy(edtKey); + + lblRootCert = new QLabel; + SET_OBJECT_NAME(lblRootCert); + edtRootCert = new QLineEdit; + SET_OBJECT_NAME(edtRootCert); + lblRootCert->setBuddy(edtRootCert); + + lblCrl = new QLabel; + SET_OBJECT_NAME(lblCrl); + edtCrl = new QLineEdit; + SET_OBJECT_NAME(edtCrl); + lblCrl->setBuddy(edtCrl); + + formLayout = new QFormLayout; + + setLayout(formLayout); + formLayout->addRow(lblName, edtName); + formLayout->addRow(lblHost, edtHost); + formLayout->addRow(lblPort, spinPort); + formLayout->addRow(lblUser, edtUser); + formLayout->addRow(lblDbName, edtDbname); + formLayout->addRow(lblSsl, cmbbxSsl); + formLayout->addRow(lblCert, edtCert); + formLayout->addRow(lblKey, edtKey); + formLayout->addRow(lblRootCert, edtRootCert); + formLayout->addRow(lblCrl, edtCrl); + + retranslateUi(); +} + +void ConnectionConfigurationWidget::retranslateUi() +{ + lblName->setText(QApplication::translate("ConnectionConfigurationWidget", "&Name", nullptr)); + lblHost->setText(QApplication::translate("ConnectionConfigurationWidget", "&Host", nullptr)); + lblPort->setText(QApplication::translate("ConnectionConfigurationWidget", "&Port", nullptr)); + lblUser->setText(QApplication::translate("ConnectionConfigurationWidget", "&Username", nullptr)); + lblDbName->setText(QApplication::translate("ConnectionConfigurationWidget", "&Database", nullptr)); + lblSsl->setText(QApplication::translate("ConnectionConfigurationWidget", "&SSL mode", nullptr)); + lblCert->setText(QApplication::translate("ConnectionConfigurationWidget", "&Certificate", nullptr)); + lblKey->setText(QApplication::translate("ConnectionConfigurationWidget", "&Key", nullptr)); + lblRootCert->setText(QApplication::translate("ConnectionConfigurationWidget", "&Root cert.", nullptr)); + lblCrl->setText(QApplication::translate("ConnectionConfigurationWidget", "Revocation &list", nullptr)); +} + +void ConnectionConfigurationWidget::setData(const ConnectionConfig &cfg) +{ + m_uuid = cfg.uuid(); + edtName->setText(stdStrToQ(cfg.name())); + edtHost->setText(stdStrToQ(cfg.host())); + spinPort->setValue(cfg.port()); + edtUser->setText(stdStrToQ(cfg.user())); + edtDbname->setText(stdStrToQ(cfg.dbname())); + cmbbxSsl->setCurrentIndex(static_cast(cfg.sslMode())); + edtCert->setText(stdStrToQ(cfg.sslCert())); + edtKey->setText(stdStrToQ(cfg.sslKey())); + edtRootCert->setText(stdStrToQ(cfg.sslRootCert())); + edtCrl->setText(stdStrToQ(cfg.sslCrl())); +} + +ConnectionConfig ConnectionConfigurationWidget::data() const +{ + ConnectionConfig cfg; + cfg.setUuid(m_uuid); + cfg.setName(qStrToStd(edtName->text())); + cfg.setHost(qStrToStd(edtHost->text())); + cfg.setPort(static_cast(spinPort->value())); + cfg.setUser(qStrToStd(edtUser->text())); + cfg.setDbname(qStrToStd(edtDbname->text())); + cfg.setSslMode(static_cast(cmbbxSsl->currentIndex())); + cfg.setSslCert(qStrToStd(edtCert->text())); + cfg.setSslKey(qStrToStd(edtKey->text())); + cfg.setSslRootCert(qStrToStd(edtRootCert->text())); + cfg.setSslCrl(qStrToStd(edtCrl->text())); + return cfg; +} + diff --git a/pglab/ConnectionConfigurationWidget.h b/pglab/ConnectionConfigurationWidget.h new file mode 100644 index 0000000..4d4eadf --- /dev/null +++ b/pglab/ConnectionConfigurationWidget.h @@ -0,0 +1,60 @@ +#ifndef CONNECTIONCONFIGURATIONWIDGET_H +#define CONNECTIONCONFIGURATIONWIDGET_H + +#include +#include + +class ConnectionController; +class ConnectionConfig; +class QFormLayout; +class QLabel; +class QLineEdit; +class QSpinBox; +class QComboBox; +class QDataWidgetMapper; + + +class ConnectionConfigurationWidget : public QWidget +{ + Q_OBJECT +public: + static void editExistingInWindow(ConnectionController *ctrl, const ConnectionConfig &cfg); + + explicit ConnectionConfigurationWidget(QWidget *parent = nullptr); + + void retranslateUi(); + void setData(const ConnectionConfig &cfg); + ConnectionConfig data() const; +signals: + +private: + QUuid m_uuid; + + QFormLayout *formLayout; + QLabel *lblName; + QLineEdit *edtName; + QLabel *lblHost; + QLineEdit *edtHost; + QLabel *lblPort; + QSpinBox *spinPort; + QLabel *lblUser; + QLineEdit *edtUser; + QLabel *lblDbName; + QLineEdit *edtDbname; + QLabel *lblSsl; + QComboBox *cmbbxSsl; + QLabel *lblCert; + QLineEdit *edtCert; + QLabel *lblKey; + QLineEdit *edtKey; + QLabel *lblRootCert; + QLineEdit *edtRootCert; + QLabel *lblCrl; + QLineEdit *edtCrl; + +public slots: +}; + + + +#endif // CONNECTIONCONFIGURATIONWIDGET_H diff --git a/pglab/ConnectionController.cpp b/pglab/ConnectionController.cpp new file mode 100644 index 0000000..013e3cc --- /dev/null +++ b/pglab/ConnectionController.cpp @@ -0,0 +1,216 @@ +#include "ConnectionController.h" +#include "MasterController.h" +#include "ConnectionManagerWindow.h" +#include "ConnectionListModel.h" +#include "PasswordManager.h" +#include "DatabaseWindow.h" +#include "ServerWindow.h" +#include "BackupDialog.h" +#include "PasswordPromptDialog.h" +#include "ConnectionConfigurationWidget.h" + + +ConnectionController::ConnectionController(MasterController *parent) + : QObject(parent) + , m_masterController(parent) +{} + +ConnectionController::~ConnectionController() +{ + delete m_connectionManagerWindow; + delete m_connectionListModel; +} + +void ConnectionController::init() +{ + //std::string dbfilename = QDir::toNativeSeparators(GetUserConfigDatabaseName()).toUtf8().data(); + //m_userConfigDatabase = std::make_shared(dbfilename); + + + m_passwordManager = std::make_shared(); + + m_connectionListModel = new ConnectionListModel(this); + m_connectionListModel->load(); + + m_connectionManagerWindow = new ConnectionManagerWindow(m_masterController, nullptr); + m_connectionManagerWindow->show(); + +} + +void ConnectionController::showConnectionManager() +{ + m_connectionManagerWindow->show(); +} + +void ConnectionController::openSqlWindowForConnection(int connection_index) +{ + + auto res = m_connectionListModel->get(connection_index); + if (res.valid()) { + auto cc = res.get(); + + if (retrieveConnectionPassword(cc)) { + m_connectionListModel->save(cc); + // TODO instead of directly openening the mainwindow + // do async connect and only open window when we have + // working connection + auto w = new DatabaseWindow(m_masterController, nullptr); + w->setAttribute( Qt::WA_DeleteOnClose ); + w->setConfig(cc); + w->showMaximized(); + } + } + +} + +void ConnectionController::openBackupDlgForConnection(int connection_index) +{ + auto res = m_connectionListModel->get(connection_index); + if (res.valid()) { + auto cc = res.get(); + if (retrieveConnectionPassword(cc)) { + m_connectionListModel->save(cc); + auto w = new BackupDialog(nullptr); //new ServerWindow(this, nullptr); + w->setAttribute( Qt::WA_DeleteOnClose ); + w->setConfig(cc); + w->show(); + } + } +} + +void ConnectionController::createConnection() +{ + ConnectionConfig cc; + cc.setUuid(QUuid::createUuid()); + ConnectionConfigurationWidget::editExistingInWindow(this, cc); +} + +void ConnectionController::editConnection(int connection_index) +{ + auto res = m_connectionListModel->get(connection_index); + if (res.valid()) { + auto cc = res.get(); + ConnectionConfigurationWidget::editExistingInWindow(this, cc); + } +} + +void ConnectionController::openServerWindowForConnection(int connection_index) +{ + auto res = m_connectionListModel->get(connection_index); + if (res.valid()) { + auto cc = res.get(); + if (retrieveConnectionPassword(cc)) { + m_connectionListModel->save(cc); + auto w = new ServerWindow(m_masterController, nullptr); + w->setAttribute( Qt::WA_DeleteOnClose ); + w->setConfig(cc); + w->show(); + } + } +} + + +bool ConnectionController::retrieveConnectionPassword(ConnectionConfig &cc) +{ + auto pw_state = cc.passwordState(); + if (pw_state == PasswordState::NotNeeded) { + return true; + } + else if (pw_state == PasswordState::SavedPasswordManager) { + std::string pw; + bool result = getPasswordFromPskdb(getPskId(cc), pw); + if (result) { + cc.setPassword(pw); + return true; + } + } + // Geen else hier want als voorgaande blok niet geretourneerd heeft moeten we wachtwoord + // ook aan de gebruiker vragen zoals hier gebeurd. + QString str = ConnectionListModel::makeLongDescription(cc); + auto dlg = std::make_unique(PasswordPromptDialog::SaveOption, nullptr); + dlg->setCaption(tr("Connection password prompt")); + 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 ConnectionController::getPasswordFromPskdb(const std::string &password_id, std::string &password) +{ + if (!UnlockPasswordManagerIfNeeded()) + return false; + + return m_passwordManager->get(password_id, password); +} + + +bool ConnectionController::storePasswordInPskdb(const std::string &password_id, const std::string password) +{ + if (!UnlockPasswordManagerIfNeeded()) + return false; + + m_passwordManager->set(password_id, password); + return true; +} + +bool ConnectionController::UnlockPasswordManagerIfNeeded() +{ + auto&& user_cfg_db = m_masterController->userConfigDatabase(); + if (m_passwordManager->initialized(user_cfg_db)) { + if (!m_passwordManager->locked()) + return true; + + while (true) { + // ask user for passphrase + auto dlg = std::make_unique(nullptr, nullptr); + dlg->setCaption(tr("Unlock password manager")); + dlg->setDescription(tr("Enter password for password manager")); + int exec_result = dlg->exec(); + bool ok = (exec_result == QDialog::Accepted); + + if (!ok) { + // leave this retry loop + break; + } + // user gave OK, if succeeds return true otherwise loop a prompt for password again. + if (m_passwordManager->openDatabase(user_cfg_db, dlg->password())) + return true; + } + } + else { + // Ask user for passphrase + confirmation, clearly instruct this is first setup + // create + auto dlg = std::make_unique(PasswordPromptDialog::ConfirmPassword, nullptr); + dlg->setCaption(tr("Password manager setup")); + dlg->setDescription(tr("Enter a strong password for password manager initialization. A strong key will be " + "derived from your password and it will be impossible to recover anything from the " + "password manager without the password you enter here.")); + int exec_result = dlg->exec(); + if (exec_result == QDialog::Accepted) { + QString passphrase = dlg->password(); + if (m_passwordManager->createDatabase(user_cfg_db, passphrase)) + return true; + } + } + return false; +} + +std::string ConnectionController::getPskId(const ConnectionConfig &cc) +{ + std::string id = "dbpw/"; + id += cc.uuid().toString().toUtf8().data(); + return id; +} + + + diff --git a/pglab/ConnectionController.h b/pglab/ConnectionController.h new file mode 100644 index 0000000..a5c8f11 --- /dev/null +++ b/pglab/ConnectionController.h @@ -0,0 +1,63 @@ +#ifndef CONNECTIONCONTROLLER_H +#define CONNECTIONCONTROLLER_H + +#include + +class MasterController; +class ConnectionConfig; +class ConnectionList; +class ConnectionListModel; +class ConnectionManagerWindow; +class PasswordManager; + +class ConnectionController : public QObject { + Q_OBJECT +public: + explicit ConnectionController(MasterController *parent = nullptr); + ~ConnectionController(); + + void init(); + + ConnectionListModel *getConnectionListModel() + { + return m_connectionListModel; + } + + void showConnectionManager(); + void openSqlWindowForConnection(int connection_index); + void openServerWindowForConnection(int connection_index); + void openBackupDlgForConnection(int connection_index); + + /// Starts the form for creating a new conncetion. + /// This function returns immidiatly! + void createConnection(); + /// Starts the form for editing a conncetion. + /// This function returns immidiatly! + void editConnection(int connection_index); +private: + MasterController *m_masterController; + ConnectionList *m_connectionList = nullptr; + ConnectionListModel *m_connectionListModel = nullptr; + ConnectionManagerWindow *m_connectionManagerWindow = nullptr; + + /** Using long lived object so it can remember its master password for sometime + * if the user wishes it. + */ + std::shared_ptr m_passwordManager; + + /** Retrieves the connection password from the user (directly or through the psk db) + * + */ + bool retrieveConnectionPassword(ConnectionConfig &cc); + + bool getPasswordFromPskdb(const std::string &password_id, std::string &password); + + bool storePasswordInPskdb(const std::string &password_id, const std::string password); + + bool UnlockPasswordManagerIfNeeded(); + + static std::string getPskId(const ConnectionConfig &cc); +}; + + +#endif // CONNECTIONCONTROLLER_H diff --git a/pglab/ConnectionList.cpp b/pglab/ConnectionList.cpp deleted file mode 100644 index a6c8a01..0000000 --- a/pglab/ConnectionList.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "ConnectionList.h" -#include "ScopeGuard.h" -#include "util.h" -#include "PasswordManager.h" -#include -#include -#include - - -namespace { - - /** Saves a connection configuration. - - Before calling this you may want to call beginGroup. - */ - void SaveConnectionConfig(QSettings &settings, const ConnectionConfig &cc) - { - settings.setValue("name", stdStrToQ(cc.name())); - settings.setValue("host", stdStrToQ(cc.host())); - settings.setValue("hostaddr", stdStrToQ(cc.hostAddr())); - settings.setValue("port", cc.port()); - settings.setValue("user", stdStrToQ(cc.user())); - //settings.setValue("password", stdStrToQ(cc.password())); - settings.setValue("dbname", stdStrToQ(cc.dbname())); - settings.setValue("sslmode", static_cast(cc.sslMode())); - settings.setValue("sslcert", stdStrToQ(cc.sslCert())); - settings.setValue("sslkey", stdStrToQ(cc.sslKey())); - settings.setValue("sslrootcert", stdStrToQ(cc.sslRootCert())); - settings.setValue("sslcrl", stdStrToQ(cc.sslCrl())); - settings.setValue("passwordState", static_cast(cc.passwordState())); - } - - template - bool in_range(T value) - { - return value >= std::numeric_limits::min() && value <= std::numeric_limits::max(); - } - - void LoadConnectionConfig(QSettings &settings, ConnectionConfig &cc) - { - cc.setName(qvarToStdStr(settings.value("name"))); - cc.setHost(qvarToStdStr(settings.value("host"))); - cc.setHostAddr(qvarToStdStr(settings.value("hostaddr"))); - int p = settings.value("port", 5432).toInt(); - if (!in_range(p)) { - p = 0; // let the user re-enter a valid value - } - - cc.setPort(static_cast(p)); - cc.setUser(qvarToStdStr(settings.value("user"))); - - //cc.setPassword(qvarToStdStr(settings.value("password"))); - - cc.setDbname(qvarToStdStr(settings.value("dbname"))); - cc.setSslMode(static_cast(settings.value("sslmode").toInt())); - cc.setSslCert(qvarToStdStr(settings.value("sslcert"))); - cc.setSslKey(qvarToStdStr(settings.value("sslkey"))); - cc.setSslRootCert(qvarToStdStr(settings.value("sslrootcert"))); - cc.setSslCrl(qvarToStdStr(settings.value("sslcrl"))); - - PasswordState pwstate; - QVariant v = settings.value("passwordState"); - if (v.isNull()) pwstate = PasswordState::NotStored; - else pwstate = static_cast(v.toInt()); - cc.setPasswordState(pwstate); - } - - - -} // end of unnamed namespace - -/// \todo should return an expected as creation of the folder can fail -QString ConnectionList::iniFileName() -{ - QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - QDir dir(path); - if (!dir.exists()) { - dir.mkpath("."); - } - path += "/connections.ini"; - return path; -} - - -ConnectionList::ConnectionList() = default; - -size_t ConnectionList::createNew() -{ - ConnectionConfig cc; - cc.setUuid(QUuid::createUuid()); - m_connections.push_back(cc); - return m_connections.size()-1; -} - -void ConnectionList::remove(size_t idx, size_t count) -{ - auto f = m_connections.begin() + static_cast(idx); - auto l = f + static_cast(count); - deleteFromIni(f, l); - m_connections.erase(f, l); - - // remove from password save -} - -void ConnectionList::deleteFromIni(const t_Connections::iterator &begin, const t_Connections::iterator &end) -{ - QString file_name = iniFileName(); - QSettings settings(file_name, QSettings::IniFormat); - for (auto i = begin; i != end; ++i) { - settings.remove(i->uuid().toString()); - } -} - -void ConnectionList::load() -{ - QString file_name = iniFileName(); - QSettings settings(file_name, QSettings::IniFormat); - auto groups = settings.childGroups(); - for (auto&& grp : groups) { - if (grp == "c_IniGroupSecurity") { - // Read security settings - - } else { - QUuid uuid(grp); - if ( ! uuid.isNull() ) { - settings.beginGroup(grp); - SCOPE_EXIT { settings.endGroup(); }; - - ConnectionConfig cc; - cc.setUuid(uuid); - LoadConnectionConfig(settings, cc); - m_connections.push_back(cc); - } - } - } -} - -void ConnectionList::save() -{ - QString file_name = iniFileName(); - QSettings settings(file_name, QSettings::IniFormat); - for (auto& e : m_connections) { - settings.beginGroup(e.uuid().toString()); - SCOPE_EXIT { settings.endGroup(); }; - - SaveConnectionConfig(settings, e); - e.clean(); - } - settings.sync(); -} - -void ConnectionList::save(size_t index) -{ - auto& e = m_connections.at(index); - if (e.dirty()) { - QString file_name = iniFileName(); - QSettings settings(file_name, QSettings::IniFormat); - settings.beginGroup(e.uuid().toString()); - SaveConnectionConfig(settings, e); - e.clean(); - settings.sync(); - } -} diff --git a/pglab/ConnectionList.h b/pglab/ConnectionList.h deleted file mode 100644 index 2cd8a9c..0000000 --- a/pglab/ConnectionList.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef CONNECTIONLIST_H -#define CONNECTIONLIST_H - -#include "ConnectionConfig.h" - -#include -#include -#include -#include "Expected.h" - -class ConnectionList { -public: - - ConnectionList(); - size_t size() const { return m_connections.size(); } - - ConnectionConfig& getConfigByIdx(size_t idx) - { - return m_connections.at(idx); - } - - void setConfigByIdx(size_t idx, const ConnectionConfig &cc) - { - m_connections[idx] = cc; - } - - size_t createNew(); - - void remove(size_t idx, size_t count); - - void load(); - void save(); - void save(size_t index); - -private: -// class LijstElem { -// public: -// QUuid m_uuid; ///< Unique identifier, used as a key for storing password in psk db. -// ConnectionConfig m_config; - -// LijstElem(const QUuid id, const ConnectionConfig &cfg) -// : m_uuid(id), m_config(cfg) -// {} -// }; - - using t_Connections = std::vector; - t_Connections m_connections; - - void deleteFromIni(const t_Connections::iterator &begin, const t_Connections::iterator &end); - - static QString iniFileName(); -}; - -#endif // CONNECTIONLIST_H diff --git a/pglab/ConnectionListModel.cpp b/pglab/ConnectionListModel.cpp index 7297db7..29914c0 100644 --- a/pglab/ConnectionListModel.cpp +++ b/pglab/ConnectionListModel.cpp @@ -1,14 +1,78 @@ #include "ConnectionListModel.h" -#include "ConnectionList.h" #include "ScopeGuard.h" #include "util.h" #include +#include +#include +#include -ConnectionListModel::ConnectionListModel(ConnectionList *conns, QObject *parent) +namespace { + + /** Saves a connection configuration. + + Before calling this you may want to call beginGroup. + */ + void SaveConnectionConfig(QSettings &settings, const ConnectionConfig &cc) + { + settings.setValue("name", stdStrToQ(cc.name())); + settings.setValue("host", stdStrToQ(cc.host())); + settings.setValue("hostaddr", stdStrToQ(cc.hostAddr())); + settings.setValue("port", cc.port()); + settings.setValue("user", stdStrToQ(cc.user())); + //settings.setValue("password", stdStrToQ(cc.password())); + settings.setValue("dbname", stdStrToQ(cc.dbname())); + settings.setValue("sslmode", static_cast(cc.sslMode())); + settings.setValue("sslcert", stdStrToQ(cc.sslCert())); + settings.setValue("sslkey", stdStrToQ(cc.sslKey())); + settings.setValue("sslrootcert", stdStrToQ(cc.sslRootCert())); + settings.setValue("sslcrl", stdStrToQ(cc.sslCrl())); + settings.setValue("passwordState", static_cast(cc.passwordState())); + } + + template + bool in_range(T value) + { + return value >= std::numeric_limits::min() && value <= std::numeric_limits::max(); + } + + void LoadConnectionConfig(QSettings &settings, ConnectionConfig &cc) + { + cc.setName(qvarToStdStr(settings.value("name"))); + cc.setHost(qvarToStdStr(settings.value("host"))); + cc.setHostAddr(qvarToStdStr(settings.value("hostaddr"))); + int p = settings.value("port", 5432).toInt(); + if (!in_range(p)) { + p = 0; // let the user re-enter a valid value + } + + cc.setPort(static_cast(p)); + cc.setUser(qvarToStdStr(settings.value("user"))); + + //cc.setPassword(qvarToStdStr(settings.value("password"))); + + cc.setDbname(qvarToStdStr(settings.value("dbname"))); + cc.setSslMode(static_cast(settings.value("sslmode").toInt())); + cc.setSslCert(qvarToStdStr(settings.value("sslcert"))); + cc.setSslKey(qvarToStdStr(settings.value("sslkey"))); + cc.setSslRootCert(qvarToStdStr(settings.value("sslrootcert"))); + cc.setSslCrl(qvarToStdStr(settings.value("sslcrl"))); + + PasswordState pwstate; + QVariant v = settings.value("passwordState"); + if (v.isNull()) pwstate = PasswordState::NotStored; + else pwstate = static_cast(v.toInt()); + cc.setPasswordState(pwstate); + } + + + +} // end of unnamed namespace + + +ConnectionListModel::ConnectionListModel(QObject *parent) : QAbstractListModel(parent) - , m_connections(conns) { } @@ -18,7 +82,7 @@ int ConnectionListModel::rowCount(const QModelIndex &parent) const { int result = 0; if (parent == QModelIndex()) { - result = m_connections->size(); + result = m_connections.size(); } return result; } @@ -34,7 +98,7 @@ QVariant ConnectionListModel::data(const QModelIndex &index, int role) const if (role == Qt::DisplayRole || role == Qt::EditRole) { int row = index.row(); int col = index.column(); - const ConnectionConfig& cfg = m_connections->getConfigByIdx(row); + const ConnectionConfig& cfg = m_connections.at(row); switch (col) { case Description: result = makeLongDescription(cfg); @@ -72,7 +136,7 @@ bool ConnectionListModel::setData(const QModelIndex &index, const QVariant &valu // auto& elem = m_connections.at(row); // elem.m_dirty = true; // ConnectionConfig& cfg = elem.m_config; - ConnectionConfig& cfg = m_connections->getConfigByIdx(row); + ConnectionConfig& cfg = m_connections[row]; if (col > 0) { result = true; } @@ -109,7 +173,7 @@ Qt::ItemFlags ConnectionListModel::flags(const QModelIndex &index) const { Qt::ItemFlags result; int row = index.row(); - if (row >= 0 && row < (int)m_connections->size()) { + if (row >= 0 && row < m_connections.size()) { result = Qt::ItemIsSelectable | Qt::ItemIsEnabled; if (index.column() != Description) result |= Qt::ItemIsEditable; @@ -133,75 +197,122 @@ QString ConnectionListModel::makeLongDescription(const ConnectionConfig &cfg) return stdStrToQ(result); } -void ConnectionListModel::newItem() -{ - int i = m_connections->createNew(); - auto idx = createIndex(i, 0); - emit dataChanged(idx, idx); -} - -Expected ConnectionListModel::get(size_t row) -{ - if (row < m_connections->size()) { - return m_connections->getConfigByIdx(row); - } - return Expected::fromException(std::out_of_range("Invalid row")); -} - - -#include - -template -size_t as_size_t(T t); - -template <> -size_t as_size_t(int t) -{ - BOOST_ASSERT(t >= 0); - return static_cast(t); -} - -template <> -size_t as_size_t(long t) -{ - BOOST_ASSERT(t >= 0); - return static_cast(t); -} - - -//void ConnectionListModel::del(const int idx) bool ConnectionListModel::removeRows(int row, int count, const QModelIndex &parent) { bool result = false; - if (row >= 0 && row < (int)m_connections->size()) { + + if (row >= 0 && row < m_connections.size()) { beginRemoveRows(parent, row, row + count -1); SCOPE_EXIT { endRemoveRows(); }; - m_connections->remove(as_size_t(row), as_size_t(count)); + QString file_name = iniFileName(); + QSettings settings(file_name, QSettings::IniFormat); + for (int idx = 0; idx < count; ++idx) { + auto&& cc = m_connections[idx+row]; + { + settings.beginGroup(cc.uuid().toString()); + SCOPE_EXIT { settings.endGroup(); }; + for (auto&& k : settings.childKeys()) { + settings.remove(k); + } + } + } + m_connections.remove(row, count); result = true; } return result; } +void ConnectionListModel::newItem() +{ +// int i = m_connections->createNew(); +// auto idx = createIndex(i, 0); +// emit dataChanged(idx, idx); +} -//void ConnectionListModel::load() -//{ -// m_connections->load(); -//} +Expected ConnectionListModel::get(int row) +{ + if (row < m_connections.size()) { + return m_connections.at(row); + } + return Expected::fromException(std::out_of_range("Invalid row")); +} + +void ConnectionListModel::load() +{ + QString file_name = iniFileName(); + QSettings settings(file_name, QSettings::IniFormat); + auto groups = settings.childGroups(); + for (auto&& grp : groups) { + if (grp == "c_IniGroupSecurity") { + // Read security settings + + } else { + QUuid uuid(grp); + if ( ! uuid.isNull() ) { + settings.beginGroup(grp); + SCOPE_EXIT { settings.endGroup(); }; + + ConnectionConfig cc; + cc.setUuid(uuid); + LoadConnectionConfig(settings, cc); + m_connections.push_back(cc); + } + } + } +} void ConnectionListModel::save() { - m_connections->save(); + QString file_name = iniFileName(); + QSettings settings(file_name, QSettings::IniFormat); + for (auto& e : m_connections) { + settings.beginGroup(e.uuid().toString()); + SCOPE_EXIT { settings.endGroup(); }; + + SaveConnectionConfig(settings, e); + e.clean(); + } + settings.sync(); } -void ConnectionListModel::save(size_t index) +void ConnectionListModel::save(int index) { - m_connections->save(index); + auto& e = m_connections[index]; + if (e.dirty()) { + QString file_name = iniFileName(); + QSettings settings(file_name, QSettings::IniFormat); + settings.beginGroup(e.uuid().toString()); + SaveConnectionConfig(settings, e); + e.clean(); + settings.sync(); + } } -void ConnectionListModel::save(size_t index, const ConnectionConfig &cc) +void ConnectionListModel::save(const ConnectionConfig &cc) { - m_connections->setConfigByIdx(index, cc); - m_connections->save(index); + auto find_res = std::find(m_connections.begin(), m_connections.end(), cc.uuid()); + int i; + if (find_res == m_connections.end()) { + m_connections.push_back(cc); + i = m_connections.size() - 1; + } + else { + *find_res = cc; + i = find_res - m_connections.begin(); + } + emit dataChanged(createIndex(i, 0), createIndex(i, ColCount-1)); + save(i); +} + +QString ConnectionListModel::iniFileName() +{ + QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + QDir dir(path); + if (!dir.exists()) { + dir.mkpath("."); + } + path += "/connections.ini"; + return path; } diff --git a/pglab/ConnectionListModel.h b/pglab/ConnectionListModel.h index be61202..611bb04 100644 --- a/pglab/ConnectionListModel.h +++ b/pglab/ConnectionListModel.h @@ -8,8 +8,8 @@ #include "ConnectionConfig.h" #include "Expected.h" +#include -class ConnectionList; /** \brief Model class for the list of connections. * @@ -30,30 +30,44 @@ public: ColCount }; - ConnectionListModel(ConnectionList *conns, QObject *parent); + ConnectionListModel(QObject *parent); ConnectionListModel(const ConnectionListModel&) = delete; ~ConnectionListModel() override; + + // BEGIN Model/View related functions virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex &/*parent*/) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; // virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; virtual Qt::ItemFlags flags(const QModelIndex &index) const override; + virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + // END Model/View related functions void newItem(); - Expected get(size_t row); - virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + Expected get(int row); + void load(); + // Writes all entries to storage void save(); - void save(size_t index); - void save(size_t index, const ConnectionConfig &cc); + // Writes the specified entry to storage + void save(int index); +// // Save changes to the config +// void save(size_t index, const ConnectionConfig &cc); + /** Matches cc to the list by looking at its uuid. + * + * If it is not in the list it is added. If the uuid is in the list that entry is updated. + * In both cases the data is also directly written to long term storage. + */ + void save(const ConnectionConfig &cc); static QString makeLongDescription(const ConnectionConfig &cfg); private: - ConnectionList *m_connections; - + using ConnectionList = QVector; + ConnectionList m_connections; + QString iniFileName(); }; #endif // CONNECTIONLISTMODEL_H diff --git a/pglab/ConnectionManagerWindow.cpp b/pglab/ConnectionManagerWindow.cpp index 7c053e0..fae38d3 100644 --- a/pglab/ConnectionManagerWindow.cpp +++ b/pglab/ConnectionManagerWindow.cpp @@ -1,8 +1,7 @@ #include "ConnectionManagerWindow.h" #include "ui_ConnectionManagerWindow.h" -//#include "mainwindow.h" #include "MasterController.h" -#include +#include "ConnectionController.h" #include #include #include "ConnectionListModel.h" @@ -30,43 +29,17 @@ ConnectionManagerWindow::ConnectionManagerWindow(MasterController *master, QWidg { ui->setupUi(this); ui->listView->setModel(m_connectionController->getConnectionListModel()); - - setupWidgetMappings(); - - connect(ui->listView->selectionModel(), - SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(on_currentChanged(QModelIndex,QModelIndex))); } ConnectionManagerWindow::~ConnectionManagerWindow() { delete ui; - delete m_mapper; } void ConnectionManagerWindow::on_actionAdd_Connection_triggered() { - auto clm = m_connectionController->getConnectionListModel(); - clm->newItem(); - - // Select the new row - auto idx = clm->index(clm->rowCount() - 1, 0); - ui->listView->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::Select); -} - -void ConnectionManagerWindow::on_currentChanged(const QModelIndex ¤t, - const QModelIndex &) -{ - int currow = current.row(); - auto clm = m_connectionController->getConnectionListModel(); - if (prevSelection) - clm->save(*prevSelection); - m_mapper->setCurrentIndex(currow); - if (currow >= 0) - prevSelection = static_cast(currow); - else - prevSelection.reset(); + m_connectionController->createConnection(); } void ConnectionManagerWindow::on_actionDelete_connection_triggered() @@ -82,33 +55,18 @@ void ConnectionManagerWindow::on_actionDelete_connection_triggered() } } - -void ConnectionManagerWindow::setupWidgetMappings() -{ - auto clm = m_connectionController->getConnectionListModel(); - m_mapper = new QDataWidgetMapper(this); - m_mapper->setModel(clm); - m_mapper->addMapping(ui->edtName, 1); - m_mapper->addMapping(ui->edtHost, 2); - m_mapper->addMapping(ui->spinPort, 3); - m_mapper->addMapping(ui->edtUser, 4); - m_mapper->addMapping(ui->edtDbname, 6); - m_mapper->toFirst(); -} - void ConnectionManagerWindow::on_actionConnect_triggered() { auto ci = ui->listView->selectionModel()->currentIndex(); if (ci.isValid()) { - auto r = static_cast(ci.row()); - m_connectionController->openSqlWindowForConnection(r); + m_connectionController->openSqlWindowForConnection(ci.row()); } } void ConnectionManagerWindow::on_actionQuit_application_triggered() { auto res = QMessageBox::question(this, "pglab", - tr("Close ALL windows?"), QMessageBox::Yes, QMessageBox::No); + tr("Close all windows?"), QMessageBox::Yes, QMessageBox::No); if (res == QMessageBox::Yes) { QApplication::quit(); } @@ -129,7 +87,12 @@ void ConnectionManagerWindow::on_actionManage_server_triggered() void ConnectionManagerWindow::on_listView_activated(const QModelIndex &index) { if (index.isValid()) { - auto r = static_cast(index.row()); - m_connectionController->openSqlWindowForConnection(r); + m_connectionController->openSqlWindowForConnection(index.row()); } } + +void ConnectionManagerWindow::on_actionConfigure_connection_triggered() +{ + auto ci = ui->listView->selectionModel()->currentIndex(); + m_connectionController->editConnection(ci.row()); +} diff --git a/pglab/ConnectionManagerWindow.h b/pglab/ConnectionManagerWindow.h index 85638fc..54e1d1e 100644 --- a/pglab/ConnectionManagerWindow.h +++ b/pglab/ConnectionManagerWindow.h @@ -11,7 +11,6 @@ class ConnectionManagerWindow; class ConnectionConfig; class ConnectionController; class MasterController; -class QDataWidgetMapper; class QStandardItemModel; /** \brief Class that holds glue code for the ConnectionManager UI. @@ -25,7 +24,6 @@ public: private slots: void on_actionAdd_Connection_triggered(); - void on_currentChanged(const QModelIndex ¤t, const QModelIndex &previous); void on_actionDelete_connection_triggered(); void on_actionConnect_triggered(); void on_actionQuit_application_triggered(); @@ -34,15 +32,12 @@ private slots: void on_listView_activated(const QModelIndex &index); + void on_actionConfigure_connection_triggered(); + private: Ui::ConnectionManagerWindow *ui; - QDataWidgetMapper *m_mapper = nullptr; MasterController *m_masterController; ConnectionController *m_connectionController; - - std::optional prevSelection; - - void setupWidgetMappings(); }; #endif // CONNECTIONMANAGERWINDOW_H diff --git a/pglab/ConnectionManagerWindow.ui b/pglab/ConnectionManagerWindow.ui index 1fae730..41fc0f6 100644 --- a/pglab/ConnectionManagerWindow.ui +++ b/pglab/ConnectionManagerWindow.ui @@ -6,7 +6,7 @@ 0 0 - 800 + 413 600 @@ -28,148 +28,6 @@ true - - - - - - Name - - - - - - - - - - Host - - - - - - - - - - Port - - - - - - - 65535 - - - - - - - Username - - - - - - - - - - Database - - - - - - - - - - SSL - - - - - - - 2 - - - - reject - - - - - allow - - - - - prefer - - - - - require - - - - - verify-ca - - - - - verify-full - - - - - - - - Certificate - - - - - - - - - - Key - - - - - - - - - - Root cert. - - - - - - - - - - Revocation list - - - - - - - - @@ -179,7 +37,7 @@ 0 0 - 800 + 413 20 @@ -224,6 +82,7 @@ QToolButton { + @@ -285,6 +144,16 @@ QToolButton { Manage server + + + + :/icons/server_configuration.png + + + + Configure connection + + diff --git a/pglab/DatabaseWindow.cpp b/pglab/DatabaseWindow.cpp index 2e24156..4467836 100644 --- a/pglab/DatabaseWindow.cpp +++ b/pglab/DatabaseWindow.cpp @@ -3,6 +3,7 @@ #include "CrudTab.h" #include "widgets/CatalogTablesPage.h" #include "OpenDatabase.h" +#include "ConnectionController.h" #include "MasterController.h" #include "TaskExecutor.h" #include diff --git a/pglab/MasterController.cpp b/pglab/MasterController.cpp index df2fd88..a851e9f 100644 --- a/pglab/MasterController.cpp +++ b/pglab/MasterController.cpp @@ -1,16 +1,8 @@ #include "MasterController.h" -#include "ConnectionManagerWindow.h" -#include "ConnectionList.h" -#include "ConnectionListModel.h" -#include "PasswordManager.h" -#include "DatabaseWindow.h" -#include "ServerWindow.h" -#include "BackupDialog.h" -#include "PasswordPromptDialog.h" +#include "ConnectionController.h" #include #include #include -#include namespace { @@ -60,193 +52,3 @@ QSqlDatabase& MasterController::userConfigDatabase() } -ConnectionController::ConnectionController(MasterController *parent) - : QObject(parent) - , m_masterController(parent) -{} - -ConnectionController::~ConnectionController() -{ - delete m_connectionManagerWindow; - delete m_connectionListModel; - delete m_connectionList; -} - -void ConnectionController::init() -{ - //std::string dbfilename = QDir::toNativeSeparators(GetUserConfigDatabaseName()).toUtf8().data(); - //m_userConfigDatabase = std::make_shared(dbfilename); - - - m_passwordManager = std::make_shared(); - - m_connectionList = new ConnectionList; - m_connectionList->load(); - m_connectionListModel = new ConnectionListModel(m_connectionList, this); - - m_connectionManagerWindow = new ConnectionManagerWindow(m_masterController, nullptr); - m_connectionManagerWindow->show(); - -} - -void ConnectionController::showConnectionManager() -{ - m_connectionManagerWindow->show(); -} - -void ConnectionController::openSqlWindowForConnection(size_t connection_index) -{ - - auto res = m_connectionListModel->get(connection_index); - if (res.valid()) { - auto cc = res.get(); - - if (retrieveConnectionPassword(cc)) { - m_connectionListModel->save(connection_index, cc); - // TODO instead of directly openening the mainwindow - // do async connect and only open window when we have - // working connection - auto w = new DatabaseWindow(m_masterController, nullptr); - w->setAttribute( Qt::WA_DeleteOnClose ); - w->setConfig(cc); - w->showMaximized(); - } - } - -} - -void ConnectionController::openBackupDlgForConnection(size_t connection_index) -{ - auto res = m_connectionListModel->get(connection_index); - if (res.valid()) { - auto cc = res.get(); - if (retrieveConnectionPassword(cc)) { - m_connectionListModel->save(connection_index, cc); - auto w = new BackupDialog(nullptr); //new ServerWindow(this, nullptr); - w->setAttribute( Qt::WA_DeleteOnClose ); - w->setConfig(cc); - w->show(); - } - } -} - -void ConnectionController::openServerWindowForConnection(size_t connection_index) -{ - auto res = m_connectionListModel->get(connection_index); - if (res.valid()) { - auto cc = res.get(); - if (retrieveConnectionPassword(cc)) { - m_connectionListModel->save(connection_index, cc); - auto w = new ServerWindow(m_masterController, nullptr); - w->setAttribute( Qt::WA_DeleteOnClose ); - w->setConfig(cc); - w->show(); - } - } -} - - -bool ConnectionController::retrieveConnectionPassword(ConnectionConfig &cc) -{ - auto pw_state = cc.passwordState(); - if (pw_state == PasswordState::NotNeeded) { - return true; - } - else if (pw_state == PasswordState::SavedPasswordManager) { - std::string pw; - bool result = getPasswordFromPskdb(getPskId(cc), pw); - if (result) { - cc.setPassword(pw); - return true; - } - } - // Geen else hier want als voorgaande blok niet geretourneerd heeft moeten we wachtwoord - // ook aan de gebruiker vragen zoals hier gebeurd. - QString str = ConnectionListModel::makeLongDescription(cc); - auto dlg = std::make_unique(PasswordPromptDialog::SaveOption, nullptr); - dlg->setCaption(tr("Connection password prompt")); - 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 ConnectionController::getPasswordFromPskdb(const std::string &password_id, std::string &password) -{ - if (!UnlockPasswordManagerIfNeeded()) - return false; - - return m_passwordManager->get(password_id, password); -} - - -bool ConnectionController::storePasswordInPskdb(const std::string &password_id, const std::string password) -{ - if (!UnlockPasswordManagerIfNeeded()) - return false; - - m_passwordManager->set(password_id, password); - return true; -} - -bool ConnectionController::UnlockPasswordManagerIfNeeded() -{ - auto&& user_cfg_db = m_masterController->userConfigDatabase(); - if (m_passwordManager->initialized(user_cfg_db)) { - if (!m_passwordManager->locked()) - return true; - - while (true) { - // ask user for passphrase - auto dlg = std::make_unique(nullptr, nullptr); - dlg->setCaption(tr("Unlock password manager")); - dlg->setDescription(tr("Enter password for password manager")); - int exec_result = dlg->exec(); - bool ok = (exec_result == QDialog::Accepted); - - if (!ok) { - // leave this retry loop - break; - } - // user gave OK, if succeeds return true otherwise loop a prompt for password again. - if (m_passwordManager->openDatabase(user_cfg_db, dlg->password())) - return true; - } - } - else { - // Ask user for passphrase + confirmation, clearly instruct this is first setup - // create - auto dlg = std::make_unique(PasswordPromptDialog::ConfirmPassword, nullptr); - dlg->setCaption(tr("Password manager setup")); - dlg->setDescription(tr("Enter a strong password for password manager initialization. A strong key will be " - "derived from your password and it will be impossible to recover anything from the " - "password manager without the password you enter here.")); - int exec_result = dlg->exec(); - if (exec_result == QDialog::Accepted) { - QString passphrase = dlg->password(); - if (m_passwordManager->createDatabase(user_cfg_db, passphrase)) - return true; - } - } - return false; -} - -std::string ConnectionController::getPskId(const ConnectionConfig &cc) -{ - std::string id = "dbpw/"; - id += cc.uuid().toString().toUtf8().data(); - return id; -} - - - diff --git a/pglab/MasterController.h b/pglab/MasterController.h index 888d783..824976a 100644 --- a/pglab/MasterController.h +++ b/pglab/MasterController.h @@ -8,15 +8,6 @@ #include #include -//namespace Botan { -// class Sqlite3_Database; -//} - -class ConnectionConfig; -class ConnectionList; -class ConnectionListModel; -class ConnectionManagerWindow; -class PasswordManager; class ConnectionController; /** \brief Controller class responsible for all things global. @@ -43,47 +34,5 @@ private: ConnectionController* m_connectionController = nullptr; }; -class ConnectionController : public QObject { - Q_OBJECT -public: - explicit ConnectionController(MasterController *parent = nullptr); - ~ConnectionController(); - - void init(); - - ConnectionListModel *getConnectionListModel() - { - return m_connectionListModel; - } - - void showConnectionManager(); - void openSqlWindowForConnection(size_t connection_index); - void openServerWindowForConnection(size_t connection_index); - void openBackupDlgForConnection(size_t connection_index); - -private: - MasterController *m_masterController; - ConnectionList *m_connectionList = nullptr; - ConnectionListModel *m_connectionListModel = nullptr; - ConnectionManagerWindow *m_connectionManagerWindow = nullptr; - - /** Using long lived object so it can remember its master password for sometime - * if the user wishes it. - */ - std::shared_ptr m_passwordManager; - - /** Retrieves the connection password from the user (directly or through the psk db) - * - */ - bool retrieveConnectionPassword(ConnectionConfig &cc); - - bool getPasswordFromPskdb(const std::string &password_id, std::string &password); - - bool storePasswordInPskdb(const std::string &password_id, const std::string password); - - bool UnlockPasswordManagerIfNeeded(); - - static std::string getPskId(const ConnectionConfig &cc); -}; #endif // MASTERCONTROLLER_H diff --git a/pglab/SslModeModel.cpp b/pglab/SslModeModel.cpp new file mode 100644 index 0000000..9694d2f --- /dev/null +++ b/pglab/SslModeModel.cpp @@ -0,0 +1,69 @@ +#include "SslModeModel.h" + +SslModeModel::SslModeModel(QObject *parent) + : QAbstractListModel(parent) +{} + +int SslModeModel::rowCount(const QModelIndex &) const +{ + return 6; +} + +int SslModeModel::columnCount(const QModelIndex &) const +{ + return ColCount; +} + +QVariant SslModeModel::data(const QModelIndex &index, int role) const +{ + QVariant v; + if (role == Qt::DisplayRole) { + switch (index.column()) { + case Name: + switch(index.row()) { + case 0: + v = tr("disable"); + break; + case 1: + v = tr("allow"); + break; + case 2: + v = tr("prefer"); + break; + case 3: + v = tr("require"); + break; + case 4: + v = tr("verify_ca"); + break; + case 5: + v = tr("verify_full"); + break; + } + break; + case Description: + switch(index.row()) { + case 0: + v = tr("try a non encrypted connection only"); + break; + case 1: + v = tr("try no encryption first then try encrypted"); + break; + case 2: + v = tr("try encrypted first then not encrypted"); + break; + case 3: + v = tr("require an encrypted connection"); + break; + case 4: + v = tr("verify encryption certificate has a valid signature"); + break; + case 5: + v = tr("verify encryption certificate has a valid signature and matches the host"); + break; + } + break; + } + } + return v; +} diff --git a/pglab/SslModeModel.h b/pglab/SslModeModel.h new file mode 100644 index 0000000..687f1ef --- /dev/null +++ b/pglab/SslModeModel.h @@ -0,0 +1,20 @@ +#ifndef SSLMODEMODEL_H +#define SSLMODEMODEL_H + +#include + +class SslModeModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum Column { Name, Description, ColCount }; + + SslModeModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +}; + +#endif // SSLMODEMODEL_H diff --git a/pglab/icons/server_configuration.png b/pglab/icons/server_configuration.png new file mode 100644 index 0000000..dcf14ae Binary files /dev/null and b/pglab/icons/server_configuration.png differ diff --git a/pglab/pglab.pro b/pglab/pglab.pro index 3d0e3f9..ed2bb43 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -21,6 +21,8 @@ DEFINES += _WIN32_WINNT=0x0501 win32:RC_ICONS += pglab.ico SOURCES += main.cpp\ + ConnectionConfigurationWidget.cpp \ + ConnectionController.cpp \ NotificationListWidget.cpp \ NotificationModel.cpp \ NotificationService.cpp \ @@ -30,6 +32,7 @@ SOURCES += main.cpp\ CreateDatabaseDialog.cpp \ ConnectionManagerWindow.cpp \ ConnectionListModel.cpp \ + SslModeModel.cpp \ stopwatch.cpp \ TuplesResultWidget.cpp \ BackupDialog.cpp \ @@ -40,7 +43,6 @@ SOURCES += main.cpp\ ServerWindow.cpp \ DatabasesTableModel.cpp \ RolesTableModel.cpp \ - ConnectionList.cpp \ ProcessStdioWidget.cpp \ GlobalIoService.cpp \ ResultTableModelUtil.cpp \ @@ -85,6 +87,8 @@ PropertyProxyModel.cpp \ widgets/CatalogSequencesPage.cpp HEADERS += \ + ConnectionConfigurationWidget.h \ + ConnectionController.h \ IDatabaseWindow.h \ NotificationListWidget.h \ NotificationModel.h \ @@ -95,6 +99,7 @@ HEADERS += \ CreateDatabaseDialog.h \ ConnectionManagerWindow.h \ ConnectionListModel.h \ + SslModeModel.h \ stopwatch.h \ TuplesResultWidget.h \ BackupDialog.h \ @@ -105,7 +110,6 @@ HEADERS += \ ServerWindow.h \ DatabasesTableModel.h \ RolesTableModel.h \ - ConnectionList.h \ ProcessStdioWidget.h \ GlobalIoService.h \ ResultTableModelUtil.h \ diff --git a/pglab/resources.qrc b/pglab/resources.qrc index 476a271..689fb61 100644 --- a/pglab/resources.qrc +++ b/pglab/resources.qrc @@ -28,5 +28,6 @@ icons/constraints/primarykey.png icons/constraints/unique.png icons/constraints/index.png + icons/server_configuration.png diff --git a/pglablib/ConnectionConfig.h b/pglablib/ConnectionConfig.h index 1652bcf..a58d69a 100644 --- a/pglablib/ConnectionConfig.h +++ b/pglablib/ConnectionConfig.h @@ -26,7 +26,7 @@ class QString; class ConnectionConfig { public: - ConnectionConfig(); + ConnectionConfig(); // Default object containing invalid uuid void setUuid(const QUuid &uuid); const QUuid &uuid() const; @@ -79,6 +79,8 @@ public: bool dirty() const; void clean(); + + bool operator==(QUuid id) const { return m_uuid == id; } private: QUuid m_uuid; std::string m_name;