#include #include #include #include #include #include "auth_flow.h" #include "keyring.h" #include "file_functions.h" // This function is supposed to open the authentication dialog with various // fields like method selection and fields to enter password or select a key, // but for now, it'll simply submit a public key auth request. Hostname // parameter is unused. void start_auth_flow(AOApplication *ao_app, QString username) { ao_app->ex_auth_username = username; AuthRequest req; req.username = username; req.method = AuthMethod::certificate; ao_app->send_ex_message(serializeAuthRequest(req)); } KeySelectDialog::KeySelectDialog(KeyringModel *model, QStringView username, QWidget *parent) : QDialog(parent) , m_model(model) { this->setAttribute(Qt::WA_DeleteOnClose); this->setWindowTitle(QString("Select key for %1").arg(username)); auto view = new QTableView(this); view->setModel(m_model); view->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); view->setSelectionBehavior(QAbstractItemView::SelectRows); view->setSelectionMode(QAbstractItemView::SingleSelection); QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); buttons->button(QDialogButtonBox::Ok)->setEnabled(false); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(view); layout->addWidget(buttons); connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(buttons, &QDialogButtonBox::accepted, this, [this, view] { auto rows = view->selectionModel()->selectedRows(); if (rows.isEmpty()) { return; } QByteArray key_id = m_model->data(rows.first(), KeyringModel::KeyIDRole).toByteArray(); QString key_name = m_model->data(rows.first()).toString(); emit key_selected(key_id, key_name); }); connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, this, [view, buttons](const QItemSelection &, const QItemSelection &) { bool selected = !view->selectionModel()->selectedRows().isEmpty(); buttons->button(QDialogButtonBox::Ok)->setEnabled(selected); }); } KeyPassphraseDialog::KeyPassphraseDialog(QStringView key_name, QWidget *parent) : QDialog(parent) { this->setWindowTitle(QString("Enter passphrase for key %1").arg(key_name)); QVBoxLayout *pw_layout = new QVBoxLayout(this); pw_layout->addWidget(new QLabel(QStringLiteral("Passphrase:"), this)); m_pw_line = new QLineEdit(this); m_pw_line->setEchoMode(QLineEdit::Password); pw_layout->addWidget(m_pw_line); QDialogButtonBox *pw_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); pw_layout->addWidget(pw_buttons); m_err_lbl = new QLabel(this); m_err_lbl->setVisible(false); pw_layout->addWidget(m_err_lbl); connect(pw_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(pw_buttons, &QDialogButtonBox::accepted, this, [this] { emit passphrase_submitted(m_pw_line->text().toUtf8()); m_pw_line->clear(); // zero the buffer }); } void KeyPassphraseDialog::display_error(ResponseResult error) const { QString error_text; switch (error) { case ResponseResult::unsupported_version: error_text = QStringLiteral("Unsupported key version."); break; case ResponseResult::decryption_failed: error_text = QStringLiteral("Wrong passphrase (or corrupted key)."); break; default: error_text = QString("Error unlocking key (code %1). Catastrophic failure.").arg((int)error); break; } m_err_lbl->setText(error_text); m_err_lbl->setVisible(true); } AuthFlow::AuthFlow(AOApplication *ao_app, const AuthChallenge &challenge, QWidget *parent) : QObject(parent) , m_ao_app(ao_app) , m_challenge(challenge) { auto saved_key = ao_app->saved_auth.lookup(ao_app->m_serverdata.m_server_hostname.toUtf8(), ao_app->ex_auth_username.toUtf8()); if (!saved_key.isEmpty()) { m_mode = FlowMode::Saved; m_key_dlg = nullptr; on_key_selected(saved_key, QString("%1@%2 (saved)").arg(ao_app->ex_auth_username, ao_app->m_serverdata.m_server_hostname)); } else { m_mode = FlowMode::Default; m_key_dlg = new KeySelectDialog(&ao_app->keyring_model, ao_app->ex_auth_username, parent); connect(m_key_dlg, &KeySelectDialog::key_selected, this, &AuthFlow::on_key_selected); m_key_dlg->open(); } } void AuthFlow::on_key_selected(QByteArrayView key_id, QStringView key_name) { m_pwd_dlg = new KeyPassphraseDialog(key_name, m_key_dlg); connect(m_pwd_dlg, &KeyPassphraseDialog::passphrase_submitted, this, [this, key_id](QByteArrayView passphrase) { AuthResponse response; ResponseResult result = unlock_and_auth(key_id, passphrase, m_challenge.challenge, m_ao_app->ex_auth_username.toUtf8(), response); if (result == ResponseResult::success) { m_ao_app->send_ex_message(serializeAuthResponse(response)); if (m_mode == FlowMode::Default) { m_key_dlg->accept(); m_ao_app->saved_auth.insert(m_ao_app->m_serverdata.m_server_hostname.toUtf8(), m_ao_app->ex_auth_username.toUtf8(), key_id); } else { m_pwd_dlg->accept(); } deleteLater(); } else { m_pwd_dlg->display_error(result); } }); connect(m_pwd_dlg, &QDialog::rejected, this, [this] { if (m_mode == FlowMode::Saved) { m_ao_app->saved_auth.remove(m_ao_app->m_serverdata.m_server_hostname.toUtf8(), m_ao_app->ex_auth_username.toUtf8()); m_mode = FlowMode::Default; m_key_dlg = new KeySelectDialog(&m_ao_app->keyring_model, m_ao_app->ex_auth_username); connect(m_key_dlg, &KeySelectDialog::key_selected, this, &AuthFlow::on_key_selected); m_key_dlg->open(); } }); m_pwd_dlg->open(); }