diff options
Diffstat (limited to 'src/auth_flow.cpp')
| -rw-r--r-- | src/auth_flow.cpp | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/src/auth_flow.cpp b/src/auth_flow.cpp new file mode 100644 index 0000000..07544e8 --- /dev/null +++ b/src/auth_flow.cpp @@ -0,0 +1,159 @@ +#include <QHeaderView> +#include <QJsonDocument> +#include <QJsonObject> +#include <QTableView> +#include <QVBoxLayout> + +#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(); +} |
