Skip to content

Commit

Permalink
Add support for importing Passkey to entry
Browse files Browse the repository at this point in the history
  • Loading branch information
varjolintu committed Nov 4, 2023
1 parent 454dc71 commit aa8adb1
Show file tree
Hide file tree
Showing 20 changed files with 345 additions and 130 deletions.
17 changes: 17 additions & 0 deletions share/translations/keepassxc_en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,15 @@ Do you want to delete the entry?
<source>%1 (Passkey)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>KeePassXC: Update Passkey</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Entry already has a Passkey.
Do you want to overwrite the Passkey in %1 - %2?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>BrowserSettingsWidget</name>
Expand Down Expand Up @@ -6040,6 +6049,14 @@ Do you want to overwrite it?
<source>Group:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Import to entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Do you want to import the Passkey to this entry?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PasskeyImporter</name>
Expand Down
39 changes: 36 additions & 3 deletions src/browser/BrowserService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,20 @@ void BrowserService::addPasskeyToEntry(Entry* entry,
return;
}

// Ask confirmation if entry already contains a Passkey
if (entry->hasPasskey()) {
if (MessageBox::question(
m_currentDatabaseWidget,
tr("KeePassXC: Update Passkey"),
tr("Entry already has a Passkey.\nDo you want to overwrite the Passkey in %1 - %2?")
.arg(entry->title(), entry->attributes()->value(BrowserPasskeys::KPEX_PASSKEY_USERNAME)),
MessageBox::Overwrite | MessageBox::Cancel,
MessageBox::Cancel)
!= MessageBox::Overwrite) {
return;
}
}

entry->beginUpdate();

