diff options
author | saturneric <[email protected]> | 2024-11-18 16:53:29 +0000 |
---|---|---|
committer | saturneric <[email protected]> | 2024-11-18 16:53:55 +0000 |
commit | a5d7cc6aa2951b20064108fadd39eaae3b8472b2 (patch) | |
tree | cc93a20b8be1e1c8571bb8d9ea3c8eba379ed6a6 | |
parent | feat: make primary key and subkey clearly (diff) | |
download | GpgFrontend-a5d7cc6aa2951b20064108fadd39eaae3b8472b2.tar.gz GpgFrontend-a5d7cc6aa2951b20064108fadd39eaae3b8472b2.zip |
feat: add delete subkey function
-rw-r--r-- | src/core/function/gpg/GpgKeyManager.cpp | 186 | ||||
-rw-r--r-- | src/core/function/gpg/GpgKeyManager.h | 13 | ||||
-rw-r--r-- | src/test/core/GpgCoreTestKeyManagement.cpp | 216 | ||||
-rw-r--r-- | src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp | 65 | ||||
-rw-r--r-- | src/ui/dialog/keypair_details/KeyPairSubkeyTab.h | 15 | ||||
-rw-r--r-- | src/ui/function/SetOwnerTrustLevel.cpp | 9 | ||||
-rw-r--r-- | src/ui/main_window/MainWindow.cpp | 2 |
7 files changed, 443 insertions, 63 deletions
diff --git a/src/core/function/gpg/GpgKeyManager.cpp b/src/core/function/gpg/GpgKeyManager.cpp index 18a8751e..bd52c341 100644 --- a/src/core/function/gpg/GpgKeyManager.cpp +++ b/src/core/function/gpg/GpgKeyManager.cpp @@ -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; -}
\ No newline at end of file +} +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(); +} diff --git a/src/core/function/gpg/GpgKeyManager.h b/src/core/function/gpg/GpgKeyManager.h index a34b34c1..83a38d05 100644 --- a/src/core/function/gpg/GpgKeyManager.h +++ b/src/core/function/gpg/GpgKeyManager.h @@ -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); diff --git a/src/test/core/GpgCoreTestKeyManagement.cpp b/src/test/core/GpgCoreTestKeyManagement.cpp new file mode 100644 index 00000000..114c237a --- /dev/null +++ b/src/test/core/GpgCoreTestKeyManagement.cpp @@ -0,0 +1,216 @@ +/** + * Copyright (C) 2021-2024 Saturneric <[email protected]> + * + * 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 <[email protected]> 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----- + +lQOYBGcSdI8BCACwi1n2Bx1v6qmQxRgrONYlmzKrSBvNyoSOdAVcTJDXYjdlNFCq +p1DJ9zbW63XxU9gtGtf21L9/mrq4tNmR8j+xKOf7Mlth/WKOMmch0yuk11Ffh3O6 +8uERHASYavKcAWVzs6R2r1hNMs8Vvhw1M0tx5R+qc02/l5A+c4OyTlStAsq7MoIz ++j1yLGAtcJu0Y64OHmRq0Zbz4xEFrsdfFvUuqHu9h+igz99ibVCMMJzbf042EMAt +Mh7fXXt/RrJCzrjgxnATNWWyz874PJB/Krr7r7t4zZ9OKqbzu6SmRlwv7GKwBlJk +SLXrmfJqza66yBfc4PoSvBALkWD9s0OjJgCtABEBAAEAB/0YFotpxELB+HS2ag4I +J7MgYnKhaC9S/uTjQvVQSKoimSYRyveOsVGWnQKAhJQNH3GJhfYdmZ2fXY9IkHR3 +M2R5Wal9XruVPStrV3k25kc4MKDLtgGWanlHClmeKkl7+7zJ8qLoBri3n80dnFjg +8WTD341Yhm7/S0DFJKG9fG6VRGgv5KVpMmLsulw9tCuykBz1FpqDpQNy7L5XiouN +FYgQC5D9W2NpcKBGaT5EKeZE0ARWMmrYwuI+TeHj6LZj0FEVtd/0ZiLN12qxk/C9 +hdvAeFkNIoDVXtsPL3wW3p0fXhCQX1FRx+rPXA2g0qwRFhXyEQ4sRqOJ872ivygZ +dDaJBADLuFg9BnKWTXYhsfTSOC5JyscRDgx6t4nSxpMW8TdK5eMC3j4KQOMYyG6r +ICtlcDC2FDMYVHByTL1QAi+g+MbaBtFFxQLRR8VMxpU91dO/y4HENut1h/OhYyui +yPfn076601+kmWglqlHsDQiOFhWX480EumxrK5jz8mmn1nvU8wQA3dmpDGHtOZPB +YbGx6bNLyxBWP8XosYJBRekyKQZgsQ2FJFVJRijXe+hWWIqmCp+EnODp76vua8ez +Oy7qdICFIAKVUzysj9aQpgJIfqahqo9INCMmEmqrIOAiql1B+PhJXth5gt/4TwBw +0ks1unHnuF4cBl6kvAwjCaFlIBreu98EALMQqYrJd4d6UuUdvCIfdmxKqgvgtJms +t4SxYz3nx0h/MDaW3gzE35/zjiKSjYDYGhiTqLwBj4MwuPQ53daJEOQAl+Tgu/Dx +fZvHtekrqdGwyQg4mty3iFzNUNOc+/6k2CTG5FaLkPB6nCVcRL7CjQc1PNofsAk3 +X5f1WwiI52ThQb+0H2FhYWFhYShhYWFhYWEpPGFhYWFhYUBhYWEuYWFhYT6JAVcE +EwEIAEEWIQRg5eoexx+vEuHiC2KCLX4T9bhdfQUCZxJ0jwIbLwUJA8Jm9QULCQgH +AgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRCCLX4T9bhdfZB8B/4h2Q9dWEcTs0nC +5k9ZNcqGT5kokrz4bqHbOXZUz149LNsnMte2/Ieb9WzVKuSXXy4nGc11aFI8CRO3 +sGxJhyHD9Rc0cMCAJZMsRh8ZdxyJt5LmFLjZZaWSyX2ppkTiongri+QI+ZQXUwmz +Wvd+zFes04a0PX4YyEJRHFMj5Uyj5KXcD+yLHr+GvY8aNHod19MASAsAYmkOP2y2 +4BJWFt8WvR34mIyim3WXH9GLbazphUuvxBTkGnDD884UYsVtem3B5QdwSXUrM91d +YzDmGsnSXOToB8V0YNgEOZB6mJOgwWtu66Tfv3gVmfaKjwfw0WTS+Fnu7dybq+ft +EVUkja6qnHsEZztd4hIIKoZIzj0DAQcCAwTs21ejx4VsJ2GwHFva0eYThHzl6dW7 +NMPT9Vt4NwHqoHm8qcAMQERtxkOcj0ct4CKHn/jptYNnTJ1Dqo/Ci/DWAwEIBwAA +/j4SH2MZfb/l+ChNY9O+ecgpEH16+V7GHcDOMOi8uQzkESGJATwEGAEIACYWIQRg +5eoexx+vEuHiC2KCLX4T9bhdfQUCZztd4gIbDAUJA8Jm+gAKCRCCLX4T9bhdfYt3 +B/9vDOBplNvCdzacIYzADDT3eIwhZYiph0Daf6UoH1WYxrSzzFV9NZLtRVdW+Q4f +Ws2Z4yEFI1U20aBpFMBI9VmKefeCHgFv90BH/k6iAxppDP6bJwxlpJhGRq1T68Yp +xqmEYZELd+NVRUsGyrBqvClZzntT1ZdeIqhddomlWBmUieqsCNHV/X7A6YO0T/CR +aBE5HqKCXXaSzateo9XnjnWcxdB6LXBKSwE1Mc7/viuqNiwNLcJs1S5t+Xhs0wir +Bk0B8JFMSweQHl54KbmILfbGgJVpYEKdNP5NjQ+pEBWZ9RwUtSew2ToRl2pejH3T +vONGb8+4qlEKWCyRQU8ItJupnHsEZztd6RIIKoZIzj0DAQcCAwRSIREPm5GzD2jl +ZIJoyiDZ63qahZMS0ZaM++JyAuoJDDdchEG8vbDd6hDHtuNIt5C/kNSRlrhSKd3X +eyrWEdl6AwEIBwABAIoZYMiR4z7LkEJ3PKDICj45gQHJrLOrHS+aAx10Rz4RDRyJ +ATwEGAEIACYWIQRg5eoexx+vEuHiC2KCLX4T9bhdfQUCZztd6QIbDAUJA8Jm+gAK +CRCCLX4T9bhdfSL0B/9DhMT5UBBWL/Tv+mqtNlOiOhD7oSUa7RQOhwTpKtofSOlU +qO+orqQNzDd4WFOhZqLBNsofEkEiiFzdfRMOBci9Lni1W7ZFptwIYoP7EAPcKFIp +knSwMxFsw4iByAiq0+JD2I0DgyTKOLP8vCzsX5GVztyJuD12lrccrKI25s3Htlvs +qexQQUOSjtNCsQKUWO/ZAsXxXaPnkKceDJ6hdLZX1UPuXJpx2XCTbN0PFVvyNXHC +9nEsjMUXVcYZ4fY7x+760eXXirUCIpBMjJbLdrRx92MWcprFNPnKPOaAorm1jToo +3LDUwK6U/pT68+moWg3GrANzLaaUU9rAHWW0cI2RnF0EZztd8BIKKwYBBAGXVQEF +AQEHQK6MpzBV5VCSad0TW9ZjPcxHHHbCbsHXMRFxJGlCnz89AwEIBwAA/2A8ya3E +O00FhIXpj/pCJ/KJ+xQbkD+3Fl1BMRQc5FE4DvOJATwEGAEIACYWIQRg5eoexx+v +EuHiC2KCLX4T9bhdfQUCZztd8AIbDAUJA8Jm+gAKCRCCLX4T9bhdfd5IB/sFqa4h +r0Kahac4Etk4J4DfEr1lGlfgPuWGa8AdwiKXv5W3jJMHBdvDjY8HiZy7VGfBxfKj +E3n/iHrxIO5ozvgTWnkUeNHLoMjkGaR6MRSQnVp8uTVHoD3CogO8DlPJVXZDjOrS +KpPbnFpaqxHQfZrqS9Z5h6ZzwZUzwNeyiWfxBj9ARKLSQTbbXy74qGW0PK11MYZS +qVHjA738tB4ptDW0bdPVGCZHxADIt8HCiZh31HWmg4CCWEaXJBfMy2YGJfgattMU +CgeT/sdj7Bx0xDG7tsiHEIk2MnBHB06/IJnKlsoYqtddm98Ud6oSr2dtG+o4k9lr +WsyeFFpo4GXCHrGMnIwFZztd+BYAAAA/AytlcQHIjNNmM9kZR/q8ioi4rNbQq7Ro +ocjTpUQksU6NZ/aqbcePhYYi9mQfLLky/A+H85C5OUn/d8IeBqkAAAAAAAA7AcdD +4tP0Res9exQEcaUWFK8NaUMT7cBzOGD+EituLRWumm3KlevTPMpjuyjyWVIwJIpC +jrmcdzx3XSEaeIkBPAQYAQgAJhYhBGDl6h7HH68S4eILYoItfhP1uF19BQJnO134 +AhsgBQkDwmb5AAoJEIItfhP1uF19osIH/iAyZFL91xzjjQu1NArm/gmBwqx05hGM +I+7VLVZOZIh/lpdccIU2Foeeu4AxljZDsrd/2k2h8PcnHgIuUdAx+niPoYEEDett +hmvHZZCRajWzTxFnrheqCYmqmvZn9J8AL2e90I1VHbs7VTvQpArqk0jg9+AL6aDr +NYcR4FAAM66giutz3fLOEfZp5MRpdkReS0t/yzK/ta1khfLWu1zAHCXN7v4xig+P +xluv86jgkEsr7yVGgkUiWxnlkLzqrT2PTsaYxO0lWcX1CG0D/rb72icpWZEWth7Y +cBEIUb80jrN959lF8eobqrVouY5GyvZXVZFGoXS4OTkFAwlEZxWBxJw= +=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
\ No newline at end of file diff --git a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp index 43bebe86..335f9c53 100644 --- a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp +++ b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp @@ -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 diff --git a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h index 4e626ae5..1222b77d 100644 --- a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h +++ b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h @@ -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 diff --git a/src/ui/function/SetOwnerTrustLevel.cpp b/src/ui/function/SetOwnerTrustLevel.cpp index d3ca33b3..b0fa8b0f 100644 --- a/src/ui/function/SetOwnerTrustLevel.cpp +++ b/src/ui/function/SetOwnerTrustLevel.cpp @@ -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; } diff --git a/src/ui/main_window/MainWindow.cpp b/src/ui/main_window/MainWindow.cpp index fc320391..0c88412a 100644 --- a/src/ui/main_window/MainWindow.cpp +++ b/src/ui/main_window/MainWindow.cpp @@ -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; |