#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" #include #include #include ConnectionController::ConnectionController(MasterController *parent) : QObject(parent) , m_masterController(parent) , m_relockTimer(new QTimer(this)) { m_relockTimer->setSingleShot(true); m_relockTimer->setTimerType(Qt::VeryCoarseTimer); // Force signal to go through queue so when the password manager is relocked after 0msec // the code that retrieves the password is garanteed to run before the signal is handled // because only after the password is retrieved the loop has a chance to run. m_relockTimer->callOnTimeout(this, &ConnectionController::relock, Qt::QueuedConnection); } ConnectionController::~ConnectionController() { delete m_connectionManagerWindow; delete m_connectionTreeModel; } void ConnectionController::init() { m_passwordManager = std::make_shared(); m_connectionTreeModel = new ConnectionTreeModel(this, m_masterController->userConfigDatabase()); m_connectionTreeModel->load(); m_connectionManagerWindow = new ConnectionManagerWindow(m_masterController, nullptr); m_connectionManagerWindow->show(); } void ConnectionController::showConnectionManager() { m_connectionManagerWindow->show(); } void ConnectionController::openSqlWindowForConnection(QModelIndex index) { auto config = ConnectionTreeModel::getConfigFromModelIndex(index); if (config) { if (retrieveConnectionPassword(*config)) { m_connectionTreeModel->save(*config); // 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(*config); w->showMaximized(); } } } void ConnectionController::openBackupDlgForConnection(QModelIndex index) { auto config = ConnectionTreeModel::getConfigFromModelIndex(index); if (config) { if (retrieveConnectionPassword(*config)) { m_connectionTreeModel->save(*config); auto w = new BackupDialog(nullptr); //new ServerWindow(this, nullptr); w->setAttribute( Qt::WA_DeleteOnClose ); w->setConfig(*config); w->show(); } } } void ConnectionController::createConnection() { ConnectionConfig cc; cc.setUuid(QUuid::createUuid()); ConnectionConfigurationWidget::editExistingInWindow(this, cc); } void ConnectionController::editConnection(QModelIndex index) { auto config = ConnectionTreeModel::getConfigFromModelIndex(index); if (config) { ConnectionConfigurationWidget::editExistingInWindow(this, *config); } } void ConnectionController::addGroup() { auto result = QInputDialog::getText(nullptr, tr("Add new connection group"), tr("Group name")); if (!result.isEmpty()) { auto res = m_connectionTreeModel->addGroup(result); if (std::holds_alternative(res)) { QMessageBox::critical(nullptr, tr("Add group failed"), tr("Failed to add group.\n") + std::get(res).text()); } } } void ConnectionController::removeGroup(QModelIndex index) { auto group = ConnectionTreeModel::getGroupFromModelIndex(index); if (group) { auto btn = QMessageBox::question(nullptr, tr("Connection group"), tr("Remove the selected group and all connections contained in the group?"), QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No), QMessageBox::NoButton); if (btn == QMessageBox::Yes) { m_connectionTreeModel->removeGroup(index.row()); } } } std::shared_ptr ConnectionController::passwordManager() { return m_passwordManager; } void ConnectionController::openServerWindowForConnection(QModelIndex index) { auto config = ConnectionTreeModel::getConfigFromModelIndex(index); if (config) { if (retrieveConnectionPassword(*config)) { m_connectionTreeModel->save(*config); auto w = new ServerWindow(m_masterController, nullptr); w->setAttribute( Qt::WA_DeleteOnClose ); w->setConfig(*config); w->show(); } } } bool ConnectionController::retrieveConnectionPassword(ConnectionConfig &cc) { auto enc_pwd = cc.encodedPassword(); if (!enc_pwd.isEmpty()) { std::string pw; bool result = decodePassword(getPskId(cc), std::string_view(enc_pwd.data(), enc_pwd.size()) , pw);// getPasswordFromPskdb(getPskId(cc), pw); if (result) { cc.setPassword(QString::fromUtf8(pw.data(), pw.size())); 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 = cc.makeLongDescription(); 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) { auto password = dlg->password(); cc.setPassword(password); if (dlg->saveChecked()) { auto ba = password.toUtf8(); std::string pw(ba.data(), static_cast(ba.size())); std::string encoded_pw; if (encodePassword(getPskId(cc), pw, encoded_pw)) { cc.setEncodedPassword({ encoded_pw.data(), static_cast(encoded_pw.size()) }); } } return true; } return false; } bool ConnectionController::decodePassword(const std::string &password_id, const std::string_view &enc_password, std::string &password) { if (!UnlockPasswordManagerIfNeeded()) return false; password = m_passwordManager->decrypt(password_id, enc_password); return true; } bool ConnectionController::encodePassword(const std::string &password_id, const std::string &password, std::string &enc_password) { if (!UnlockPasswordManagerIfNeeded()) return false; enc_password = m_passwordManager->encrypt(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(PasswordPromptDialog::RememberPassword, 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())) { int rem = dlg->remember(); if (rem >= 0) { int timeout = rem * 60 * 1000; /// rem is in minutes, timeout in millisec m_relockTimer->start(timeout); } 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; } void ConnectionController::relock() { m_passwordManager->closeDatabase(); }