entry->attributes()->set(BrowserPasskeys::KPEX_PASSKEY_USERNAME, username);
Expand Down Expand Up @@ -1289,8 +1303,7 @@ QList<Entry*> BrowserService::getPasskeyEntries(const QString& rpId, const Strin
{
QList<Entry*> entries;
for (const auto& entry : searchEntries(rpId, "", keyList, true)) {
if (entry->attributes()->hasKey(BrowserPasskeys::KPEX_PASSKEY_PRIVATE_KEY_PEM)
&& entry->attributes()->value(BrowserPasskeys::KPEX_PASSKEY_RELYING_PARTY) == rpId) {
if (entry->hasPasskey() && entry->attributes()->value(BrowserPasskeys::KPEX_PASSKEY_RELYING_PARTY) == rpId) {
entries << entry;
}
}
Expand Down Expand Up @@ -1417,14 +1430,34 @@ bool BrowserService::handleURL(const QString& entryUrl,
return false;
}

QSharedPointer<Database> BrowserService::getDatabase()
QSharedPointer<Database> BrowserService::getDatabase(const QUuid& rootGroupUuid)
{
if (!rootGroupUuid.isNull()) {
const auto openDatabases = getOpenDatabases();
for (const auto& db : openDatabases) {
if (db->rootGroup()->uuid() == rootGroupUuid) {
return db;
}
}
}

if (m_currentDatabaseWidget) {
return m_currentDatabaseWidget->database();
}
return {};
}

QList<QSharedPointer<Database>> BrowserService::getOpenDatabases()
{
QList<QSharedPointer<Database>> databaseList;
for (auto dbWidget : getMainWindow()->getOpenDatabases()) {
if (!dbWidget->isLocked()) {
databaseList << dbWidget->database();
}
}
return databaseList;
}

QSharedPointer<Database> BrowserService::selectedDatabase()
{
QList<DatabaseWidget*> databaseWidgets;
Expand Down
3 changes: 2 additions & 1 deletion src/browser/BrowserService.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ class BrowserService : public QObject
void showPasswordGenerator(const KeyPairMessage& keyPairMessage);
bool isPasswordGeneratorRequested() const;
bool isUrlIdentical(const QString& first, const QString& second) const;
QSharedPointer<Database> getDatabase(const QUuid& rootGroupUuid = {});
QSharedPointer<Database> selectedDatabase();
QList<QSharedPointer<Database>> getOpenDatabases();
#ifdef WITH_XC_BROWSER_PASSKEYS
QJsonObject
showPasskeysRegisterPrompt(const QJsonObject& publicKey, const QString& origin, const StringPairList& keyList);
Expand Down Expand Up @@ -192,7 +194,6 @@ private slots:
const QString& siteUrl,
const QString& formUrl,
const bool omitWwwSubdomain = false);
QSharedPointer<Database> getDatabase();
QString getDatabaseRootUuid();
QString getDatabaseRecycleBinUuid();
bool checkLegacySettings(QSharedPointer<Database> db);
Expand Down
5 changes: 5 additions & 0 deletions src/core/Entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,11 @@ bool Entry::hasTotp() const
return !m_data.totpSettings.isNull();
}

bool Entry::hasPasskey() const
{
return m_attributes->hasPasskey();
}

QString Entry::totp() const
{
if (hasTotp()) {
Expand Down
1 change: 1 addition & 0 deletions src/core/Entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class Entry : public ModifiableObject
void setExcludeFromReports(bool state);

bool hasTotp() const;
bool hasPasskey() const;
bool isExpired() const;
bool willExpireInDays(int days) const;
bool isRecycled() const;
Expand Down
12 changes: 12 additions & 0 deletions src/core/EntryAttributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ bool EntryAttributes::hasKey(const QString& key) const
return m_attributes.contains(key);
}

bool EntryAttributes::hasPasskey() const
{
const auto keyList = keys();
for (const auto& key : keyList) {
if (isPasskeyAttribute(key)) {
return true;
}
}

return false;
}

QList<QString> EntryAttributes::customKeys() const
{
QList<QString> customKeys;
Expand Down
3 changes: 2 additions & 1 deletion src/core/EntryAttributes.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2023 KeePassXC Team <[email protected]>
* Copyright (C) 2012 Felix Geyer <[email protected]>
* Copyright (C) 2017 KeePassXC Team <[email protected]>
*
* 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
Expand Down Expand Up @@ -33,6 +33,7 @@ class EntryAttributes : public ModifiableObject
explicit EntryAttributes(QObject* parent = nullptr);
QList<QString> keys() const;
bool hasKey(const QString& key) const;
bool hasPasskey() const;
QList<QString> customKeys() const;
QString value(const QString& key) const;
QList<QString> values(const QList<QString>& keys) const;
Expand Down
7 changes: 6 additions & 1 deletion src/gui/DatabaseTabWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,12 @@ void DatabaseTabWidget::showPasskeys()

void DatabaseTabWidget::importPasskey()
{
currentDatabaseWidget()->switchToImportPasskey();
currentDatabaseWidget()->showImportPasskeyDialog();
}

void DatabaseTabWidget::importPasskeyToEntry()
{
currentDatabaseWidget()->showImportPasskeyDialog(true);
}
#endif

Expand Down
1 change: 1 addition & 0 deletions src/gui/DatabaseTabWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public slots:
#ifdef WITH_XC_BROWSER_PASSKEYS
void showPasskeys();
void importPasskey();
void importPasskeyToEntry();
#endif
void performGlobalAutoType(const QString& search);
void performBrowserUnlock();
Expand Down
14 changes: 12 additions & 2 deletions src/gui/DatabaseWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1407,10 +1407,20 @@ void DatabaseWidget::switchToPasskeys()
m_reportsDialog->activatePasskeysPage();
}

void DatabaseWidget::switchToImportPasskey()
void DatabaseWidget::showImportPasskeyDialog(bool isEntry)
{
PasskeyImporter passkeyImporter;
passkeyImporter.importPasskey(m_db);

if (isEntry) {
auto currentEntry = currentSelectedEntry();
if (!currentEntry) {
return;
}

passkeyImporter.importPasskey(m_db, currentEntry);
} else {
passkeyImporter.importPasskey(m_db);
}
}
#endif

Expand Down
2 changes: 1 addition & 1 deletion src/gui/DatabaseWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public slots:
void switchToDatabaseSettings();
#ifdef WITH_XC_BROWSER_PASSKEYS
void switchToPasskeys();
void switchToImportPasskey();
void showImportPasskeyDialog(bool isEntry = false);
#endif
void switchToOpenDatabase();
void switchToOpenDatabase(const QString& filePath);
Expand Down
9 changes: 9 additions & 0 deletions src/gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ MainWindow::MainWindow()
m_entryContextMenu->addSeparator();
m_entryContextMenu->addAction(m_ui->actionEntryAutoType);
m_entryContextMenu->addSeparator();
#ifdef WITH_XC_BROWSER_PASSKEYS
m_entryContextMenu->addAction(m_ui->actionEntryImportPasskey);
m_entryContextMenu->addSeparator();
#endif
m_entryContextMenu->addAction(m_ui->actionEntryEdit);
m_entryContextMenu->addAction(m_ui->actionEntryClone);
m_entryContextMenu->addAction(m_ui->actionEntryDelete);
Expand Down Expand Up @@ -441,6 +445,7 @@ MainWindow::MainWindow()
#ifdef WITH_XC_BROWSER_PASSKEYS
m_ui->actionPasskeys->setIcon(icons()->icon("passkey"));
m_ui->actionImportPasskey->setIcon(icons()->icon("document-import"));
m_ui->actionEntryImportPasskey->setIcon(icons()->icon("document-import"));
#endif

m_actionMultiplexer.connect(
Expand Down Expand Up @@ -491,6 +496,7 @@ MainWindow::MainWindow()
#ifdef WITH_XC_BROWSER_PASSKEYS
connect(m_ui->actionPasskeys, SIGNAL(triggered()), m_ui->tabWidget, SLOT(showPasskeys()));
connect(m_ui->actionImportPasskey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importPasskey()));
connect(m_ui->actionEntryImportPasskey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importPasskeyToEntry()));
#endif
connect(m_ui->actionImportCsv, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importCsv()));
connect(m_ui->actionImportKeePass1, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importKeePass1Database()));
Expand Down Expand Up @@ -989,6 +995,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
#ifdef WITH_XC_BROWSER_PASSKEYS
m_ui->actionPasskeys->setEnabled(true);
m_ui->actionImportPasskey->setEnabled(true);
m_ui->actionEntryImportPasskey->setEnabled(true);
#endif
#ifdef WITH_XC_SSHAGENT
bool singleEntryHasSshKey =
Expand Down Expand Up @@ -1060,9 +1067,11 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
#ifdef WITH_XC_BROWSER_PASSKEYS
m_ui->actionPasskeys->setEnabled(false);
m_ui->actionImportPasskey->setEnabled(false);
m_ui->actionEntryImportPasskey->setEnabled(false);
#else
m_ui->actionPasskeys->setVisible(false);
m_ui->actionImportPasskey->setVisible(false);
m_ui->actionEntryImportPasskey->setVisible(false);
#endif

m_searchWidgetAction->setEnabled(false);
Expand Down
10 changes: 10 additions & 0 deletions src/gui/MainWindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@
<addaction name="separator"/>
<addaction name="actionEntryAutoType"/>
<addaction name="separator"/>
<addaction name="actionEntryImportPasskey"/>
<addaction name="separator"/>
<addaction name="actionEntryOpenUrl"/>
<addaction name="actionEntryDownloadIcon"/>
<addaction name="separator"/>
Expand Down Expand Up @@ -730,6 +732,14 @@
<string>Perform &amp;Auto-Type</string>
</property>
</action>
<action name="actionEntryImportPasskey">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Import Passkey</string>
</property>
</action>
<action name="actionEntryAutoTypeUsername">
<property name="enabled">
<bool>false</bool>
Expand Down
Loading

0 comments on commit aa8adb1

Please sign in to comment.