diff options
Diffstat (limited to 'src/gui/databasekey')
-rw-r--r-- | src/gui/databasekey/KeyComponentWidget.cpp | 201 | ||||
-rw-r--r-- | src/gui/databasekey/KeyComponentWidget.h | 137 | ||||
-rw-r--r-- | src/gui/databasekey/KeyComponentWidget.ui | 226 | ||||
-rw-r--r-- | src/gui/databasekey/KeyFileEditWidget.cpp | 148 | ||||
-rw-r--r-- | src/gui/databasekey/KeyFileEditWidget.h | 57 | ||||
-rw-r--r-- | src/gui/databasekey/KeyFileEditWidget.ui | 94 | ||||
-rw-r--r-- | src/gui/databasekey/PasswordEditWidget.cpp | 114 | ||||
-rw-r--r-- | src/gui/databasekey/PasswordEditWidget.h | 57 | ||||
-rw-r--r-- | src/gui/databasekey/PasswordEditWidget.ui | 100 | ||||
-rw-r--r-- | src/gui/databasekey/YubiKeyEditWidget.cpp | 141 | ||||
-rw-r--r-- | src/gui/databasekey/YubiKeyEditWidget.h | 57 | ||||
-rw-r--r-- | src/gui/databasekey/YubiKeyEditWidget.ui | 96 |
12 files changed, 1428 insertions, 0 deletions
diff --git a/src/gui/databasekey/KeyComponentWidget.cpp b/src/gui/databasekey/KeyComponentWidget.cpp new file mode 100644 index 000000000..769cbab95 --- /dev/null +++ b/src/gui/databasekey/KeyComponentWidget.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "KeyComponentWidget.h" +#include "ui_KeyComponentWidget.h" + +#include <QStackedWidget> +#include <QTimer> + +KeyComponentWidget::KeyComponentWidget(QWidget* parent) + : KeyComponentWidget({}, parent) +{ +} + +KeyComponentWidget::KeyComponentWidget(const QString& name, QWidget* parent) + : QWidget(parent) + , m_ui(new Ui::KeyComponentWidget()) +{ + m_ui->setupUi(this); + + connect(m_ui->addButton, SIGNAL(clicked(bool)), SIGNAL(componentAddRequested())); + connect(m_ui->changeButton, SIGNAL(clicked(bool)), SIGNAL(componentEditRequested())); + connect(m_ui->removeButton, SIGNAL(clicked(bool)), SIGNAL(componentRemovalRequested())); + connect(m_ui->cancelButton, SIGNAL(clicked(bool)), SLOT(cancelEdit())); + + connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(resetComponentEditWidget())); + + connect(this, SIGNAL(nameChanged(QString)), SLOT(updateComponentName(QString))); + connect(this, SIGNAL(descriptionChanged(QString)), SLOT(updateComponentDescription(QString))); + connect(this, SIGNAL(componentAddRequested()), SLOT(doAdd())); + connect(this, SIGNAL(componentEditRequested()), SLOT(doEdit())); + connect(this, SIGNAL(componentRemovalRequested()), SLOT(doRemove())); + connect(this, SIGNAL(componentAddChanged(bool)), SLOT(updateAddStatus(bool))); + + bool prev = blockSignals(true); + setComponentName(name); + blockSignals(prev); + + prev = m_ui->stackedWidget->blockSignals(true); + m_ui->stackedWidget->setCurrentIndex(Page::AddNew); + m_ui->stackedWidget->blockSignals(prev); +} + +KeyComponentWidget::~KeyComponentWidget() +{ +} + +/** + * @param name display name for the key component + */ +void KeyComponentWidget::setComponentName(const QString& name) +{ + if (name == m_componentName) { + return; + } + + m_componentName = name; + emit nameChanged(name); +} + +/** + * @return The key component's display name + */ +QString KeyComponentWidget::componentName() const +{ + return m_componentName; +} + +void KeyComponentWidget::setComponentDescription(const QString& description) +{ + if (description == m_componentDescription) { + return; + } + + m_componentDescription = description; + emit descriptionChanged(description); +} + +QString KeyComponentWidget::componentDescription() const +{ + return m_componentDescription; +} + +void KeyComponentWidget::setComponentAdded(bool added) +{ + if (m_isComponentAdded == added) { + return; + } + + m_isComponentAdded = added; + emit componentAddChanged(added); +} + +bool KeyComponentWidget::componentAdded() const +{ + return m_isComponentAdded; +} + +void KeyComponentWidget::changeVisiblePage(KeyComponentWidget::Page page) +{ + m_previousPage = static_cast<Page>(m_ui->stackedWidget->currentIndex()); + m_ui->stackedWidget->setCurrentIndex(page); +} + +KeyComponentWidget::Page KeyComponentWidget::visiblePage() const +{ + return static_cast<Page>(m_ui->stackedWidget->currentIndex()); +} + +void KeyComponentWidget::updateComponentName(const QString& name) +{ + m_ui->groupBox->setTitle(name); + m_ui->addButton->setText(tr("Add %1", "Add a key component").arg(name)); + m_ui->changeButton->setText(tr("Change %1", "Change a key component").arg(name)); + m_ui->removeButton->setText(tr("Remove %1", "Remove a key component").arg(name)); + m_ui->changeOrRemoveLabel->setText( + tr("%1 set, click to change or remove", "Change or remove a key component").arg(name)); +} + +void KeyComponentWidget::updateComponentDescription(const QString& description) +{ + m_ui->componentDescription->setText(description); +} + +void KeyComponentWidget::updateAddStatus(bool added) +{ + if (added) { + m_ui->stackedWidget->setCurrentIndex(Page::LeaveOrRemove); + } else { + m_ui->stackedWidget->setCurrentIndex(Page::AddNew); + } +} + +void KeyComponentWidget::doAdd() +{ + changeVisiblePage(Page::Edit); +} + +void KeyComponentWidget::doEdit() +{ + changeVisiblePage(Page::Edit); +} + +void KeyComponentWidget::doRemove() +{ + changeVisiblePage(Page::AddNew); +} + +void KeyComponentWidget::cancelEdit() +{ + m_ui->stackedWidget->setCurrentIndex(m_previousPage); + emit editCanceled(); +} + +void KeyComponentWidget::showEvent(QShowEvent* event) +{ + resetComponentEditWidget(); + QWidget::showEvent(event); +} + +void KeyComponentWidget::resetComponentEditWidget() +{ + if (!m_componentWidget || static_cast<Page>(m_ui->stackedWidget->currentIndex()) == Page::Edit) { + if (m_componentWidget) { + delete m_componentWidget; + } + + m_componentWidget = componentEditWidget(); + m_ui->componentWidgetLayout->addWidget(m_componentWidget); + initComponentEditWidget(m_componentWidget); + } + + QTimer::singleShot(0, this, SLOT(updateSize())); +} + +void KeyComponentWidget::updateSize() +{ + for (int i = 0; i < m_ui->stackedWidget->count(); ++i) { + if (m_ui->stackedWidget->currentIndex() == i) { + m_ui->stackedWidget->widget(i)->setSizePolicy( + m_ui->stackedWidget->widget(i)->sizePolicy().horizontalPolicy(), QSizePolicy::Preferred); + } else { + m_ui->stackedWidget->widget(i)->setSizePolicy( + m_ui->stackedWidget->widget(i)->sizePolicy().horizontalPolicy(), QSizePolicy::Ignored); + } + } +} diff --git a/src/gui/databasekey/KeyComponentWidget.h b/src/gui/databasekey/KeyComponentWidget.h new file mode 100644 index 000000000..b73f881fc --- /dev/null +++ b/src/gui/databasekey/KeyComponentWidget.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef KEEPASSXC_KEYCOMPONENTWIDGET_H +#define KEEPASSXC_KEYCOMPONENTWIDGET_H + +#include <QPointer> +#include <QScopedPointer> +#include <QWidget> + +namespace Ui +{ + class KeyComponentWidget; +} +class CompositeKey; +class QStackedWidget; + +class KeyComponentWidget : public QWidget +{ + Q_OBJECT + // clang-format off + Q_PROPERTY(QString componentName READ m_componentName READ componentName + WRITE setComponentName NOTIFY nameChanged) + Q_PROPERTY(QString componentDescription READ m_componentDescription READ componentDescription + WRITE setComponentDescription NOTIFY descriptionChanged) + Q_PROPERTY(bool componentAdded READ m_isComponentAdded READ componentAdded + WRITE setComponentAdded NOTIFY componentAddChanged) + // clang-format on + +public: + enum Page + { + AddNew = 0, + Edit = 1, + LeaveOrRemove = 2 + }; + + explicit KeyComponentWidget(QWidget* parent = nullptr); + explicit KeyComponentWidget(const QString& name, QWidget* parent = nullptr); + Q_DISABLE_COPY(KeyComponentWidget); + ~KeyComponentWidget() override; + + /** + * Add the new key component to the given \link CompositeKey. + * A caller should always check first with \link validate() if + * the new key data is actually valid before adding it to a CompositeKey. + * + * @param key CompositeKey to add new key to + * @return true if added successfully + */ + virtual bool addToCompositeKey(QSharedPointer<CompositeKey> key) = 0; + + /** + * Validate key component data to check if key component + * may be added to a CompositeKey. + * + * @param errorMessage error message in case data is not valid + * @return true if data is valid + */ + virtual bool validate(QString& errorMessage) const = 0; + + void setComponentName(const QString& name); + QString componentName() const; + void setComponentDescription(const QString& name); + QString componentDescription() const; + void setComponentAdded(bool added); + bool componentAdded() const; + void changeVisiblePage(Page page); + Page visiblePage() const; + +protected: + /** + * Construct and return an instance of the key component edit widget + * which is to be inserted into the component management UI. + * Since previous widgets will be destroyed, every successive call to + * this function must return a new widget. + * + * @return edit widget instance + */ + virtual QWidget* componentEditWidget() = 0; + + /** + * Initialize the key component widget created by \link componentEditWidget(). + * This method is called every time the component edit widget is shown. + * + * @param widget pointer to the widget + */ + virtual void initComponentEditWidget(QWidget* widget) = 0; + +signals: + void nameChanged(const QString& newName); + void descriptionChanged(const QString& newDescription); + void componentAddChanged(bool added); + void componentAddRequested(); + void componentEditRequested(); + void editCanceled(); + void componentRemovalRequested(); + +protected: + void showEvent(QShowEvent* event) override; + +private slots: + void updateComponentName(const QString& name); + void updateComponentDescription(const QString& decription); + void updateAddStatus(bool added); + void doAdd(); + void doEdit(); + void doRemove(); + void cancelEdit(); + void resetComponentEditWidget(); + void updateSize(); + +private: + bool m_isComponentAdded = false; + Page m_previousPage = Page::AddNew; + QString m_componentName; + QString m_componentDescription; + QPointer<QWidget> m_componentWidget; + + const QScopedPointer<Ui::KeyComponentWidget> m_ui; +}; + +#endif // KEEPASSXC_KEYCOMPONENTWIDGET_H diff --git a/src/gui/databasekey/KeyComponentWidget.ui b/src/gui/databasekey/KeyComponentWidget.ui new file mode 100644 index 000000000..5036655e0 --- /dev/null +++ b/src/gui/databasekey/KeyComponentWidget.ui @@ -0,0 +1,226 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>KeyComponentWidget</class> + <widget class="QWidget" name="KeyComponentWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>354</width> + <height>106</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinimumSize</enum> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="styleSheet"> + <string notr="true">QGroupBox { font-weight: bold; }</string> + </property> + <property name="title"> + <string>Key Component</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <property name="spacing"> + <number>15</number> + </property> + <item> + <widget class="QStackedWidget" name="stackedWidget"> + <property name="currentIndex"> + <number>1</number> + </property> + <widget class="QWidget" name="addPage"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>10</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="componentDescription"> + <property name="text"> + <string>Key Component Description</string> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="addButton"> + <property name="text"> + <string notr="true">Add Key Component</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="editPage"> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QWidget" name="componentWidgetContainer" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QVBoxLayout" name="componentWidgetLayout"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinimumSize</enum> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="cancelButton"> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="changeOrRemovePage"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="spacing"> + <number>10</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="changeOrRemoveLabel"> + <property name="text"> + <string>Key Component set, click to change or remove</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="changeButton"> + <property name="text"> + <string notr="true">Change Key Component</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removeButton"> + <property name="text"> + <string notr="true">Remove Key Component</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/gui/databasekey/KeyFileEditWidget.cpp b/src/gui/databasekey/KeyFileEditWidget.cpp new file mode 100644 index 000000000..e0486ae07 --- /dev/null +++ b/src/gui/databasekey/KeyFileEditWidget.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "KeyFileEditWidget.h" +#include "ui_KeyFileEditWidget.h" +#include <gui/dbsettings/DatabaseSettingsWidget.h> + +#include "gui/FileDialog.h" +#include "gui/MainWindow.h" +#include "gui/MessageBox.h" +#include "keys/CompositeKey.h" +#include "keys/FileKey.h" + +KeyFileEditWidget::KeyFileEditWidget(DatabaseSettingsWidget* parent) + : KeyComponentWidget(parent) + , m_compUi(new Ui::KeyFileEditWidget()) + , m_parent(parent) +{ + setComponentName(tr("Key File")); + setComponentDescription(tr("<p>You can add a key file containing random bytes for additional security.</p>" + "<p>You must keep it secret and never lose it or you will be locked out!</p>")); +} + +KeyFileEditWidget::~KeyFileEditWidget() +{ +} + +bool KeyFileEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key) +{ + auto fileKey = QSharedPointer<FileKey>::create(); + QString fileKeyName = m_compUi->keyFileCombo->currentText(); + if (!fileKey->load(fileKeyName, nullptr)) { + return false; + } + + if (fileKey->type() != FileKey::Hashed) { + QMessageBox::warning(getMainWindow(), + tr("Legacy key file format"), + tr("You are using a legacy key file format which may become\n" + "unsupported in the future.\n\n" + "Generate a new key file in the database security settings."), + QMessageBox::Ok); + } + + key->addKey(fileKey); + return true; +} + +bool KeyFileEditWidget::validate(QString& errorMessage) const +{ + FileKey fileKey; + QString fileKeyError; + QString fileKeyName = m_compUi->keyFileCombo->currentText(); + if (!fileKey.load(fileKeyName, &fileKeyError)) { + errorMessage = tr("Error loading the key file '%1'\nMessage: %2").arg(fileKeyName, fileKeyError); + return false; + } + return true; +} + +QWidget* KeyFileEditWidget::componentEditWidget() +{ + m_compEditWidget = new QWidget(); + m_compUi->setupUi(m_compEditWidget); + + connect(m_compUi->createKeyFileButton, SIGNAL(clicked()), SLOT(createKeyFile())); + connect(m_compUi->browseKeyFileButton, SIGNAL(clicked()), SLOT(browseKeyFile())); + + return m_compEditWidget; +} + +void KeyFileEditWidget::initComponentEditWidget(QWidget* widget) +{ + Q_UNUSED(widget); + Q_ASSERT(m_compEditWidget); + m_compUi->keyFileCombo->setFocus(); +} + +void KeyFileEditWidget::createKeyFile() +{ + Q_ASSERT(m_compEditWidget); + if (!m_compEditWidget) { + return; + } + QString filters = QString("%1 (*.key);;%2 (*)").arg(tr("Key files"), tr("All files")); + QString fileName = fileDialog()->getSaveFileName(this, tr("Create Key File..."), QString(), filters); + + if (!fileName.isEmpty()) { + QString errorMsg; + bool created = FileKey::create(fileName, &errorMsg); + if (!created) { + MessageBox::critical(getMainWindow(), + tr("Error creating key file"), + tr("Unable to create key file: %1").arg(errorMsg), + QMessageBox::Button::Ok); + } else { + m_compUi->keyFileCombo->setEditText(fileName); + } + } +} + +void KeyFileEditWidget::browseKeyFile() +{ + Q_ASSERT(m_compEditWidget); + if (!m_compEditWidget) { + return; + } + QString filters = QString("%1 (*.key);;%2 (*)").arg(tr("Key files"), tr("All files")); + QString fileName = fileDialog()->getOpenFileName(this, tr("Select a key file"), QString(), filters); + + if (QFileInfo(fileName).canonicalFilePath() == m_parent->getDatabase()->canonicalFilePath()) { + MessageBox::critical(getMainWindow(), + tr("Invalid Key File"), + tr("You cannot use the current database as its own keyfile. Please choose a different " + "file or generate a new key file.")); + return; + } else if (fileName.endsWith(".kdbx", Qt::CaseInsensitive)) { + auto response = + MessageBox::warning(getMainWindow(), + tr("Suspicious Key File"), + tr("The chosen key file looks like a password database file. A key file must be a " + "static file that never changes or you will lose access to your database " + "forever.\nAre you sure you want to continue with this file?"), + MessageBox::Continue | MessageBox::Cancel, + MessageBox::Cancel); + if (response != MessageBox::Continue) { + return; + } + } + + if (!fileName.isEmpty()) { + m_compUi->keyFileCombo->setEditText(fileName); + } +} diff --git a/src/gui/databasekey/KeyFileEditWidget.h b/src/gui/databasekey/KeyFileEditWidget.h new file mode 100644 index 000000000..dd414e133 --- /dev/null +++ b/src/gui/databasekey/KeyFileEditWidget.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef KEEPASSXC_KEYFILEEDITWIDGET_H +#define KEEPASSXC_KEYFILEEDITWIDGET_H + +#include "KeyComponentWidget.h" +#include <QPointer> + +namespace Ui +{ + class KeyFileEditWidget; +} + +class DatabaseSettingsWidget; + +class KeyFileEditWidget : public KeyComponentWidget +{ + Q_OBJECT + +public: + explicit KeyFileEditWidget(DatabaseSettingsWidget* parent); + Q_DISABLE_COPY(KeyFileEditWidget); + ~KeyFileEditWidget() override; + + bool addToCompositeKey(QSharedPointer<CompositeKey> key) override; + bool validate(QString& errorMessage) const override; + +protected: + QWidget* componentEditWidget() override; + void initComponentEditWidget(QWidget* widget) override; + +private slots: + void createKeyFile(); + void browseKeyFile(); + +private: + const QScopedPointer<Ui::KeyFileEditWidget> m_compUi; + QPointer<QWidget> m_compEditWidget; + const QPointer<DatabaseSettingsWidget> m_parent; +}; + +#endif // KEEPASSXC_KEYFILEEDITWIDGET_H diff --git a/src/gui/databasekey/KeyFileEditWidget.ui b/src/gui/databasekey/KeyFileEditWidget.ui new file mode 100644 index 000000000..088995dc8 --- /dev/null +++ b/src/gui/databasekey/KeyFileEditWidget.ui @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>KeyFileEditWidget</class> + <widget class="QWidget" name="KeyFileEditWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>370</width> + <height>76</height> + </rect> + </property> + <layout class="QGridLayout" name="gridLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item row="0" column="0"> + <widget class="QComboBox" name="keyFileCombo"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="accessibleName"> + <string>Key file selection</string> + </property> + <property name="editable"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QPushButton" name="browseKeyFileButton"> + <property name="accessibleName"> + <string>Browse for key file</string> + </property> + <property name="text"> + <string>Browse...</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QPushButton" name="createKeyFileButton"> + <property name="accessibleName"> + <string>Generate a new key file</string> + </property> + <property name="text"> + <string>Generate</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="font"> + <font> + <italic>true</italic> + </font> + </property> + <property name="text"> + <string>Note: Do not use a file that may change as that will prevent you from unlocking your database!</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/gui/databasekey/PasswordEditWidget.cpp b/src/gui/databasekey/PasswordEditWidget.cpp new file mode 100644 index 000000000..2d355cc10 --- /dev/null +++ b/src/gui/databasekey/PasswordEditWidget.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "PasswordEditWidget.h" +#include "ui_PasswordEditWidget.h" + +#include "core/Resources.h" +#include "gui/PasswordGeneratorWidget.h" +#include "keys/CompositeKey.h" +#include "keys/PasswordKey.h" + +#include <QDialog> + +PasswordEditWidget::PasswordEditWidget(QWidget* parent) + : KeyComponentWidget(parent) + , m_compUi(new Ui::PasswordEditWidget()) +{ + setComponentName(tr("Password")); + setComponentDescription(tr("<p>A password is the primary method for securing your database.</p>" + "<p>Good passwords are long and unique. KeePassXC can generate one for you.</p>")); +} + +PasswordEditWidget::~PasswordEditWidget() +{ +} + +bool PasswordEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key) +{ + QString pw = m_compUi->enterPasswordEdit->text(); + if (!pw.isEmpty()) { + key->addKey(QSharedPointer<PasswordKey>::create(pw)); + return true; + } + return false; +} + +/** + * @param visible changed password visibility state + */ +void PasswordEditWidget::setPasswordVisible(bool visible) +{ + m_compUi->enterPasswordEdit->setShowPassword(visible); +} + +/** + * @return password visibility state + */ +bool PasswordEditWidget::isPasswordVisible() const +{ + return m_compUi->enterPasswordEdit->isPasswordVisible(); +} + +bool PasswordEditWidget::isEmpty() const +{ + return (visiblePage() == Page::Edit) && m_compUi->enterPasswordEdit->text().isEmpty(); +} + +QWidget* PasswordEditWidget::componentEditWidget() +{ + m_compEditWidget = new QWidget(); + m_compUi->setupUi(m_compEditWidget); + m_compUi->enterPasswordEdit->enablePasswordGenerator(); + m_compUi->enterPasswordEdit->setRepeatPartner(m_compUi->repeatPasswordEdit); + return m_compEditWidget; +} + +void PasswordEditWidget::initComponentEditWidget(QWidget* widget) +{ + Q_UNUSED(widget); + Q_ASSERT(m_compEditWidget); + m_compUi->enterPasswordEdit->setFocus(); +} + +void PasswordEditWidget::hideEvent(QHideEvent* event) +{ + if (!isVisible() && m_compUi->enterPasswordEdit) { + m_compUi->enterPasswordEdit->setText(""); + m_compUi->repeatPasswordEdit->setText(""); + } + + QWidget::hideEvent(event); +} + +bool PasswordEditWidget::validate(QString& errorMessage) const +{ + if (m_compUi->enterPasswordEdit->text() != m_compUi->repeatPasswordEdit->text()) { + errorMessage = tr("Passwords do not match."); + return false; + } + + return true; +} + +void PasswordEditWidget::setPassword(const QString& password) +{ + Q_ASSERT(m_compEditWidget); + + m_compUi->enterPasswordEdit->setText(password); + m_compUi->repeatPasswordEdit->setText(password); +} diff --git a/src/gui/databasekey/PasswordEditWidget.h b/src/gui/databasekey/PasswordEditWidget.h new file mode 100644 index 000000000..802219451 --- /dev/null +++ b/src/gui/databasekey/PasswordEditWidget.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef KEEPASSXC_PASSWORDEDITWIDGET_H +#define KEEPASSXC_PASSWORDEDITWIDGET_H + +#include "KeyComponentWidget.h" +#include <QPointer> + +namespace Ui +{ + class PasswordEditWidget; +} + +class PasswordEditWidget : public KeyComponentWidget +{ + Q_OBJECT + +public: + explicit PasswordEditWidget(QWidget* parent = nullptr); + Q_DISABLE_COPY(PasswordEditWidget); + ~PasswordEditWidget() override; + + bool addToCompositeKey(QSharedPointer<CompositeKey> key) override; + void setPasswordVisible(bool visible); + bool isPasswordVisible() const; + bool isEmpty() const; + bool validate(QString& errorMessage) const override; + +protected: + QWidget* componentEditWidget() override; + void initComponentEditWidget(QWidget* widget) override; + void hideEvent(QHideEvent* event) override; + +private slots: + void setPassword(const QString& password); + +private: + const QScopedPointer<Ui::PasswordEditWidget> m_compUi; + QPointer<QWidget> m_compEditWidget; +}; + +#endif // KEEPASSXC_PASSWORDEDITWIDGET_H diff --git a/src/gui/databasekey/PasswordEditWidget.ui b/src/gui/databasekey/PasswordEditWidget.ui new file mode 100644 index 000000000..d8382ed94 --- /dev/null +++ b/src/gui/databasekey/PasswordEditWidget.ui @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PasswordEditWidget</class> + <widget class="QWidget" name="PasswordEditWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>571</width> + <height>78</height> + </rect> + </property> + <layout class="QFormLayout" name="formLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="enterPasswordLabel"> + <property name="text"> + <string>Enter password:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="PasswordEdit" name="enterPasswordEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>300</width> + <height>0</height> + </size> + </property> + <property name="accessibleName"> + <string>Password field</string> + </property> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="repeatPasswordLabel"> + <property name="text"> + <string>Confirm password:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="PasswordEdit" name="repeatPasswordEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>300</width> + <height>0</height> + </size> + </property> + <property name="accessibleName"> + <string>Repeat password field</string> + </property> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>PasswordEdit</class> + <extends>QLineEdit</extends> + <header>gui/PasswordEdit.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>enterPasswordEdit</tabstop> + <tabstop>repeatPasswordEdit</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/src/gui/databasekey/YubiKeyEditWidget.cpp b/src/gui/databasekey/YubiKeyEditWidget.cpp new file mode 100644 index 000000000..855bd7099 --- /dev/null +++ b/src/gui/databasekey/YubiKeyEditWidget.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "YubiKeyEditWidget.h" +#include "ui_YubiKeyEditWidget.h" + +#include "config-keepassx.h" +#include "core/AsyncTask.h" +#include "gui/MainWindow.h" +#include "gui/MessageBox.h" +#include "keys/CompositeKey.h" +#include "keys/YkChallengeResponseKey.h" + +YubiKeyEditWidget::YubiKeyEditWidget(QWidget* parent) + : KeyComponentWidget(parent) + , m_compUi(new Ui::YubiKeyEditWidget()) +{ + setComponentName(tr("YubiKey Challenge-Response")); + setComponentDescription( + tr("<p>If you own a <a href=\"https://www.yubico.com/\">YubiKey</a>, you can use it " + "for additional security.</p><p>The YubiKey requires one of its slots to be programmed as " + "<a href=\"https://www.yubico.com/products/services-software/personalization-tools/challenge-response/\">" + "HMAC-SHA1 Challenge-Response</a>.</p>")); + + connect(YubiKey::instance(), SIGNAL(detectComplete(bool)), SLOT(hardwareKeyResponse(bool)), Qt::QueuedConnection); +} + +YubiKeyEditWidget::~YubiKeyEditWidget() +{ +} + +bool YubiKeyEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key) +{ + if (!m_isDetected || !m_compEditWidget) { + return false; + } + + int selectionIndex = m_compUi->comboChallengeResponse->currentIndex(); + auto slot = m_compUi->comboChallengeResponse->itemData(selectionIndex).value<YubiKeySlot>(); + key->addChallengeResponseKey(QSharedPointer<YkChallengeResponseKey>::create(slot)); + return true; +} + +bool YubiKeyEditWidget::validate(QString& errorMessage) const +{ + if (!m_isDetected) { + errorMessage = tr("Could not find any hardware keys!"); + return false; + } + + // Perform a test challenge response + int selectionIndex = m_compUi->comboChallengeResponse->currentIndex(); + auto slot = m_compUi->comboChallengeResponse->itemData(selectionIndex).value<YubiKeySlot>(); + bool valid = AsyncTask::runAndWaitForFuture([&slot] { return YubiKey::instance()->testChallenge(slot); }); + if (!valid) { + errorMessage = tr("Selected hardware key slot does not support challenge-response!"); + } + return valid; +} + +QWidget* YubiKeyEditWidget::componentEditWidget() +{ + m_compEditWidget = new QWidget(); + m_compUi->setupUi(m_compEditWidget); + + QSizePolicy sp = m_compUi->yubikeyProgress->sizePolicy(); + sp.setRetainSizeWhenHidden(true); + m_compUi->yubikeyProgress->setSizePolicy(sp); + m_compUi->yubikeyProgress->setVisible(false); + +#ifdef WITH_XC_YUBIKEY + connect(m_compUi->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey())); + pollYubikey(); +#endif + + return m_compEditWidget; +} + +void YubiKeyEditWidget::initComponentEditWidget(QWidget* widget) +{ + Q_UNUSED(widget); + Q_ASSERT(m_compEditWidget); + m_compUi->comboChallengeResponse->setFocus(); +} + +void YubiKeyEditWidget::pollYubikey() +{ +#ifdef WITH_XC_YUBIKEY + if (!m_compEditWidget) { + return; + } + + m_isDetected = false; + m_compUi->comboChallengeResponse->clear(); + m_compUi->comboChallengeResponse->addItem(tr("Detecting hardware keys…")); + m_compUi->buttonRedetectYubikey->setEnabled(false); + m_compUi->comboChallengeResponse->setEnabled(false); + m_compUi->yubikeyProgress->setVisible(true); + + YubiKey::instance()->findValidKeys(); +#endif +} + +void YubiKeyEditWidget::hardwareKeyResponse(bool found) +{ + if (!m_compEditWidget) { + return; + } + + m_compUi->comboChallengeResponse->clear(); + m_compUi->buttonRedetectYubikey->setEnabled(true); + m_compUi->yubikeyProgress->setVisible(false); + + if (!found) { + m_compUi->comboChallengeResponse->addItem(tr("No hardware keys detected")); + m_isDetected = false; + return; + } + + for (auto& slot : YubiKey::instance()->foundKeys()) { + // add detected YubiKey to combo box and encode blocking mode in LSB, slot number in second LSB + m_compUi->comboChallengeResponse->addItem(YubiKey::instance()->getDisplayName(slot), QVariant::fromValue(slot)); + } + + m_isDetected = true; + m_compUi->comboChallengeResponse->setEnabled(true); +} diff --git a/src/gui/databasekey/YubiKeyEditWidget.h b/src/gui/databasekey/YubiKeyEditWidget.h new file mode 100644 index 000000000..d5be0a683 --- /dev/null +++ b/src/gui/databasekey/YubiKeyEditWidget.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef KEEPASSXC_YUBIKEYEDITWIDGET_H +#define KEEPASSXC_YUBIKEYEDITWIDGET_H + +#include "KeyComponentWidget.h" +#include <QPointer> + +namespace Ui +{ + class YubiKeyEditWidget; +} + +class YkChallengeResponseKey; + +class YubiKeyEditWidget : public KeyComponentWidget +{ + Q_OBJECT + +public: + explicit YubiKeyEditWidget(QWidget* parent = nullptr); + Q_DISABLE_COPY(YubiKeyEditWidget); + ~YubiKeyEditWidget() override; + + bool addToCompositeKey(QSharedPointer<CompositeKey> key) override; + bool validate(QString& errorMessage) const override; + +protected: + QWidget* componentEditWidget() override; + void initComponentEditWidget(QWidget* widget) override; + +private slots: + void hardwareKeyResponse(bool found); + void pollYubikey(); + +private: + const QScopedPointer<Ui::YubiKeyEditWidget> m_compUi; + QPointer<QWidget> m_compEditWidget; + bool m_isDetected = false; +}; + +#endif // KEEPASSXC_YUBIKEYEDITWIDGET_H diff --git a/src/gui/databasekey/YubiKeyEditWidget.ui b/src/gui/databasekey/YubiKeyEditWidget.ui new file mode 100644 index 000000000..fa150084b --- /dev/null +++ b/src/gui/databasekey/YubiKeyEditWidget.ui @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>YubiKeyEditWidget</class> + <widget class="QWidget" name="YubiKeyEditWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>381</width> + <height>64</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QGridLayout" name="gridLayout_4"> + <property name="verticalSpacing"> + <number>0</number> + </property> + <item row="0" column="1"> + <widget class="QPushButton" name="buttonRedetectYubikey"> + <property name="accessibleName"> + <string>Refresh hardware tokens</string> + </property> + <property name="text"> + <string>Refresh</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QComboBox" name="comboChallengeResponse"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="accessibleName"> + <string>Hardware key slot selection</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QProgressBar" name="yubikeyProgress"> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>2</height> + </size> + </property> + <property name="maximum"> + <number>0</number> + </property> + <property name="value"> + <number>-1</number> + </property> + <property name="textVisible"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <tabstops> + <tabstop>comboChallengeResponse</tabstop> + <tabstop>buttonRedetectYubikey</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> |