Commit 8b925007 authored by David Kaspar's avatar David Kaspar
Browse files

Qnx: Auto-generating SSH key used for connecting to a BB10 device



BlackBerryDeviceConfigurationWizardSshKeyPage wizard page has been removed.
It is not needed anymore. The wizard automatically generates or reuses
a SSH key that is stores in __settings__/qnx/id_rsa file. The same SSH key
is used for connecting to all BB10 devices.
The generation of 'default' SSH key is gone as a second step of
BlackBerryDeviceConfigurationWizardQueryPage wizard page.

Disabling device-tester for BlackBerryDeviceConfiguration.
Task-number: QTCREATORBUG-9977

Change-Id: Ice6068530c9c72fa82907decaaa3dca6077e9c3a
Reviewed-by: default avatarTobias Hunger <tobias.hunger@digia.com>
Reviewed-by: default avatarDavid Kaspar <dkaspar@blackberry.com>
parent 772e6698
......@@ -98,6 +98,13 @@ IDevice::Ptr BlackBerryDeviceConfiguration::clone() const
return Ptr(new BlackBerryDeviceConfiguration(*this));
}
bool BlackBerryDeviceConfiguration::hasDeviceTester() const
{
// we are unable to easily verify that a device is available unless we duplicate
// 'Connect to device' functionality, therefore disabling device-tester
return false;
}
BlackBerryDeviceConfiguration::ConstPtr BlackBerryDeviceConfiguration::device(const Kit *k)
{
IDevice::ConstPtr dev = DeviceKitInformation::device(k);
......
......@@ -64,6 +64,7 @@ public:
QString displayNameForActionId(Core::Id actionId) const;
void executeAction(Core::Id actionId, QWidget *parent) const;
ProjectExplorer::IDevice::Ptr clone() const;
bool hasDeviceTester() const;
static ConstPtr device(const ProjectExplorer::Kit *k);
......
......@@ -33,6 +33,7 @@
#include "blackberrydeviceconfigurationwizardpages.h"
#include "qnxconstants.h"
#include "blackberrydeviceconfiguration.h"
#include "blackberrydeviceconnectionmanager.h"
#include <ssh/sshconnection.h>
......@@ -46,13 +47,11 @@ BlackBerryDeviceConfigurationWizard::BlackBerryDeviceConfigurationWizard(QWidget
m_setupPage = new BlackBerryDeviceConfigurationWizardSetupPage(this);
m_queryPage = new BlackBerryDeviceConfigurationWizardQueryPage(m_holder, this);
m_sshKeyPage = new BlackBerryDeviceConfigurationWizardSshKeyPage(this);
m_configPage = new BlackBerryDeviceConfigurationWizardConfigPage(m_holder, this);
m_finalPage = new BlackBerryDeviceConfigurationWizardFinalPage(this);
setPage(SetupPageId, m_setupPage);
setPage(QueryPageId, m_queryPage);
setPage(SshKeyPageId, m_sshKeyPage);
setPage(ConfigPageId, m_configPage);
setPage(FinalPageId, m_finalPage);
m_finalPage->setCommitPage(true);
......@@ -65,7 +64,7 @@ ProjectExplorer::IDevice::Ptr BlackBerryDeviceConfigurationWizard::device()
sshParams.host = m_setupPage->hostName();
sshParams.password = m_setupPage->password();
sshParams.authenticationType = QSsh::SshConnectionParameters::AuthenticationTypePublicKey;
sshParams.privateKeyFile = m_sshKeyPage->privateKey();
sshParams.privateKeyFile = BlackBerryDeviceConnectionManager::instance()->privateKeyPath();
sshParams.userName = QLatin1String("devuser");
sshParams.timeout = 10;
sshParams.port = 22;
......
......@@ -53,14 +53,12 @@ private:
enum PageId {
SetupPageId,
QueryPageId,
SshKeyPageId,
ConfigPageId,
FinalPageId
};
BlackBerryDeviceConfigurationWizardSetupPage *m_setupPage;
BlackBerryDeviceConfigurationWizardQueryPage *m_queryPage;
BlackBerryDeviceConfigurationWizardSshKeyPage *m_sshKeyPage;
BlackBerryDeviceConfigurationWizardConfigPage *m_configPage;
BlackBerryDeviceConfigurationWizardFinalPage *m_finalPage;
......
......@@ -39,6 +39,7 @@
#include "ui_blackberrydeviceconfigurationwizardsshkeypage.h"
#include "ui_blackberrydeviceconfigurationwizardconfigpage.h"
#include "blackberryconfiguration.h"
#include "blackberrydeviceconnectionmanager.h"
#include "qnxutils.h"
#include <coreplugin/icore.h>
......@@ -151,8 +152,6 @@ void BlackBerryDeviceConfigurationWizardSetupPage::onDeviceSelectionChanged()
QList<QListWidgetItem *> selectedItems = m_ui->deviceListWidget->selectedItems();
const QListWidgetItem *selected = selectedItems.count() == 1 ? selectedItems[0] : 0;
const ItemKind itemKind = selected ? selected->data(ItemKindRole).value<ItemKind>() : Note;
const bool isSimulator = selected && itemKind == Autodetected
? selected->data(DeviceTypeRole).toBool() : false;
switch (itemKind) {
case SpecifyManually:
m_ui->deviceHostIp->setEnabled(true);
......@@ -228,6 +227,7 @@ BlackBerryDeviceConfigurationWizardQueryPage::BlackBerryDeviceConfigurationWizar
{
m_ui->setupUi(this);
setTitle(tr("Query Device Information"));
m_ui->progressBar->setMaximum(Done);
connect(m_deviceInformation,SIGNAL(finished(int)),this,SLOT(processQueryFinished(int)));
}
......@@ -240,11 +240,10 @@ BlackBerryDeviceConfigurationWizardQueryPage::~BlackBerryDeviceConfigurationWiza
void BlackBerryDeviceConfigurationWizardQueryPage::initializePage()
{
m_ui->statusLabel->setText(tr("Querying device information. Please wait..."));
m_ui->progressBar->setVisible(true);
m_holder.deviceInfoRetrieved = false;
setState(Querying, tr("Querying device information. Please wait..."));
m_deviceInformation->setDeviceTarget(
field(QLatin1String(DEVICEHOSTNAME_FIELD_ID)).toString(),
field(QLatin1String(DEVICEPASSWORD_FIELD_ID)).toString());
......@@ -261,148 +260,86 @@ void BlackBerryDeviceConfigurationWizardQueryPage::processQueryFinished(int stat
m_holder.isSimulator = m_deviceInformation->isSimulator();
if (m_holder.deviceInfoRetrieved)
m_ui->statusLabel->setText(tr("Device information retrieved successfully."));
checkAndGenerateSSHKeys();
else
m_ui->statusLabel->setText(tr("Cannot connect to the device. Check if the device is in development mode and has matching host name and password."));
m_ui->progressBar->setVisible(false);
emit completeChanged();
if (m_holder.deviceInfoRetrieved)
wizard()->next();
}
bool BlackBerryDeviceConfigurationWizardQueryPage::isComplete() const
{
return m_holder.deviceInfoRetrieved;
}
// ----------------------------------------------------------------------------
BlackBerryDeviceConfigurationWizardSshKeyPage::BlackBerryDeviceConfigurationWizardSshKeyPage(QWidget *parent)
: QWizardPage(parent)
, m_ui(new Ui::BlackBerryDeviceConfigurationWizardSshKeyPage)
{
m_ui->setupUi(this);
m_ui->privateKey->setExpectedKind(Utils::PathChooser::File);
m_ui->progressBar->hide();
QString initialBrowsePath = QnxUtils::dataDirPath();
if (!QFileInfo(initialBrowsePath).exists())
initialBrowsePath = QDir::homePath();
m_ui->privateKey->setInitialBrowsePathBackup(initialBrowsePath);
setTitle(tr("SSH Key Setup"));
setSubTitle(tr("Please select an existing <b>4096</b>-bit key or click <b>Generate</b> to create a new one."));
connect(m_ui->privateKey, SIGNAL(changed(QString)), this, SLOT(findMatchingPublicKey(QString)));
connect(m_ui->privateKey, SIGNAL(changed(QString)), this, SIGNAL(completeChanged()));
connect(m_ui->generate, SIGNAL(clicked()), this, SLOT(generateSshKeys()));
}
BlackBerryDeviceConfigurationWizardSshKeyPage::~BlackBerryDeviceConfigurationWizardSshKeyPage()
{
delete m_ui;
m_ui = 0;
setState(Done, tr("Cannot connect to the device. "
"Check if the device is in development mode and has matching host name and password."));
}
void BlackBerryDeviceConfigurationWizardSshKeyPage::initializePage()
void BlackBerryDeviceConfigurationWizardQueryPage::checkAndGenerateSSHKeys()
{
}
bool BlackBerryDeviceConfigurationWizardSshKeyPage::isComplete() const
{
QFileInfo privateKeyFi(m_ui->privateKey->fileName().toString());
QFileInfo publicKeyFi(m_ui->publicKey->text());
if (! BlackBerryDeviceConnectionManager::instance()->hasValidSSHKeys()) {
setState(GeneratingSshKey, tr("Generating SSH keys. Please wait..."));
return privateKeyFi.exists() && publicKeyFi.exists();
BlackBerrySshKeysGenerator *sshKeysGenerator = new BlackBerrySshKeysGenerator();
connect(sshKeysGenerator, SIGNAL(sshKeysGenerationFailed(QString)),
this, SLOT(sshKeysGenerationFailed(QString)), Qt::QueuedConnection);
connect(sshKeysGenerator, SIGNAL(sshKeysGenerationFinished(QByteArray,QByteArray)),
this, SLOT(processSshKeys(QByteArray,QByteArray)), Qt::QueuedConnection);
sshKeysGenerator->start();
} else
queryDone();
}
QString BlackBerryDeviceConfigurationWizardSshKeyPage::privateKey() const
void BlackBerryDeviceConfigurationWizardQueryPage::sshKeysGenerationFailed(const QString &error)
{
return m_ui->privateKey->fileName().toString();
}
QString BlackBerryDeviceConfigurationWizardSshKeyPage::publicKey() const
{
return m_ui->publicKey->text();
}
void BlackBerryDeviceConfigurationWizardSshKeyPage::findMatchingPublicKey(const QString &privateKeyPath)
{
const QString candidate = privateKeyPath + QLatin1String(".pub");
if (QFileInfo(candidate).exists())
m_ui->publicKey->setText(QDir::toNativeSeparators(candidate));
else
m_ui->publicKey->clear();
}
// this slot can be called asynchronously - processing it only in GeneratingSshKey state
if (m_state != GeneratingSshKey)
return;
void BlackBerryDeviceConfigurationWizardSshKeyPage::sshKeysGenerationFailed(const QString &error)
{
setBusy(false);
QMessageBox::critical(this, tr("Key Generation Failed"), error);
QString message = tr("Failed generating SSH key needed for securing connection to a device. Error: ");
message.append(error);
setState(Done, message);
}
void BlackBerryDeviceConfigurationWizardSshKeyPage::processSshKeys(const QString &privateKeyPath, const QByteArray &privateKey, const QByteArray &publicKey)
void BlackBerryDeviceConfigurationWizardQueryPage::processSshKeys(const QByteArray &privateKey,
const QByteArray &publicKey)
{
setBusy(false);
const QString publicKeyPath = privateKeyPath + QLatin1String(".pub");
if (!saveKeys(privateKey, publicKey, privateKeyPath, publicKeyPath)) // saveKeys(..) will show an error message if necessary
// this slot can be called asynchronously - processing it only in GeneratingSshKey state
if (m_state != GeneratingSshKey)
return;
m_ui->privateKey->setFileName(Utils::FileName::fromString(privateKeyPath));
m_ui->publicKey->setText(QDir::toNativeSeparators(publicKeyPath));
// condition prevents overriding already generated SSH keys
// this may happens when an user enter the QueryPage several times before
// the first SSH keys are generated i.e. when multiple calls of checkAndGenerateSSHKeys()
// before processSshKeys() is called, multiple processSshKeys() calls are triggered later.
// only the first one is allowed to write the SSH keys.
if (! BlackBerryDeviceConnectionManager::instance()->hasValidSSHKeys()) {
QString error;
if (!BlackBerryDeviceConnectionManager::instance()->setSSHKeys(privateKey, publicKey, &error)) {
QString message = tr("Failed saving SSH key needed for securing connection to a device. Error: ");
message.append(error);
setState(Done, message);
return;
}
}
emit completeChanged();
queryDone();
}
bool BlackBerryDeviceConfigurationWizardSshKeyPage::saveKeys(const QByteArray &privateKey, const QByteArray &publicKey, const QString &privateKeyPath, const QString &publicKeyPath)
void BlackBerryDeviceConfigurationWizardQueryPage::queryDone()
{
Utils::FileSaver privSaver(privateKeyPath);
privSaver.write(privateKey);
if (!privSaver.finalize(this))
return false; // finalize shows an error message if necessary
QFile::setPermissions(privateKeyPath, QFile::ReadOwner | QFile::WriteOwner);
Utils::FileSaver pubSaver(publicKeyPath);
pubSaver.write(publicKey);
if (!pubSaver.finalize(this))
return false;
return true;
setState(Done, tr("Device information retrieved successfully."));
}
void BlackBerryDeviceConfigurationWizardSshKeyPage::generateSshKeys()
void BlackBerryDeviceConfigurationWizardQueryPage::setState(QueryState state, const QString &message)
{
QString lookInDir = QnxUtils::dataDirPath();
if (!QFileInfo(lookInDir).exists())
lookInDir = QDir::homePath();
QString privateKeyPath = QFileDialog::getSaveFileName(this, tr("Choose Private Key File Name"), lookInDir);
if (privateKeyPath.isEmpty())
return;
m_state = state;
m_ui->statusLabel->setText(message);
m_ui->progressBar->setVisible(state != Done);
m_ui->progressBar->setValue(state);
emit completeChanged();
setBusy(true);
BlackBerrySshKeysGenerator *sshKeysGenerator = new BlackBerrySshKeysGenerator(privateKeyPath);
connect(sshKeysGenerator, SIGNAL(sshKeysGenerationFailed(QString)), this, SLOT(sshKeysGenerationFailed(QString)), Qt::QueuedConnection);
connect(sshKeysGenerator, SIGNAL(sshKeysGenerationFinished(QString,QByteArray,QByteArray)), this, SLOT(processSshKeys(QString,QByteArray,QByteArray)), Qt::QueuedConnection);
sshKeysGenerator->start();
if (isComplete())
if (wizard()->currentPage() == this)
wizard()->next();
}
void BlackBerryDeviceConfigurationWizardSshKeyPage::setBusy(bool busy)
bool BlackBerryDeviceConfigurationWizardQueryPage::isComplete() const
{
m_ui->privateKey->setEnabled(!busy);
m_ui->publicKey->setEnabled(!busy);
m_ui->generate->setEnabled(!busy);
m_ui->progressBar->setVisible(busy);
wizard()->button(QWizard::BackButton)->setEnabled(!busy);
wizard()->button(QWizard::NextButton)->setEnabled(!busy);
wizard()->button(QWizard::FinishButton)->setEnabled(!busy);
wizard()->button(QWizard::CancelButton)->setEnabled(!busy);
return m_state == Done
&& m_holder.deviceInfoRetrieved
&& BlackBerryDeviceConnectionManager::instance()->hasValidSSHKeys();
}
// ----------------------------------------------------------------------------
......
......@@ -102,6 +102,11 @@ private:
class BlackBerryDeviceConfigurationWizardQueryPage : public QWizardPage
{
Q_OBJECT
enum QueryState
{
Querying = 0, GeneratingSshKey, Done
};
public:
explicit BlackBerryDeviceConfigurationWizardQueryPage(BlackBerryDeviceConfigurationWizardHolder &holder, QWidget *parent = 0);
~BlackBerryDeviceConfigurationWizardQueryPage();
......@@ -111,38 +116,18 @@ public:
private slots:
void processQueryFinished(int status);
void sshKeysGenerationFailed(const QString &error);
void processSshKeys(const QByteArray &privateKey, const QByteArray &publicKey);
private:
void checkAndGenerateSSHKeys();
void queryDone();
void setState(QueryState state, const QString &message);
Ui::BlackBerryDeviceConfigurationWizardQueryPage *m_ui;
BlackBerryDeviceConfigurationWizardHolder &m_holder;
BlackBerryDeviceInformation *m_deviceInformation;
};
class BlackBerryDeviceConfigurationWizardSshKeyPage : public QWizardPage
{
Q_OBJECT
public:
explicit BlackBerryDeviceConfigurationWizardSshKeyPage(QWidget *parent = 0);
~BlackBerryDeviceConfigurationWizardSshKeyPage();
void initializePage();
bool isComplete() const;
QString privateKey() const;
QString publicKey() const;
private slots:
void findMatchingPublicKey(const QString &privateKeyPath);
void sshKeysGenerationFailed(const QString &error);
void processSshKeys(const QString &privateKeyPath, const QByteArray &privateKey, const QByteArray &publicKey);
void generateSshKeys();
private:
bool saveKeys(const QByteArray &privateKey, const QByteArray &publicKey, const QString &privateKeyPath, const QString &publicKeyPath);
void setBusy(bool busy);
Ui::BlackBerryDeviceConfigurationWizardSshKeyPage *m_ui;
QueryState m_state;
};
class BlackBerryDeviceConfigurationWizardConfigPage : public QWizardPage
......
......@@ -25,11 +25,7 @@
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="maximum">
<number>0</number>
</property>
</widget>
<widget class="QProgressBar" name="progressBar"/>
</item>
<item>
<spacer name="verticalSpacer">
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Qnx::Internal::BlackBerryDeviceConfigurationWizardSshKeyPage</class>
<widget class="QWizardPage" name="Qnx::Internal::BlackBerryDeviceConfigurationWizardSshKeyPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>409</width>
<height>185</height>
</rect>
</property>
<property name="windowTitle">
<string>WizardPage</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Private key file:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="publicKey">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="Utils::PathChooser" name="privateKey" native="true"/>
</item>
<item>
<widget class="QPushButton" name="generate">
<property name="text">
<string>Generate...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QProgressBar" name="progressBar">
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>-1</number>
</property>
</widget>
</item>
<item row="3" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Public key file:</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>SSH public key is used for securing a connection to a device.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Utils::PathChooser</class>
<extends>QWidget</extends>
<header location="global">utils/pathchooser.h</header>
<container>1</container>
<slots>
<signal>editingFinished()</signal>
<signal>browsingFinished()</signal>
</slots>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>generate</tabstop>
<tabstop>publicKey</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
......@@ -35,10 +35,16 @@
#include "blackberrydeviceconnection.h"
#include "qnxconstants.h"
#include <coreplugin/icore.h>
#include <projectexplorer/devicesupport/devicemanager.h>
#include <ssh/sshconnection.h>
#include <ssh/sshkeygenerator.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <QFileInfo>
#include <QDir>
using namespace Qnx;
using namespace Qnx::Internal;
......@@ -164,6 +170,59 @@ void BlackBerryDeviceConnectionManager::disconnectDevice(const ProjectExplorer::
disconnectDevice(device->id());
}
/*!
* @brief Returns default private key path in local settings.
* @return the default private key path
*/
const QString BlackBerryDeviceConnectionManager::privateKeyPath() const
{
return QFileInfo(Core::ICore::settings()->fileName()).absolutePath() + QLatin1String("/qtcreator/qnx/id_rsa");
}
/*!
* @brief Checks validity of default SSH keys used for connecting to a device.
* @return true, if the default SSH keys are valid
*/
bool BlackBerryDeviceConnectionManager::hasValidSSHKeys() const
{
const QString privateKey = privateKeyPath();
QFileInfo privateKeyFileInfo(privateKey);
QFileInfo publicKeyFileInfo(privateKey + QLatin1String(".pub"));
return privateKeyFileInfo.exists() && privateKeyFileInfo.isReadable()
&& publicKeyFileInfo.exists() && publicKeyFileInfo.isReadable();
}
/*!
* @brief Stores a new private and public SSH key in local settings.
* @param privateKeyContent the private key content
* @param publicKeyContent the public key content
*/
bool BlackBerryDeviceConnectionManager::setSSHKeys(const QByteArray privateKeyContent,
const QByteArray publicKeyContent, QString *error)
{
const QString privateKey = privateKeyPath();
const QString publicKey = privateKey + QLatin1String(".pub");
QFileInfo fileInfo(privateKey);
QDir dir = fileInfo.dir();
if (!dir.exists())
dir.mkpath(QLatin1String("."));
Utils::FileSaver privSaver(privateKey);
privSaver.write(privateKeyContent);
if (!privSaver.finalize(error))
return false;
QFile::setPermissions(privateKey, QFile::ReadOwner | QFile::WriteOwner);
Utils::FileSaver pubSaver(publicKey);
pubSaver.write(publicKeyContent);
if (!pubSaver.finalize(error))
return false;
return true;
}
void BlackBerryDeviceConnectionManager::disconnectDevice(Core::Id deviceId)
{
BlackBerryDeviceConnection *connection = m_connections.key(deviceId);
......
......@@ -60,6 +60,11 @@ public:
void connectDevice(const ProjectExplorer::IDevice::ConstPtr &device);
void disconnectDevice(const ProjectExplorer::IDevice::ConstPtr &device);
const QString privateKeyPath() const;
bool hasValidSSHKeys() const;
bool setSSHKeys(const QByteArray privateKeyContent, const QByteArray publicKeyContent,
QString *error);