diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index 48e3d45e7..6192f8d1c 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -2505,6 +2505,14 @@ Disable safe saves and try again? Database tab name modifier + + Remove passkey from entry + + + + Do you want to remove the passkey from this entry? + + EditEntryWidget @@ -3547,6 +3555,10 @@ This may cause the affected plugins to malfunction. %1 - Clone + + Passkey + + EntryAttachments @@ -5867,6 +5879,10 @@ We recommend you use the AppImage available on our downloads page. Toggle Allow Screen Capture + + Remove Passkey From Entry + + ManageDatabase diff --git a/src/browser/BrowserSettingsWidget.ui b/src/browser/BrowserSettingsWidget.ui index facabfa4a..495ad36ed 100644 --- a/src/browser/BrowserSettingsWidget.ui +++ b/src/browser/BrowserSettingsWidget.ui @@ -497,7 +497,7 @@ - + diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index b57fd78f1..49f3dfb5d 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 KeePassXC Team + * Copyright (C) 2024 KeePassXC Team * Copyright (C) 2010 Felix Geyer * * This program is free software: you can redistribute it and/or modify @@ -551,6 +551,12 @@ bool Entry::hasPasskey() const return m_attributes->hasPasskey(); } +void Entry::removePasskey() +{ + m_attributes->removePasskeyAttributes(); + removeTag(tr("Passkey")); +} + QString Entry::totp() const { if (hasTotp()) { diff --git a/src/core/Entry.h b/src/core/Entry.h index 3a03892d0..27d8293e3 100644 --- a/src/core/Entry.h +++ b/src/core/Entry.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 KeePassXC Team + * Copyright (C) 2024 KeePassXC Team * Copyright (C) 2010 Felix Geyer * * This program is free software: you can redistribute it and/or modify @@ -120,8 +120,10 @@ public: bool excludeFromReports() const; void setExcludeFromReports(bool state); - bool hasTotp() const; bool hasPasskey() const; + void removePasskey(); + + bool hasTotp() const; bool isExpired() const; bool willExpireInDays(int days) const; bool isRecycled() const; diff --git a/src/core/EntryAttributes.cpp b/src/core/EntryAttributes.cpp index 49c243ec1..a14ae7dde 100644 --- a/src/core/EntryAttributes.cpp +++ b/src/core/EntryAttributes.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 KeePassXC Team + * Copyright (C) 2024 KeePassXC Team * Copyright (C) 2012 Felix Geyer * * This program is free software: you can redistribute it and/or modify @@ -65,6 +65,16 @@ bool EntryAttributes::hasPasskey() const return false; } +void EntryAttributes::removePasskeyAttributes() +{ + const auto keyList = keys(); + for (const auto& key : keyList) { + if (isPasskeyAttribute(key)) { + remove(key); + } + } +} + QList EntryAttributes::customKeys() const { QList customKeys; diff --git a/src/core/EntryAttributes.h b/src/core/EntryAttributes.h index fecf6a993..ee814b03d 100644 --- a/src/core/EntryAttributes.h +++ b/src/core/EntryAttributes.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 KeePassXC Team + * Copyright (C) 2024 KeePassXC Team * Copyright (C) 2012 Felix Geyer * * This program is free software: you can redistribute it and/or modify @@ -34,6 +34,7 @@ public: QList keys() const; bool hasKey(const QString& key) const; bool hasPasskey() const; + void removePasskeyAttributes(); QList customKeys() const; QString value(const QString& key) const; QList values(const QList& keys) const; diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 1333b5fef..d98be1ef7 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 KeePassXC Team + * Copyright (C) 2024 KeePassXC Team * * 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 @@ -563,6 +563,11 @@ void DatabaseTabWidget::importPasskeyToEntry() { currentDatabaseWidget()->showImportPasskeyDialog(true); } + +void DatabaseTabWidget::removePasskeyFromEntry() +{ + currentDatabaseWidget()->removePasskeyFromEntry(); +} #endif bool DatabaseTabWidget::isModified(int index) const diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index 5c995b536..59e555451 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 KeePassXC Team + * Copyright (C) 2024 KeePassXC Team * * 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 @@ -87,6 +87,7 @@ public slots: void showPasskeys(); void importPasskey(); void importPasskeyToEntry(); + void removePasskeyFromEntry(); #endif void performGlobalAutoType(const QString& search); void performBrowserUnlock(); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 5e7f0c4b0..0311ade5b 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -1428,6 +1428,22 @@ void DatabaseWidget::showImportPasskeyDialog(bool isEntry) passkeyImporter.importPasskey(m_db); } } + +void DatabaseWidget::removePasskeyFromEntry() +{ + auto currentEntry = currentSelectedEntry(); + if (!currentEntry) { + return; + } + + auto result = MessageBox::question(this, + tr("Remove passkey from entry"), + tr("Do you want to remove the passkey from this entry?"), + MessageBox::Remove | MessageBox::Cancel); + if (result == MessageBox::Remove) { + currentEntry->removePasskey(); + } +} #endif void DatabaseWidget::performUnlockDatabase(const QString& password, const QString& keyfile) @@ -2020,6 +2036,14 @@ bool DatabaseWidget::currentEntryHasSshKey() } #endif +#ifdef WITH_XC_BROWSER_PASSKEYS +bool DatabaseWidget::currentEntryHasPasskey() +{ + auto currentEntry = m_entryView->currentEntry(); + return currentEntry && currentEntry->hasPasskey(); +} +#endif + bool DatabaseWidget::currentEntryHasNotes() { auto currentEntry = currentSelectedEntry(); diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 681260009..0b306d91d 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 KeePassXC Team + * Copyright (C) 2024 KeePassXC Team * Copyright (C) 2010 Felix Geyer * * This program is free software: you can redistribute it and/or modify @@ -212,6 +212,8 @@ public slots: #ifdef WITH_XC_BROWSER_PASSKEYS void switchToPasskeys(); void showImportPasskeyDialog(bool isEntry = false); + void removePasskeyFromEntry(); + bool currentEntryHasPasskey(); #endif void switchToOpenDatabase(); void switchToOpenDatabase(const QString& filePath); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index c89e08f0b..6e9bd2a29 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 KeePassXC Team + * Copyright (C) 2024 KeePassXC Team * Copyright (C) 2010 Felix Geyer * * This program is free software: you can redistribute it and/or modify @@ -139,6 +139,7 @@ MainWindow::MainWindow() m_entryContextMenu->addSeparator(); #ifdef WITH_XC_BROWSER_PASSKEYS m_entryContextMenu->addAction(m_ui->actionEntryImportPasskey); + m_entryContextMenu->addAction(m_ui->actionEntryRemovePasskey); m_entryContextMenu->addSeparator(); #endif m_entryContextMenu->addAction(m_ui->actionEntryEdit); @@ -412,6 +413,7 @@ MainWindow::MainWindow() m_ui->actionPasskeys->setIcon(icons()->icon("passkey")); m_ui->actionImportPasskey->setIcon(icons()->icon("document-import")); m_ui->actionEntryImportPasskey->setIcon(icons()->icon("document-import")); + m_ui->actionEntryRemovePasskey->setIcon(icons()->icon("document-close")); #endif m_actionMultiplexer.connect( @@ -463,6 +465,7 @@ MainWindow::MainWindow() 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())); + connect(m_ui->actionEntryRemovePasskey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(removePasskeyFromEntry())); #endif connect(m_ui->actionImport, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importFile())); connect(m_ui->actionExportCsv, SIGNAL(triggered()), m_ui->tabWidget, SLOT(exportToCsv())); @@ -956,9 +959,11 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionExportXML->setEnabled(true); m_ui->actionDatabaseMerge->setEnabled(m_ui->tabWidget->currentIndex() != -1); #ifdef WITH_XC_BROWSER_PASSKEYS + bool singleEntryHasPasskey = singleEntrySelected && dbWidget->currentEntryHasPasskey(); m_ui->actionPasskeys->setEnabled(true); m_ui->actionImportPasskey->setEnabled(true); m_ui->actionEntryImportPasskey->setEnabled(singleEntrySelected); + m_ui->actionEntryRemovePasskey->setEnabled(singleEntryHasPasskey); #endif #ifdef WITH_XC_SSHAGENT bool singleEntryHasSshKey = @@ -1030,10 +1035,12 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionPasskeys->setEnabled(false); m_ui->actionImportPasskey->setEnabled(false); m_ui->actionEntryImportPasskey->setEnabled(false); + m_ui->actionEntryRemovePasskey->setEnabled(false); #else m_ui->actionPasskeys->setVisible(false); m_ui->actionImportPasskey->setVisible(false); m_ui->actionEntryImportPasskey->setVisible(false); + m_ui->actionEntryRemovePasskey->setVisible(false); #endif m_searchWidgetAction->setEnabled(false); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index 69606b856..4cc82114c 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -335,6 +335,7 @@ + @@ -775,6 +776,14 @@ Import Passkey + + + false + + + Remove Passkey From Entry + + false diff --git a/src/gui/SearchHelpWidget.ui b/src/gui/SearchHelpWidget.ui index 907b2822d..d9778fd22 100644 --- a/src/gui/SearchHelpWidget.ui +++ b/src/gui/SearchHelpWidget.ui @@ -388,7 +388,7 @@ - + 0