From abb4aea1718e31406303033e735f529ab8ac81e6 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 21 Dec 2018 14:01:17 +0100 Subject: [PATCH] RemoteLinux: Improve device wizard We add a new wizard page for deploying a public key. This way, the user has to enter the password only once during initial device setup, just as it used to be before we switched the SSH backend. Change-Id: Ic30e830a839033e7e7664c2713d3b74b5472e969 Reviewed-by: hjk --- src/plugins/qnx/qnxdevicewizard.cpp | 38 ++-- src/plugins/qnx/qnxdevicewizard.h | 7 +- .../genericlinuxdeviceconfigurationwizard.cpp | 29 ++- ...riclinuxdeviceconfigurationwizardpages.cpp | 196 +++++++++++++----- ...nericlinuxdeviceconfigurationwizardpages.h | 42 ++-- ...linuxdeviceconfigurationwizardsetuppage.ui | 77 +------ .../remotelinux/publickeydeploymentdialog.h | 5 +- 7 files changed, 209 insertions(+), 185 deletions(-) diff --git a/src/plugins/qnx/qnxdevicewizard.cpp b/src/plugins/qnx/qnxdevicewizard.cpp index 2ca8968388b..43208dd5480 100644 --- a/src/plugins/qnx/qnxdevicewizard.cpp +++ b/src/plugins/qnx/qnxdevicewizard.cpp @@ -26,7 +26,6 @@ #include "qnxdevicewizard.h" #include "qnxconstants.h" -#include "qnxdevice.h" #include #include @@ -37,44 +36,33 @@ using namespace ProjectExplorer; namespace Qnx { namespace Internal { -class QnxDeviceWizardSetupPage : public RemoteLinux::GenericLinuxDeviceConfigurationWizardSetupPage -{ -public: - QnxDeviceWizardSetupPage(QWidget *parent) : - RemoteLinux::GenericLinuxDeviceConfigurationWizardSetupPage(parent) - {} - - QString defaultConfigurationName() const override { return QnxDeviceWizard::tr("QNX Device"); } -}; - QnxDeviceWizard::QnxDeviceWizard(QWidget *parent) : Utils::Wizard(parent) { setWindowTitle(tr("New QNX Device Configuration Setup")); - m_setupPage = new QnxDeviceWizardSetupPage(this); + m_setupPage = new RemoteLinux::GenericLinuxDeviceConfigurationWizardSetupPage(this); + m_keyDeploymentPage + = new RemoteLinux::GenericLinuxDeviceConfigurationWizardKeyDeploymentPage(this); m_finalPage = new RemoteLinux::GenericLinuxDeviceConfigurationWizardFinalPage(this); setPage(SetupPageId, m_setupPage); + setPage(KeyDeploymenPageId, m_keyDeploymentPage); setPage(FinalPageId, m_finalPage); m_finalPage->setCommitPage(true); + QSsh::SshConnectionParameters sshParams; + sshParams.timeout = 10; + m_device = QnxDevice::create(tr("QNX Device"), Core::Id(Constants::QNX_QNX_OS_TYPE), + IDevice::Hardware); + m_device->setSshParameters(sshParams); + m_device->setFreePorts(Utils::PortList::fromString(QLatin1String("10000-10100"))); + m_setupPage->setDevice(m_device); + m_keyDeploymentPage->setDevice(m_device); } IDevice::Ptr QnxDeviceWizard::device() { - QSsh::SshConnectionParameters sshParams; - sshParams.url = m_setupPage->url(); - sshParams.timeout = 10; - sshParams.authenticationType = m_setupPage->authenticationType(); - if (sshParams.authenticationType == QSsh::SshConnectionParameters::AuthenticationTypeSpecificKey) - sshParams.privateKeyFile = m_setupPage->privateKeyFilePath(); - - QnxDevice::Ptr device = QnxDevice::create(m_setupPage->configurationName(), - Core::Id(Constants::QNX_QNX_OS_TYPE), IDevice::Hardware); - device->setSshParameters(sshParams); - device->setFreePorts(Utils::PortList::fromString(QLatin1String("10000-10100"))); - - return device; + return m_device; } } // namespace Internal diff --git a/src/plugins/qnx/qnxdevicewizard.h b/src/plugins/qnx/qnxdevicewizard.h index 1c7751cd9a3..e67566f3c04 100644 --- a/src/plugins/qnx/qnxdevicewizard.h +++ b/src/plugins/qnx/qnxdevicewizard.h @@ -25,11 +25,13 @@ #pragma once -#include +#include "qnxdevice.h" + #include namespace RemoteLinux { class GenericLinuxDeviceConfigurationWizardSetupPage; +class GenericLinuxDeviceConfigurationWizardKeyDeploymentPage; class GenericLinuxDeviceConfigurationWizardFinalPage; } @@ -48,11 +50,14 @@ public: private: enum PageId { SetupPageId, + KeyDeploymenPageId, FinalPageId }; RemoteLinux::GenericLinuxDeviceConfigurationWizardSetupPage *m_setupPage; + RemoteLinux::GenericLinuxDeviceConfigurationWizardKeyDeploymentPage *m_keyDeploymentPage; RemoteLinux::GenericLinuxDeviceConfigurationWizardFinalPage *m_finalPage; + QnxDevice::Ptr m_device; }; } // namespace Internal diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp index 30a26d66d79..6e73ea83a2f 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp @@ -36,20 +36,20 @@ using namespace QSsh; namespace RemoteLinux { namespace Internal { -namespace { -enum PageId { SetupPageId, FinalPageId }; -} // anonymous namespace +enum PageId { SetupPageId, KeyDeploymentPageId, FinalPageId }; class GenericLinuxDeviceConfigurationWizardPrivate { public: GenericLinuxDeviceConfigurationWizardPrivate(QWidget *parent) - : setupPage(parent), finalPage(parent) + : setupPage(parent), keyDeploymentPage(parent), finalPage(parent) { } GenericLinuxDeviceConfigurationWizardSetupPage setupPage; + GenericLinuxDeviceConfigurationWizardKeyDeploymentPage keyDeploymentPage; GenericLinuxDeviceConfigurationWizardFinalPage finalPage; + LinuxDevice::Ptr device; }; } // namespace Internal @@ -59,8 +59,17 @@ GenericLinuxDeviceConfigurationWizard::GenericLinuxDeviceConfigurationWizard(QWi { setWindowTitle(tr("New Generic Linux Device Configuration Setup")); setPage(Internal::SetupPageId, &d->setupPage); + setPage(Internal::KeyDeploymentPageId, &d->keyDeploymentPage); setPage(Internal::FinalPageId, &d->finalPage); d->finalPage.setCommitPage(true); + d->device = LinuxDevice::create(tr("Generic Linux Device"), + Core::Id(Constants::GenericLinuxOsType), IDevice::Hardware); + d->device->setFreePorts(Utils::PortList::fromString(QLatin1String("10000-10100"))); + SshConnectionParameters sshParams; + sshParams.timeout = 10; + d->device->setSshParameters(sshParams); + d->setupPage.setDevice(d->device); + d->keyDeploymentPage.setDevice(d->device); } GenericLinuxDeviceConfigurationWizard::~GenericLinuxDeviceConfigurationWizard() @@ -70,17 +79,7 @@ GenericLinuxDeviceConfigurationWizard::~GenericLinuxDeviceConfigurationWizard() IDevice::Ptr GenericLinuxDeviceConfigurationWizard::device() { - SshConnectionParameters sshParams; - sshParams.url = d->setupPage.url(); - sshParams.timeout = 10; - sshParams.authenticationType = d->setupPage.authenticationType(); - if (sshParams.authenticationType == SshConnectionParameters::AuthenticationTypeSpecificKey) - sshParams.privateKeyFile = d->setupPage.privateKeyFilePath(); - IDevice::Ptr device = LinuxDevice::create(d->setupPage.configurationName(), - Core::Id(Constants::GenericLinuxOsType), IDevice::Hardware); - device->setFreePorts(Utils::PortList::fromString(QLatin1String("10000-10100"))); - device->setSshParameters(sshParams); - return device; + return d->device; } } // namespace RemoteLinux diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp index 2ed7f6b1fa7..dac4449aeeb 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp @@ -26,7 +26,19 @@ #include "genericlinuxdeviceconfigurationwizardpages.h" #include "ui_genericlinuxdeviceconfigurationwizardsetuppage.h" +#include "publickeydeploymentdialog.h" + #include +#include +#include +#include + +#include +#include +#include +#include +#include +#include namespace RemoteLinux { namespace Internal { @@ -35,6 +47,7 @@ class GenericLinuxDeviceConfigurationWizardSetupPagePrivate { public: Ui::GenericLinuxDeviceConfigurationWizardSetupPage ui; + LinuxDevice::Ptr device; }; class GenericLinuxDeviceConfigurationWizardFinalPagePrivate @@ -54,18 +67,9 @@ GenericLinuxDeviceConfigurationWizardSetupPage::GenericLinuxDeviceConfigurationW d->ui.setupUi(this); setTitle(tr("Connection")); setSubTitle(QLatin1String(" ")); // For Qt bug (background color) - d->ui.privateKeyPathChooser->setExpectedKind(PathChooser::File); - d->ui.privateKeyPathChooser->setHistoryCompleter(QLatin1String("Ssh.KeyFile.History")); - d->ui.privateKeyPathChooser->setPromptDialogTitle(tr("Choose a Private Key File")); connect(d->ui.nameLineEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); connect(d->ui.hostNameLineEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); connect(d->ui.userNameLineEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); - connect(d->ui.privateKeyPathChooser, &PathChooser::validChanged, - this, &QWizardPage::completeChanged); - connect(d->ui.defaultAuthButton, &QAbstractButton::toggled, - this, &GenericLinuxDeviceConfigurationWizardSetupPage::handleAuthTypeChanged); - connect(d->ui.keyButton, &QAbstractButton::toggled, - this, &GenericLinuxDeviceConfigurationWizardSetupPage::handleAuthTypeChanged); } GenericLinuxDeviceConfigurationWizardSetupPage::~GenericLinuxDeviceConfigurationWizardSetupPage() @@ -75,21 +79,25 @@ GenericLinuxDeviceConfigurationWizardSetupPage::~GenericLinuxDeviceConfiguration void GenericLinuxDeviceConfigurationWizardSetupPage::initializePage() { - d->ui.nameLineEdit->setText(defaultConfigurationName()); - d->ui.hostNameLineEdit->setText(defaultHostName()); - d->ui.userNameLineEdit->setText(defaultUserName()); - d->ui.defaultAuthButton->setChecked(true); - d->ui.privateKeyPathChooser->setPath(ProjectExplorer::IDevice::defaultPrivateKeyFilePath()); - handleAuthTypeChanged(); + d->ui.nameLineEdit->setText(d->device->displayName()); + d->ui.hostNameLineEdit->setText(d->device->sshParameters().host()); + d->ui.userNameLineEdit->setText(d->device->sshParameters().userName()); } bool GenericLinuxDeviceConfigurationWizardSetupPage::isComplete() const { return !configurationName().isEmpty() && !d->ui.hostNameLineEdit->text().trimmed().isEmpty() - && !d->ui.userNameLineEdit->text().trimmed().isEmpty() - && (authenticationType() != SshConnectionParameters::AuthenticationTypeSpecificKey - || d->ui.privateKeyPathChooser->isValid()); + && !d->ui.userNameLineEdit->text().trimmed().isEmpty(); +} + +bool GenericLinuxDeviceConfigurationWizardSetupPage::validatePage() +{ + d->device->setDisplayName(configurationName()); + SshConnectionParameters sshParams = d->device->sshParameters(); + sshParams.url = url(); + d->device->setSshParameters(sshParams); + return true; } QString GenericLinuxDeviceConfigurationWizardSetupPage::configurationName() const @@ -106,45 +114,11 @@ QUrl GenericLinuxDeviceConfigurationWizardSetupPage::url() const return url; } -SshConnectionParameters::AuthenticationType GenericLinuxDeviceConfigurationWizardSetupPage::authenticationType() const +void GenericLinuxDeviceConfigurationWizardSetupPage::setDevice(const LinuxDevice::Ptr &device) { - return d->ui.keyButton->isChecked() ? SshConnectionParameters::AuthenticationTypeSpecificKey - : SshConnectionParameters::AuthenticationTypeAll; + d->device = device; } -QString GenericLinuxDeviceConfigurationWizardSetupPage::privateKeyFilePath() const -{ - return d->ui.privateKeyPathChooser->path(); -} - -QString GenericLinuxDeviceConfigurationWizardSetupPage::defaultConfigurationName() const -{ - return tr("Generic Linux Device"); -} - -QString GenericLinuxDeviceConfigurationWizardSetupPage::defaultHostName() const -{ - return QString(); -} - -QString GenericLinuxDeviceConfigurationWizardSetupPage::defaultUserName() const -{ - return QString(); -} - -QString GenericLinuxDeviceConfigurationWizardSetupPage::defaultPassWord() const -{ - return QString(); -} - -void GenericLinuxDeviceConfigurationWizardSetupPage::handleAuthTypeChanged() -{ - d->ui.privateKeyPathChooser->setEnabled(authenticationType() - == SshConnectionParameters::AuthenticationTypeSpecificKey); - emit completeChanged(); -} - - GenericLinuxDeviceConfigurationWizardFinalPage::GenericLinuxDeviceConfigurationWizardFinalPage(QWidget *parent) : QWizardPage(parent), d(new Internal::GenericLinuxDeviceConfigurationWizardFinalPagePrivate) { @@ -168,7 +142,119 @@ void GenericLinuxDeviceConfigurationWizardFinalPage::initializePage() QString GenericLinuxDeviceConfigurationWizardFinalPage::infoText() const { return tr("The new device configuration will now be created.\n" - "In addition, device connectivity will be tested."); + "In addition, device connectivity will be tested."); +} + +struct GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::Private +{ + QStringList defaultKeys() const + { + const QString baseDir = QDir::homePath() + "/.ssh"; + return QStringList{baseDir + "/id_rsa", baseDir + "/id_ecdsa", baseDir + "/id_ed25519"}; + } + + PathChooser keyFileChooser; + QLabel iconLabel; + LinuxDevice::Ptr device; +}; + +GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::GenericLinuxDeviceConfigurationWizardKeyDeploymentPage(QWidget *parent) + : QWizardPage(parent), d(new Private) +{ + setTitle(tr("Key Deployment")); + setSubTitle(" "); + const QString info = tr("We recommend that you log into your device using public key " + "authentication.\n" + "If your device is already set up for this, you do not have to do " + "anything here.\n" + "Otherwise, please deploy the public key for the private key " + "with which to connect in the future.\n" + "If you do not have a private key yet, you can also " + "create one here."); + d->keyFileChooser.setExpectedKind(PathChooser::File); + d->keyFileChooser.setHistoryCompleter("Ssh.KeyFile.History"); + d->keyFileChooser.setPromptDialogTitle(tr("Choose a Private Key File")); + auto const deployButton = new QPushButton(tr("Deploy Public Key"), this); + connect(deployButton, &QPushButton::clicked, + this, &GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::deployKey); + auto const createButton = new QPushButton(tr("Create New Key Pair"), this); + connect(createButton, &QPushButton::clicked, + this, &GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::createKey); + auto const mainLayout = new QVBoxLayout(this); + auto const keyLayout = new QHBoxLayout; + auto const deployLayout = new QHBoxLayout; + mainLayout->addWidget(new QLabel(info)); + keyLayout->addWidget(new QLabel(tr("Private key file:"))); + keyLayout->addWidget(&d->keyFileChooser); + keyLayout->addWidget(createButton); + keyLayout->addStretch(); + mainLayout->addLayout(keyLayout); + deployLayout->addWidget(deployButton); + deployLayout->addWidget(&d->iconLabel); + deployLayout->addStretch(); + mainLayout->addLayout(deployLayout); + connect(&d->keyFileChooser, &PathChooser::pathChanged, this, [this, deployButton] { + deployButton->setEnabled(d->keyFileChooser.fileName().exists()); + d->iconLabel.clear(); + emit completeChanged(); + }); + for (const QString &defaultKey : d->defaultKeys()) { + if (QFileInfo::exists(defaultKey)) { + d->keyFileChooser.setPath(defaultKey); + break; + } + } +} + +GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::~GenericLinuxDeviceConfigurationWizardKeyDeploymentPage() +{ + delete d; +} + +void GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::setDevice(const LinuxDevice::Ptr &device) +{ + d->device = device; +} + +void GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::initializePage() +{ + if (!d->device->sshParameters().privateKeyFile.isEmpty()) + d->keyFileChooser.setPath(privateKeyFilePath()); + d->iconLabel.clear(); +} + +bool GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::isComplete() const +{ + return d->keyFileChooser.path().isEmpty() || d->keyFileChooser.fileName().exists(); +} + +bool GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::validatePage() +{ + if (!d->defaultKeys().contains(d->keyFileChooser.path())) { + SshConnectionParameters sshParams = d->device->sshParameters(); + sshParams.authenticationType = SshConnectionParameters::AuthenticationTypeSpecificKey; + sshParams.privateKeyFile = d->keyFileChooser.path(); + d->device->setSshParameters(sshParams); + } + return true; +} + +QString GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::privateKeyFilePath() const +{ + return d->keyFileChooser.path(); +} + +void GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::createKey() +{ + SshKeyCreationDialog dlg(this); + if (dlg.exec() == QDialog::Accepted) + d->keyFileChooser.setPath(dlg.privateKeyFilePath()); +} + +void GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::deployKey() +{ + PublicKeyDeploymentDialog dlg(d->device, privateKeyFilePath() + ".pub", this); + d->iconLabel.setPixmap((dlg.exec() == QDialog::Accepted ? Icons::OK : Icons::BROKEN).pixmap()); } } // namespace RemoteLinux diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h index 290ecfaecbc..60837d171b8 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h @@ -25,6 +25,7 @@ #pragma once +#include "linuxdevice.h" #include "remotelinux_export.h" #include @@ -32,6 +33,7 @@ #include namespace RemoteLinux { +class LinuxDevice; namespace Internal { class GenericLinuxDeviceConfigurationWizardSetupPagePrivate; class GenericLinuxDeviceConfigurationWizardFinalPagePrivate; @@ -45,25 +47,41 @@ public: explicit GenericLinuxDeviceConfigurationWizardSetupPage(QWidget *parent = nullptr); ~GenericLinuxDeviceConfigurationWizardSetupPage() override; + void setDevice(const LinuxDevice::Ptr &device); + +private: void initializePage() override; bool isComplete() const override; + bool validatePage() override; QString configurationName() const; QUrl url() const; - QSsh::SshConnectionParameters::AuthenticationType authenticationType() const; - QString privateKeyFilePath() const; - - virtual QString defaultConfigurationName() const; - virtual QString defaultHostName() const; - virtual QString defaultUserName() const; - virtual QString defaultPassWord() const; - -private: - void handleAuthTypeChanged(); Internal::GenericLinuxDeviceConfigurationWizardSetupPagePrivate * const d; }; +class REMOTELINUX_EXPORT GenericLinuxDeviceConfigurationWizardKeyDeploymentPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit GenericLinuxDeviceConfigurationWizardKeyDeploymentPage(QWidget *parent = nullptr); + ~GenericLinuxDeviceConfigurationWizardKeyDeploymentPage() override; + + void setDevice(const LinuxDevice::Ptr &device); + +private: + void initializePage() override; + bool isComplete() const override; + bool validatePage() override; + + QString privateKeyFilePath() const; + void createKey(); + void deployKey(); + + struct Private; + Private * const d; +}; class REMOTELINUX_EXPORT GenericLinuxDeviceConfigurationWizardFinalPage : public QWizardPage { @@ -72,12 +90,12 @@ public: GenericLinuxDeviceConfigurationWizardFinalPage(QWidget *parent); ~GenericLinuxDeviceConfigurationWizardFinalPage() override; - void initializePage() override; - protected: virtual QString infoText() const; private: + void initializePage() override; + Internal::GenericLinuxDeviceConfigurationWizardFinalPagePrivate * const d; }; diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardsetuppage.ui b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardsetuppage.ui index 1c93a359b75..d03e451f4a7 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardsetuppage.ui +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardsetuppage.ui @@ -6,8 +6,8 @@ 0 0 - 590 - 188 + 564 + 119 @@ -82,81 +82,8 @@ - - - - The authentication type: - - - - - - - - - Default - - - - - - - Specific key - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - The file containing the user's private key: - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - Utils::PathChooser - QWidget -
utils/pathchooser.h
- 1 -
-
diff --git a/src/plugins/remotelinux/publickeydeploymentdialog.h b/src/plugins/remotelinux/publickeydeploymentdialog.h index 037e53aaccc..442678a8b56 100644 --- a/src/plugins/remotelinux/publickeydeploymentdialog.h +++ b/src/plugins/remotelinux/publickeydeploymentdialog.h @@ -42,11 +42,12 @@ public: static PublicKeyDeploymentDialog *createDialog(const ProjectExplorer::IDevice::ConstPtr &deviceConfig, QWidget *parent = nullptr); + PublicKeyDeploymentDialog(const ProjectExplorer::IDevice::ConstPtr &deviceConfig, + const QString &publicKeyFileName, QWidget *parent = nullptr); + ~PublicKeyDeploymentDialog() override; private: - explicit PublicKeyDeploymentDialog(const ProjectExplorer::IDevice::ConstPtr &deviceConfig, - const QString &publicKeyFileName, QWidget *parent = nullptr); void handleDeploymentFinished(const QString &errorMsg); void handleDeploymentError(const QString &errorMsg); void handleDeploymentSuccess();