#include "ConnectionController.h" #include "MasterController.h" #include "ConnectionManagerWindow.h" #include "ConnectionListModel.h" #include "utils/PasswordManager.h" #include "DatabaseWindow.h" #include "BackupDialog.h" #include "PasswordPromptDialog.h" #include "ConnectionConfigurationWidget.h" #include #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); } 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); openSqlWindowForConnection(*config); } } } void ConnectionController::openSqlWindowForConnection(const ConnectionConfig &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); w->setAttribute( Qt::WA_DeleteOnClose ); w->setConfig(*config); w->show(); } } } void ConnectionController::createConnection() { ConnectionConfig cc; cc.setUuid(QUuid::createUuid()); editConfig(cc); } void ConnectionController::editConnection(QModelIndex index) { auto config = ConnectionTreeModel::getConfigFromModelIndex(index); if (config) { editConfig(*config); } } void ConnectionController::editCopy(QModelIndex index) { auto config = ConnectionTreeModel::getConfigFromModelIndex(index); if (config) { auto cc = *config; cc.setUuid(QUuid::createUuid()); cc.setEncodedPassword({}); // maybe we should decode en reencode? editConfig(cc); } } void ConnectionController::editConfig(ConnectionConfig &cc) { ConnectionConfigurationWidget::editExistingInWindow(this, cc, [this] (ConnectionConfigurationWidget &w) -> void { saveConnection(w); }); } void ConnectionController::saveConnection(ConnectionConfigurationWidget &w) { auto cc = w.data(); auto grp = w.group(); if (w.savePassword()) encryptPassword(cc); if (w.clearPassword()) cc.setEncodedPassword({}); m_connectionTreeModel->save(grp, cc); } void ConnectionController::addGroup() { auto result = QInputDialog::getText(nullptr, tr("Add new connection group"), tr("Group name")); if (!result.isEmpty()) { try { m_connectionTreeModel->addGroup(result); } catch (const SQLiteException &ex) { QMessageBox::critical(nullptr, tr("Add group failed"), tr("Failed to add group.\n") + QString(ex.what())); } } } 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; } bool ConnectionController::retrieveConnectionPassword(ConnectionConfig &cc) { auto enc_pwd = cc.encodedPassword(); if (!enc_pwd.isEmpty()) { std::string pw; bool result = retrieveFromPasswordManager(getPskId(cc.uuid()), std::string_view(enc_pwd.data(), enc_pwd.size()) , 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()) encryptPassword(cc); return true; } return false; } bool ConnectionController::retrieveFromPasswordManager(const std::string &password_id, const std::string_view &enc_password, std::string &password) { try { if (!UnlockPasswordManagerIfNeeded()) return false; password = m_passwordManager->decrypt(password_id, enc_password); return true; } catch (const PasswordManagerException &) { return false; } } bool ConnectionController::encryptPassword(ConnectionConfig &cc) { if (!UnlockPasswordManagerIfNeeded()) return false; std::string password = cc.password().toStdString(); std::string password_id = getPskId(cc.uuid()); std::string enc_password = m_passwordManager->encrypt(password_id, password); cc.setEncodedPassword({ enc_password.data(), static_cast(enc_password.size()) }); return true; } bool ConnectionController::decodeConnectionPassword(QUuid id, QByteArray encoded, QString &out_password) { std::string password_id = getPskId(id); std::string enc(encoded.data(), encoded.size()); std::string password; bool res = retrieveFromPasswordManager(password_id, enc, password); if (res) out_password = QString::fromStdString(password); return res; } void ConnectionController::resetPasswordManager() { SQLiteConnection& user_cfg_db = m_masterController->userConfigDatabase(); SQLiteTransaction tx(user_cfg_db); m_passwordManager->resetMasterPassword(user_cfg_db); m_connectionTreeModel->clearAllPasswords(); tx.Commit(); } 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) { PassphraseResult pp_result = PassphrasePrompt(); if (!pp_result.success) break; // user gave OK, if succeeds return true otherwise loop a prompt for password again. if (m_passwordManager->openDatabase(user_cfg_db, pp_result.passphrase)) { setRelockTimer(pp_result.rememberForMinutes); return true; } } } else { InitializePasswordManager(); return true; } return false; } void ConnectionController::setRelockTimer(int rem_minutes) { if (rem_minutes >= 0) { int timeout = rem_minutes * 60 * 1000; /// rem is in minutes, timeout in millisec m_relockTimer->start(timeout); } } PassphraseResult ConnectionController::PassphrasePrompt() { 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); PassphraseResult result; result.success = ok; if (ok) { result.passphrase = dlg->password(); result.rememberForMinutes = dlg->remember(); } return result; } bool ConnectionController::InitializePasswordManager() { // 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(); auto&& user_cfg_db = m_masterController->userConfigDatabase(); if (m_passwordManager->createDatabase(user_cfg_db, passphrase)) return true; } return false; } std::string ConnectionController::getPskId(QUuid connectionid) { std::string id = "dbpw/"; id += connectionid.toString().toUtf8().data(); return id; } void ConnectionController::relock() { m_passwordManager->closeDatabase(); }