feat: add delete subkey function
Some checks failed
Develop CI Qt6 / build (push) Failing after 1m25s
Develop CI Qt5 / build (push) Failing after 1m31s

This commit is contained in:
saturneric 2024-11-18 17:53:29 +01:00
parent 94281700b1
commit a5d7cc6aa2
7 changed files with 443 additions and 63 deletions

View File

@ -94,60 +94,55 @@ auto GpgFrontend::GpgKeyManager::SetExpire(
auto GpgFrontend::GpgKeyManager::SetOwnerTrustLevel(const GpgKey& key,
int trust_level) -> bool {
if (trust_level < 0 || trust_level > 5) {
if (trust_level < 1 || trust_level > 5) {
FLOG_W("illegal owner trust level: %d", trust_level);
}
AutomatonNextStateHandler next_state_handler = [](AutomatonState state,
QString status,
QString args) {
LOG_D() << "next_state_handler state: " << static_cast<unsigned int>(state)
<< ", gpg_status: " << status << ", args: " << args;
AutomatonNextStateHandler next_state_handler =
[](AutomatonState state, QString status, QString args) {
auto tokens = args.split(' ');
auto tokens = args.split(' ');
switch (state) {
case AS_START:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_COMMAND;
}
return AS_ERROR;
case AS_COMMAND:
if (status == "GET_LINE" && args == "edit_ownertrust.value") {
return AS_VALUE;
}
return AS_ERROR;
case AS_VALUE:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_QUIT;
} else if (status == "GET_BOOL" &&
args == "edit_ownertrust.set_ultimate.okay") {
return AS_REALLY_ULTIMATE;
}
return AS_ERROR;
case AS_REALLY_ULTIMATE:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_QUIT;
}
return AS_ERROR;
case AS_QUIT:
if (status == "GET_LINE" && args == "keyedit.save.okay") {
return AS_SAVE;
}
return AS_ERROR;
case AS_ERROR:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_QUIT;
}
return AS_ERROR;
default:
return AS_ERROR;
};
};
switch (state) {
case AS_START:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_COMMAND;
}
return AS_ERROR;
case AS_COMMAND:
if (status == "GET_LINE" && args == "edit_ownertrust.value") {
return AS_VALUE;
}
return AS_ERROR;
case AS_VALUE:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_QUIT;
} else if (status == "GET_BOOL" &&
args == "edit_ownertrust.set_ultimate.okay") {
return AS_REALLY_ULTIMATE;
}
return AS_ERROR;
case AS_REALLY_ULTIMATE:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_QUIT;
}
return AS_ERROR;
case AS_QUIT:
if (status == "GET_BOOL" && args == "keyedit.save.okay") {
return AS_SAVE;
}
return AS_ERROR;
case AS_ERROR:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_QUIT;
}
return AS_ERROR;
default:
return AS_ERROR;
};
};
AutomatonActionHandler action_handler =
[trust_level](AutomatonHandelStruct& handler, AutomatonState state) {
FLOG_D("action_handler state: %d", static_cast<unsigned int>(state));
switch (state) {
case AS_COMMAND:
return QString("trust");
@ -210,11 +205,15 @@ auto GpgFrontend::GpgKeyManager::interactor_cb_fnc(void* handle,
return 0;
}
LOG_D() << "current state" << handle_struct->CurrentStatus()
<< "gpg status: " << status_s << ", args: " << args_s;
AutomatonState next_state = handle_struct->NextState(status_s, args_s);
if (next_state == AS_ERROR) {
FLOG_D("handle struct next state caught error, skipping...");
return GPG_ERR_FALSE;
FLOG_D("handle struct next state caught error, abort...");
return -1;
}
LOG_D() << "next state" << next_state;
if (next_state == AS_SAVE) {
handle_struct->SetSuccess(true);
@ -224,6 +223,8 @@ auto GpgFrontend::GpgKeyManager::interactor_cb_fnc(void* handle,
handle_struct->SetStatus(next_state);
Command cmd = handle_struct->Action();
LOG_D() << "next action, cmd:" << cmd;
if (!cmd.isEmpty()) {
auto btye_array = cmd.toUtf8();
gpgme_io_write(fd, btye_array, btye_array.size());
@ -234,4 +235,89 @@ auto GpgFrontend::GpgKeyManager::interactor_cb_fnc(void* handle,
}
return 0;
}
}
auto GpgFrontend::GpgKeyManager::DeleteSubkey(const GpgKey& key,
int subkey_index) -> bool {
if (subkey_index < 0 || subkey_index >= key.GetSubKeys()->size()) {
LOG_W() << "illegal subkey index: " << subkey_index;
}
AutomatonNextStateHandler next_state_handler =
[](AutomatonState state, QString status, QString args) {
auto tokens = args.split(' ');
switch (state) {
case AS_START:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_SELECT;
}
return AS_ERROR;
case AS_SELECT:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_COMMAND;
}
return AS_ERROR;
case AS_COMMAND:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_QUIT;
} else if (status == "GET_BOOL" &&
args == "keyedit.remove.subkey.okay") {
return AS_REALLY_ULTIMATE;
}
return AS_ERROR;
case AS_REALLY_ULTIMATE:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_QUIT;
}
return AS_ERROR;
case AS_QUIT:
if (status == "GET_BOOL" && args == "keyedit.save.okay") {
return AS_SAVE;
}
return AS_ERROR;
case AS_ERROR:
if (status == "GET_LINE" && args == "keyedit.prompt") {
return AS_QUIT;
}
return AS_ERROR;
default:
return AS_ERROR;
};
};
AutomatonActionHandler action_handler =
[subkey_index](AutomatonHandelStruct& handler, AutomatonState state) {
switch (state) {
case AS_SELECT:
return QString("key %1").arg(subkey_index);
case AS_COMMAND:
return QString("delkey");
case AS_REALLY_ULTIMATE:
handler.SetSuccess(true);
return QString("Y");
case AS_QUIT:
return QString("quit");
case AS_SAVE:
handler.SetSuccess(true);
return QString("Y");
case AS_START:
case AS_ERROR:
return QString("");
default:
return QString("");
}
return QString("");
};
auto key_fpr = key.GetFingerprint();
AutomatonHandelStruct handel_struct(key_fpr);
handel_struct.SetHandler(next_state_handler, action_handler);
GpgData data_out;
auto err =
gpgme_op_interact(ctx_.DefaultContext(), static_cast<gpgme_key_t>(key), 0,
GpgKeyManager::interactor_cb_fnc,
static_cast<void*>(&handel_struct), data_out);
return CheckGpgError(err) == GPG_ERR_NO_ERROR && handel_struct.Success();
}

View File

@ -91,6 +91,16 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyManager
*/
auto SetOwnerTrustLevel(const GpgKey& key, int trust_level) -> bool;
/**
* @brief
*
* @param key
* @param subkey_index
* @return true
* @return false
*/
auto DeleteSubkey(const GpgKey& key, int subkey_index) -> bool;
private:
static auto interactor_cb_fnc(void* handle, const char* status,
const char* args, int fd) -> gpgme_error_t;
@ -98,6 +108,7 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyManager
using Command = QString;
using AutomatonState = enum {
AS_START,
AS_SELECT,
AS_COMMAND,
AS_VALUE,
AS_REALLY_ULTIMATE,
@ -115,7 +126,7 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyManager
struct AutomatonHandelStruct {
void SetStatus(AutomatonState next_state) { current_state_ = next_state; }
auto CuurentStatus() -> AutomatonState { return current_state_; }
auto CurrentStatus() -> AutomatonState { return current_state_; }
void SetHandler(AutomatonNextStateHandler next_state_handler,
AutomatonActionHandler action_handler) {
next_state_handler_ = std::move(next_state_handler);

View File

@ -0,0 +1,216 @@
/**
* Copyright (C) 2021-2024 Saturneric <eric@bktus.com>
*
* This file is part of GpgFrontend.
*
* GpgFrontend 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 3 of the License, or
* (at your option) any later version.
*
* GpgFrontend 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 GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
*
* The initial version of the source code is inherited from
* the gpg4usb project, which is under GPL-3.0-or-later.
*
* All the source code of GpgFrontend was modified and released by
* Saturneric <eric@bktus.com> starting on May 12, 2021.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include "GpgCoreTest.h"
#include "core/GpgConstants.h"
#include "core/function/gpg/GpgKeyGetter.h"
#include "core/function/gpg/GpgKeyImportExporter.h"
#include "core/function/gpg/GpgKeyManager.h"
#include "core/function/gpg/GpgKeyOpera.h"
#include "core/model/GpgImportInformation.h"
#include "core/utils/GpgUtils.h"
const char *TEST_PRIVATE_KEY_DATA = R"(
-----BEGIN PGP PRIVATE KEY BLOCK-----
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
=OHnq
-----END PGP PRIVATE KEY BLOCK-----
)";
namespace GpgFrontend::Test {
TEST_F(GpgCoreTest, CoreDeleteSubkeyTestA) {
auto info = GpgKeyImportExporter::GetInstance().ImportKey(
GFBuffer(QString::fromLatin1(TEST_PRIVATE_KEY_DATA)));
ASSERT_EQ(info->not_imported, 0);
ASSERT_EQ(info->imported, 1);
auto key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
.GetKey("822D7E13F5B85D7D");
ASSERT_TRUE(key.IsGood());
auto subkeys = key.GetSubKeys();
ASSERT_EQ(subkeys->size(), 5);
ASSERT_EQ((*subkeys)[2].GetID(), "2D1F9FC59B568A8C");
auto res = GpgKeyManager::GetInstance().DeleteSubkey(key, 2);
ASSERT_TRUE(res);
key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
.GetKey("822D7E13F5B85D7D");
ASSERT_TRUE(key.IsGood());
subkeys = key.GetSubKeys();
ASSERT_EQ(subkeys->size(), 4);
ASSERT_EQ((*subkeys)[2].GetID(), "CE038203C4D03C3D");
GpgKeyOpera::GetInstance().DeleteKey(key.GetId());
}
TEST_F(GpgCoreTest, CoreSetOwnerTrustA) {
auto info = GpgKeyImportExporter::GetInstance().ImportKey(
GFBuffer(QString::fromLatin1(TEST_PRIVATE_KEY_DATA)));
ASSERT_EQ(info->not_imported, 0);
ASSERT_EQ(info->imported, 1);
auto key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
.GetKey("822D7E13F5B85D7D");
ASSERT_TRUE(key.IsGood());
auto res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 1);
ASSERT_TRUE(res);
GpgKeyGetter::GetInstance().FlushKeyCache();
key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
.GetKey("822D7E13F5B85D7D");
ASSERT_TRUE(key.IsGood());
// why?
ASSERT_EQ(key.GetOwnerTrustLevel(), 0);
res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 2);
ASSERT_TRUE(res);
GpgKeyGetter::GetInstance().FlushKeyCache();
key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
.GetKey("822D7E13F5B85D7D");
ASSERT_TRUE(key.IsGood());
ASSERT_EQ(key.GetOwnerTrustLevel(), 2);
res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 3);
ASSERT_TRUE(res);
GpgKeyGetter::GetInstance().FlushKeyCache();
key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
.GetKey("822D7E13F5B85D7D");
ASSERT_TRUE(key.IsGood());
ASSERT_EQ(key.GetOwnerTrustLevel(), 3);
res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 4);
ASSERT_TRUE(res);
GpgKeyGetter::GetInstance().FlushKeyCache();
key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
.GetKey("822D7E13F5B85D7D");
ASSERT_TRUE(key.IsGood());
ASSERT_EQ(key.GetOwnerTrustLevel(), 4);
res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 5);
ASSERT_TRUE(res);
GpgKeyGetter::GetInstance().FlushKeyCache();
key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
.GetKey("822D7E13F5B85D7D");
ASSERT_TRUE(key.IsGood());
ASSERT_EQ(key.GetOwnerTrustLevel(), 5);
res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 0);
ASSERT_FALSE(res);
res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, -1);
ASSERT_FALSE(res);
res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 6);
ASSERT_FALSE(res);
GpgKeyOpera::GetInstance().DeleteKey(key.GetId());
}
} // namespace GpgFrontend::Test

View File

@ -31,6 +31,7 @@
#include "core/GpgModel.h"
#include "core/function/gpg/GpgKeyGetter.h"
#include "core/function/gpg/GpgKeyImportExporter.h"
#include "core/function/gpg/GpgKeyManager.h"
#include "core/utils/CommonUtils.h"
#include "core/utils/GpgUtils.h"
#include "core/utils/IOUtils.h"
@ -149,6 +150,11 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(int channel, const QString& key_id,
setAttribute(Qt::WA_DeleteOnClose, true);
slot_refresh_subkey_list();
// set up signal
connect(this, &KeyPairSubkeyTab::SignalKeyDatabaseRefresh,
UISignalStation::GetInstance(),
&UISignalStation::SignalKeyDatabaseRefresh);
}
void KeyPairSubkeyTab::create_subkey_list() {
@ -338,8 +344,13 @@ void KeyPairSubkeyTab::create_subkey_opera_menu() {
connect(export_subkey_act_, &QAction::triggered, this,
&KeyPairSubkeyTab::slot_export_subkey);
delete_subkey_act_ = new QAction(tr("Delete"));
connect(delete_subkey_act_, &QAction::triggered, this,
&KeyPairSubkeyTab::slot_delete_subkey);
subkey_opera_menu_->addAction(export_subkey_act_);
subkey_opera_menu_->addAction(edit_subkey_act_);
subkey_opera_menu_->addAction(delete_subkey_act_);
}
void KeyPairSubkeyTab::slot_edit_subkey() {
@ -359,6 +370,7 @@ void KeyPairSubkeyTab::contextMenuEvent(QContextMenuEvent* event) {
export_subkey_act_->setDisabled(!subkey.IsSecretKey());
edit_subkey_act_->setDisabled(!subkey.IsSecretKey());
delete_subkey_act_->setDisabled(!subkey.IsSecretKey());
subkey_opera_menu_->exec(event->globalPos());
}
@ -391,11 +403,7 @@ void KeyPairSubkeyTab::slot_export_subkey() {
tr("Do you want to proceed with exporting this subkey?"),
QMessageBox::Cancel | QMessageBox::Yes, QMessageBox::Cancel);
if (ret != QMessageBox::Yes) {
QMessageBox::information(this, tr("Export Cancelled"),
tr("The subkey export has been cancelled."));
return;
}
if (ret != QMessageBox::Yes) return;
const auto& subkey = get_selected_subkey();
@ -431,4 +439,51 @@ void KeyPairSubkeyTab::slot_export_subkey() {
}
}
void KeyPairSubkeyTab::slot_delete_subkey() {
const auto& subkey = get_selected_subkey();
const auto subkeys = key_.GetSubKeys();
QString message = tr("<h3>You are about to delete the subkey:</h3><br />"
"<b>KeyID:</b> %1<br /><br />"
"This action is irreversible. Please confirm.")
.arg(subkey.GetID());
int ret = QMessageBox::warning(
this, tr("Delete Subkey Confirmation"), message,
QMessageBox::Cancel | QMessageBox::Yes, QMessageBox::Cancel);
if (ret != QMessageBox::Yes) return;
int index = 0;
for (const auto& sk : *subkeys) {
if (sk.GetFingerprint() == subkey.GetFingerprint()) {
break;
}
index++;
}
if (index == 0) {
QMessageBox::critical(
this, tr("Illegal Operation"),
tr("Cannot delete the primary key or an invalid subkey."));
return;
}
auto res = GpgKeyManager::GetInstance(current_gpg_context_channel_)
.DeleteSubkey(key_, index);
if (!res) {
QMessageBox::critical(this, tr("Operation Failed"),
tr("The selected subkey could not be deleted. "
"Please check your permissions or try again."));
return;
}
QMessageBox::information(
this, tr("Operation Successful"),
tr("The subkey with KeyID %1 has been successfully deleted.")
.arg(subkey.GetID()));
emit SignalKeyDatabaseRefresh();
}
} // namespace GpgFrontend::UI

View File

@ -93,6 +93,7 @@ class KeyPairSubkeyTab : public QWidget {
QAction* export_subkey_act_;
QAction* edit_subkey_act_;
QAction* delete_subkey_act_;
private slots:
@ -138,6 +139,20 @@ class KeyPairSubkeyTab : public QWidget {
*/
void slot_export_subkey();
/**
* @brief
*
*/
void slot_delete_subkey();
signals:
/**
* @brief
*
*/
void SignalKeyDatabaseRefresh();
protected:
/**
* @brief

View File

@ -47,8 +47,8 @@ auto SetOwnerTrustLevel::Exec(int channel, const QString& key_id) -> bool {
QStringList items;
items << tr("Unknown") << tr("Undefined") << tr("Never") << tr("Marginal")
<< tr("Full") << tr("Ultimate");
items << tr("Undefined") << tr("Never") << tr("Marginal") << tr("Full")
<< tr("Ultimate");
bool ok;
QString item = QInputDialog::getItem(this, tr("Modify Owner Trust Level"),
tr("Trust for the Key Pair:"), items,
@ -69,11 +69,6 @@ auto SetOwnerTrustLevel::Exec(int channel, const QString& key_id) -> bool {
}
if (trust_level == 0) {
QMessageBox::warning(
this, tr("Warning"),
QString(
tr("Owner Trust Level cannot set to Unknown level, automately "
"changing it into Undefined level.")));
trust_level = 1;
}

View File

@ -202,6 +202,8 @@ void MainWindow::recover_editor_unsaved_pages_from_cache() {
auto title = unsaved_page_json["title"].toString();
auto content = unsaved_page_json["content"].toString();
LOG_D() << "restoring tab, title: " << title;
if (first) {
edit_->SlotCloseTab();
first = false;