From 4bec3dcbc9ed98de9cd722dcb7027876af3066c3 Mon Sep 17 00:00:00 2001 From: Quentin Rouland Date: Thu, 30 Jan 2025 16:25:29 +0100 Subject: [PATCH] First draft Rewrite get all key with rnp --- clickable.yaml | 1 + cmake/FindJSON-C.cmake | 127 ++++++++++++++++++ plugins/Pass/CMakeLists.txt | 4 +- plugins/Pass/jobs/getkeysjob.cpp | 29 ++++ plugins/Pass/jobs/getkeysjob.h | 38 ++++++ plugins/Pass/jobs/importkeyjob.cpp | 55 +++++--- plugins/Pass/jobs/importkeyjob.h | 7 +- plugins/Pass/jobs/rnpjob.cpp | 83 ++++++++++-- plugins/Pass/jobs/rnpjob.h | 29 ++-- plugins/Pass/pass.cpp | 86 ++++++++---- plugins/Pass/pass.h | 29 +++- po/utpass.qrouland.pot | 2 +- qml/Main.qml | 4 +- qml/dialogs/SimpleValidationDialog.qml | 1 - qml/pages/settings/DeleteRepo.qml | 1 - qml/pages/settings/InfoKeys.qml | 4 +- tests/assets/gpghome/pubring.pgp | Bin 0 -> 425 bytes tests/assets/gpghome/secring.pgp | Bin 0 -> 591 bytes tests/plugins/TestsUtils/passphraseprovider.h | 5 +- tests/plugins/TestsUtils/utils.cpp | 41 +++++- tests/plugins/TestsUtils/utils.h | 5 +- tests/units/pass/PassTestCase.qml | 12 +- tests/units/pass/tst_get_keys.qml | 52 +++++++ tests/units/pass/tst_import_key.qml | 61 +++++---- tests/units/tst_git.qml | 4 +- 25 files changed, 558 insertions(+), 122 deletions(-) create mode 100644 cmake/FindJSON-C.cmake create mode 100644 plugins/Pass/jobs/getkeysjob.cpp create mode 100644 plugins/Pass/jobs/getkeysjob.h create mode 100644 tests/assets/gpghome/pubring.pgp create mode 100644 tests/assets/gpghome/secring.pgp create mode 100644 tests/units/pass/tst_get_keys.qml diff --git a/clickable.yaml b/clickable.yaml index c93f51f..08a20e5 100644 --- a/clickable.yaml +++ b/clickable.yaml @@ -12,6 +12,7 @@ dependencies_target: - libquazip5-dev - libgpgmepp-dev - libgpgme-dev +- libjson-c-dev - gpg libraries: diff --git a/cmake/FindJSON-C.cmake b/cmake/FindJSON-C.cmake new file mode 100644 index 0000000..157de18 --- /dev/null +++ b/cmake/FindJSON-C.cmake @@ -0,0 +1,127 @@ +# Copyright (c) 2018, 2024 Ribose Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +#.rst: +# FindJSON-C +# ----------- +# +# Find the json-c library. +# +# IMPORTED Targets +# ^^^^^^^^^^^^^^^^ +# +# This module defines :prop_tgt:`IMPORTED` targets: +# +# ``JSON-C::JSON-C`` +# The json-c library, if found. +# +# Result variables +# ^^^^^^^^^^^^^^^^ +# +# This module defines the following variables: +# +# :: +# +# JSON-C_FOUND - true if the headers and library were found +# JSON-C_INCLUDE_DIRS - where to find headers +# JSON-C_LIBRARIES - list of libraries to link +# JSON-C_VERSION - library version that was found, if any + +# use pkg-config to get the directories and then use these values +# in the find_path() and find_library() calls +find_package(PkgConfig) +pkg_check_modules(PC_JSON-C QUIET json-c) + +# RHEL-based systems may have json-c12 +if (NOT PC_JSON-C_FOUND) + pkg_check_modules(PC_JSON-C QUIET json-c12) +endif() + +# ..or even json-c13, accompanied by non-develop json-c (RHEL 8 ubi) +if (NOT PC_JSON-C_FOUND) + pkg_check_modules(PC_JSON-C QUIET json-c13) +endif() + +# find the headers +find_path(JSON-C_INCLUDE_DIR + NAMES json_c_version.h + HINTS + ${PC_JSON-C_INCLUDEDIR} + ${PC_JSON-C_INCLUDE_DIRS} + PATH_SUFFIXES json-c json-c12 json-c13 +) + +# find the library +find_library(JSON-C_LIBRARY + NAMES json-c libjson-c json-c12 libjson-c12 json-c13 libjson-c13 + HINTS + ${PC_JSON-C_LIBDIR} + ${PC_JSON-C_LIBRARY_DIRS} +) + +# determine the version +if(PC_JSON-C_VERSION) + set(JSON-C_VERSION ${PC_JSON-C_VERSION}) +elseif(JSON-C_INCLUDE_DIR AND EXISTS "${JSON-C_INCLUDE_DIR}/json_c_version.h") + file(STRINGS "${JSON-C_INCLUDE_DIR}/json_c_version.h" _json-c_version_h + REGEX "^#define[\t ]+JSON_C_VERSION[\t ]+\"[^\"]*\"$") + + string(REGEX REPLACE ".*#define[\t ]+JSON_C_VERSION[\t ]+\"([^\"]*)\".*" + "\\1" _json-c_version_str "${_json-c_version_h}") + set(JSON-C_VERSION "${_json-c_version_str}" + CACHE INTERNAL "The version of json-c which was detected") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(JSON-C + REQUIRED_VARS JSON-C_LIBRARY JSON-C_INCLUDE_DIR JSON-C_VERSION + VERSION_VAR JSON-C_VERSION +) + +if (JSON-C_FOUND) + set(JSON-C_INCLUDE_DIRS ${JSON-C_INCLUDE_DIR} ${PC_JSON-C_INCLUDE_DIRS}) + set(JSON-C_LIBRARIES ${JSON-C_LIBRARY}) +endif() + +if (JSON-C_FOUND AND NOT TARGET JSON-C::JSON-C) + # create the new library target + add_library(JSON-C::JSON-C UNKNOWN IMPORTED) + # set the required include dirs for the target + if (JSON-C_INCLUDE_DIRS) + set_target_properties(JSON-C::JSON-C + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${JSON-C_INCLUDE_DIRS}" + ) + endif() + # set the required libraries for the target + if (EXISTS "${JSON-C_LIBRARY}") + set_target_properties(JSON-C::JSON-C + PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${JSON-C_LIBRARY}" + ) + endif() +endif() + +mark_as_advanced(JSON-C_INCLUDE_DIR JSON-C_LIBRARY) diff --git a/plugins/Pass/CMakeLists.txt b/plugins/Pass/CMakeLists.txt index cca72cc..972b1ee 100644 --- a/plugins/Pass/CMakeLists.txt +++ b/plugins/Pass/CMakeLists.txt @@ -8,6 +8,7 @@ set( passkeymodel.h jobs/rmjob.cpp jobs/rnpjob.cpp + jobs/getkeysjob.cpp jobs/importkeyjob.cpp ) @@ -32,6 +33,7 @@ qt5_use_modules(${PLUGIN} Qml Quick DBus) set(RNP_BUILD_DIR "${CMAKE_SOURCE_DIR}/build/${ARCH_TRIPLET}/rnp/install") find_package(OpenSSL REQUIRED) +find_package(JSON-C 0.11) INCLUDE_DIRECTORIES(${RNP_BUILD_DIR}/include) @@ -57,7 +59,7 @@ add_library(libqgpgme SHARED IMPORTED) set_property(TARGET libqgpgme PROPERTY IMPORTED_LOCATION "/usr/lib/${ARCH_TRIPLET}/libqgpgme.so") -target_link_libraries(${PLUGIN} rnp sexpp gpgerror libassuan libgpgme libgpgmepp libqgpgme OpenSSL::Crypto) +target_link_libraries(${PLUGIN} rnp sexpp gpgerror libassuan libgpgme libgpgmepp libqgpgme OpenSSL::Crypto JSON-C::JSON-C) set(QT_IMPORTS_DIR "/lib/${ARCH_TRIPLET}") install(TARGETS ${PLUGIN} DESTINATION ${QT_IMPORTS_DIR}/${PLUGIN}/) diff --git a/plugins/Pass/jobs/getkeysjob.cpp b/plugins/Pass/jobs/getkeysjob.cpp new file mode 100644 index 0000000..a2fc9a7 --- /dev/null +++ b/plugins/Pass/jobs/getkeysjob.cpp @@ -0,0 +1,29 @@ +#include +#include "getkeysjob.h" +#include +#include +extern "C" { +#include +#include +} + +GetKeysJob::GetKeysJob(QDir rnp_homedir): + RnpJob(rnp_homedir) +{ + this->setObjectName("GetKeysJob"); +} + + + +void GetKeysJob::run() +{ + qDebug() << "[GetKeysJob] Starting"; + + // Loading keyring + QSet fingerprints = QSet(); + this->load_full_keyring(&fingerprints); + + //Get all infos keys + emit resultSuccess(fingerprints); + qDebug() << "[GetKeysJob] Finished Successfully "; +} diff --git a/plugins/Pass/jobs/getkeysjob.h b/plugins/Pass/jobs/getkeysjob.h new file mode 100644 index 0000000..1dcdf2d --- /dev/null +++ b/plugins/Pass/jobs/getkeysjob.h @@ -0,0 +1,38 @@ +#ifndef GETKEYSJOB_H +#define GETKEYSJOB_H + +#include + + +#include "rnpjob.h" + +/** + * @class GetKeysJob + * @brief A class to handle get all gpg keys from rings in a separate thread. + * + */ +class GetKeysJob : public RnpJob +{ + Q_OBJECT + + /** + * @brief The main function that performs the get all keys operation. + * + * Handles the process of removing recursively a target path. + */ + void run() override; + +signals: + void resultError(const rnp_result_t err); + void resultSuccess(const QSet result); + +public: + /** + * @brief Constructor for the GetKeysJob class. + * + * @param rnp_homedir Rnp home dir that contains the keyrings. + */ + GetKeysJob(QDir rnp_homedir); +}; + +#endif // GETKEYSJOB_H diff --git a/plugins/Pass/jobs/importkeyjob.cpp b/plugins/Pass/jobs/importkeyjob.cpp index 7d4bf24..8053af8 100644 --- a/plugins/Pass/jobs/importkeyjob.cpp +++ b/plugins/Pass/jobs/importkeyjob.cpp @@ -1,5 +1,8 @@ #include +#include +#include #include "importkeyjob.h" + extern "C" { #include #include @@ -9,38 +12,52 @@ ImportKeyJob::ImportKeyJob(QDir rnp_homedir, QString key_file_path): RnpJob(rnp_homedir), m_key_file_path(key_file_path) { - this->setObjectName("DecryptJob"); + this->setObjectName("ImportKeyJob"); } void ImportKeyJob::run() { - qDebug() << "ImportKeyJob Starting "; + qDebug() << "[ImportKeyJob] Starting"; rnp_input_t input = NULL; auto ret = rnp_input_from_path(&input, this->m_key_file_path.toLocal8Bit().constData()); - if(ret == RNP_SUCCESS) { - ret = rnp_load_keys(this->m_ffi, - "GPG", - input, - RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS); + if (ret == RNP_SUCCESS) { + char *r = NULL; + ret = rnp_import_keys(this->m_ffi, + input, + RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS, + &r); + + qDebug() << "[ImportKeyJob]" << QJsonDocument::fromJson(r); + rnp_buffer_destroy(r); } + rnp_input_destroy(input); - terminateWithError(ret); + terminateOnError(ret); rnp_output_t output = NULL; - ret = rnp_output_to_file(&output, this->pubringPath().toLocal8Bit().constData(), RNP_OUTPUT_FILE_RANDOM); - if(ret == RNP_SUCCESS) { - ret = rnp_save_keys(this->m_ffi, RNP_KEYSTORE_GPG, output, RNP_LOAD_SAVE_SECRET_KEYS); - } - rnp_output_destroy(output); - terminateWithError(ret); + qDebug() << "[ImportKeyJob] Writing pubring to " << this->pubringPath(); + ret = rnp_output_to_file(&output, this->pubringPath().toLocal8Bit().constData(), RNP_OUTPUT_FILE_OVERWRITE); + if (ret == RNP_SUCCESS) { + qDebug() << "[ImportKeyJob] Saving key pubring "; + ret = rnp_save_keys(this->m_ffi, RNP_KEYSTORE_GPG, output, RNP_LOAD_SAVE_PUBLIC_KEYS); - ret = rnp_output_to_file(&output, this->secringPath().toLocal8Bit().constData(), RNP_OUTPUT_FILE_OVERWRITE); - if(ret == RNP_SUCCESS) { - ret = rnp_save_keys(this->m_ffi, RNP_KEYSTORE_GPG, output, RNP_LOAD_SAVE_SECRET_KEYS); + } + if (ret == RNP_SUCCESS) { + ret = rnp_output_finish(output); } rnp_output_destroy(output); - terminateWithError(ret); + terminateOnError(ret); + + qDebug() << "[ImportKeyJob] Writing secring to " << this->secringPath(); + ret = rnp_output_to_file(&output, this->secringPath().toLocal8Bit().constData(), RNP_OUTPUT_FILE_OVERWRITE); + if (ret == RNP_SUCCESS) { + qDebug() << "[ImportKeyJob] Saving key secring "; + ret = rnp_save_keys(this->m_ffi, RNP_KEYSTORE_GPG, output, RNP_LOAD_SAVE_SECRET_KEYS); + } + + rnp_output_destroy(output); + terminateOnError(ret); emit resultSuccess(); - qDebug() << "ImportKeyJob Finished Successfully "; + qDebug() << "[ImportKeyJob] Finished Successfully "; } diff --git a/plugins/Pass/jobs/importkeyjob.h b/plugins/Pass/jobs/importkeyjob.h index 2fe8531..ebaa19b 100644 --- a/plugins/Pass/jobs/importkeyjob.h +++ b/plugins/Pass/jobs/importkeyjob.h @@ -20,19 +20,22 @@ class ImportKeyJob : public RnpJob */ void run() override; +signals: + void resultSuccess(); private: QString m_key_file_path; ///< The path of the key file to import. public: /** - * @brief Constructor for the RmJob class. + * @brief Constructor for the ImportKeyJob class. * * Initializes the ImportKeyJob with the file to import. * + * @param rnp_homedir Rnp home dir that contains the keyrings. * @param path Path of the key file to import. */ - ImportKeyJob(QDir rnp_homedir, QString key_file_path); + ImportKeyJob(QDir rnp_homedir, QString path); }; #endif // IMPORTKEYJOB_H diff --git a/plugins/Pass/jobs/rnpjob.cpp b/plugins/Pass/jobs/rnpjob.cpp index 478f617..6f46f2f 100644 --- a/plugins/Pass/jobs/rnpjob.cpp +++ b/plugins/Pass/jobs/rnpjob.cpp @@ -1,5 +1,8 @@ #include - +#include +#include +#include +#include "qjsonarray.h" #include "rnpjob.h" extern "C" { #include @@ -9,29 +12,34 @@ extern "C" { RnpJob::RnpJob(QDir rnp_homedir): m_rnp_homedir(rnp_homedir) { + qRegisterMetaType("rnp_result_t"); + qRegisterMetaType> ("QSet"); + + auto ret = rnp_ffi_create(&this->m_ffi, - RNP_KEYSTORE_GPG, - RNP_KEYSTORE_GPG); + RNP_KEYSTORE_GPG, + RNP_KEYSTORE_GPG); if(ret != RNP_SUCCESS) { - qDebug() << "Err : " << ret; + qDebug() << "[RnpJob] Err : " << ret; qFatal("Error on rnp ffi init!"); } } -RnpJob::~RnpJob(){ +RnpJob::~RnpJob() +{ auto ret = rnp_ffi_destroy(this->m_ffi); if(ret != RNP_SUCCESS) { - qDebug() << "Err : " << ret; + qDebug() << "[RnpJob] Err : " << ret; qFatal("Something go wrong on rnp ffi detroy"); } } bool RnpJob::passProvider(rnp_ffi_t ffi, - void * app_ctx, - rnp_key_handle_t key, - const char * pgp_context, - char buf[], - size_t buf_len) + void *app_ctx, + rnp_key_handle_t key, + const char *pgp_context, + char buf[], + size_t buf_len) { if (strcmp(pgp_context, "protect")) { return false; @@ -40,3 +48,56 @@ bool RnpJob::passProvider(rnp_ffi_t ffi, strncpy(buf, "password", buf_len); return true; } + + +void RnpJob::load_key_file(QSet *fingerprints, const QString path, const uint32_t flags) +{ + qDebug() << "[RnpJob] load keyring at" << path; + rnp_input_t input = NULL; + if (QFileInfo::exists(this->pubringPath())) { + auto ret = rnp_input_from_path(&input, path.toLocal8Bit().constData()); + char *json = NULL; + if (ret == RNP_SUCCESS) { + ret = rnp_import_keys(this->m_ffi, + input, + flags, + &json); + if (ret != RNP_SUCCESS) { + + } + } + QJsonDocument json_document = QJsonDocument::fromJson(json); + qDebug() << "[RnpJob] json" << json_document; + foreach (const QJsonValue fingerprint, json_document.object()["keys"].toArray()) { + qDebug() << "[RnpJob] Add fingerprint" << fingerprint["fingerprint"].toString(); + fingerprints->insert(fingerprint["fingerprint"].toString()); + } + + rnp_input_destroy(input); + rnp_buffer_destroy(json); + terminateOnError(ret); + qDebug() << "[RnpJob] keyring loaded successfully"; + } else { + qDebug() << "[RnpJob] No keyring" << path << "not found"; + } +} + + +void RnpJob::load_pub_keyring(QSet *fingerprints) +{ + this->load_key_file(fingerprints, this->pubringPath(), RNP_LOAD_SAVE_PUBLIC_KEYS); + qDebug() << "[RnpJob] pub fingerprints" << *fingerprints; +} + +void RnpJob::load_sec_keyring(QSet *fingerprints) +{ + this->load_key_file(fingerprints, this->secringPath(), RNP_LOAD_SAVE_SECRET_KEYS); + qDebug() << "[RnpJob] sec fingerprints" << *fingerprints; +} + +void RnpJob::load_full_keyring(QSet *fingerprints) +{ + this->load_pub_keyring(fingerprints); + this->load_sec_keyring(fingerprints); + qDebug() << "[RnpJob] full fingerprints" << *fingerprints; +} diff --git a/plugins/Pass/jobs/rnpjob.h b/plugins/Pass/jobs/rnpjob.h index c6058eb..a87afe3 100644 --- a/plugins/Pass/jobs/rnpjob.h +++ b/plugins/Pass/jobs/rnpjob.h @@ -9,10 +9,10 @@ extern "C" { #include -#define terminateWithError(ret) \ +#define terminateOnError(ret) \ if(ret != RNP_SUCCESS) { \ - qDebug() << "Err : " << ret; \ - qDebug() << "Err Msg : " << rnp_result_to_string(ret); \ + qDebug() << "[RnpJob] Err : " << ret; \ + qDebug() << "[RnpJob] Err Msg : " << rnp_result_to_string(ret); \ emit resultError(ret); \ return; \ } \ @@ -30,16 +30,16 @@ class RnpJob : public QThread signals: void resultError(const rnp_result_t err); - void resultSuccess(); private: static bool passProvider(rnp_ffi_t ffi, - void * app_ctx, - rnp_key_handle_t key, - const char * pgp_context, - char buf[], - size_t buf_len); + void *app_ctx, + rnp_key_handle_t key, + const char *pgp_context, + char buf[], + size_t buf_len); QDir m_rnp_homedir; ///< rmp ffi. + void load_key_file(QSet *fingerprints, const QString path, const uint32_t flags); protected: rnp_ffi_t m_ffi; ///< rmp ffi. @@ -49,7 +49,8 @@ protected: * * @return The path to public keys keyring */ - QString pubringPath() { + QString pubringPath() + { return this->m_rnp_homedir.filePath("pubring.pgp"); } @@ -58,16 +59,22 @@ protected: * * @return The path to secret keys keyring */ - QString secringPath() { + QString secringPath() + { return this->m_rnp_homedir.filePath("secring.pgp"); } + void load_sec_keyring(QSet *fingerprints); + void load_pub_keyring(QSet *fingerprints); + void load_full_keyring(QSet *fingerprints); public: /** * @brief Constructor for the RnpJob class. * * Initializes the RnpJob instance. + * + * @param rnp_homedir Rnp home dir that contains the keyrings. */ RnpJob(QDir rnp_homedir); diff --git a/plugins/Pass/pass.cpp b/plugins/Pass/pass.cpp index 1ed7bfc..afb4aad 100644 --- a/plugins/Pass/pass.cpp +++ b/plugins/Pass/pass.cpp @@ -2,6 +2,7 @@ #include #include +#include "jobs/getkeysjob.h" #include "jobs/importkeyjob.h" #include "pass.h" @@ -11,33 +12,46 @@ Pass::Pass(): m_password_store (QStandardPaths::writableLocation( QStandardPaths::AppDataLocation).append("/.password-store")), m_gpg_home (QStandardPaths::writableLocation( - QStandardPaths::AppDataLocation).append("/.rnp")), + QStandardPaths::AppDataLocation).append("/.rnp")), m_sem(std::unique_ptr(new QSemaphore(1))), m_show_filename(QString()) { - qRegisterMetaType("rnp_result_t"); + } void Pass::initialize(QObject *window) { if (!window) { - qWarning("Window should not be null unless your in testing"); + qWarning("[Pass] Window should be null only for testing"); } + this->initGpgHome(); + this->initPasswordStore(); +} - // this->m_gpg = std::unique_ptr(new Gpg(window)); - // UTPassphraseProvider *passphrase_provider = dynamic_cast(this->m_gpg->passphrase_provider()); - // QObject::connect(this, &Pass::responsePassphraseDialogPropagate, passphrase_provider, - // &UTPassphraseProvider::handleResponse); - // QObject::connect(this->m_gpg.get(), &Gpg::getKeysResult, this, &Pass::getAllGPGKeysResult); - // QObject::connect(this->m_gpg.get(), &Gpg::deleteKeyResult, this, &Pass::deleteGPGKeyResult); - // QObject::connect(this->m_gpg.get(), &Gpg::decryptResult, this, &Pass::showResult); +void Pass::initGpgHome() +{ + // delete gpghome from previous version using GPGME + QString path = QStandardPaths::writableLocation( + QStandardPaths::AppDataLocation).append("/.gpghome"); + QDir dir(path); + dir.removeRecursively(); - QDir dir(m_password_store); - if (!dir.exists()) { - dir.mkpath("."); + // create gpghome for rnp + QDir dir_gpg_home(this->m_gpg_home); + if (!dir_gpg_home.exists()) { + dir_gpg_home.mkpath("."); } - qInfo() << "Password Store is :" << m_password_store; + qInfo() << "[Pass] GPG Home is :" << m_gpg_home; +} + +void Pass::initPasswordStore() +{ + QDir dir_password_store(this->m_password_store); + if (!dir_password_store.exists()) { + dir_password_store.mkpath("."); + } + qInfo() << "[Pass] Password Store is :" << m_password_store; } // bool Pass::show(QUrl url) @@ -125,9 +139,9 @@ void Pass::initialize(QObject *window) bool Pass::importGPGKey(QUrl url) { - qInfo() << "Import GPG Key from " << url; + qInfo() << "[Pass] Import GPG Key from " << url; if (!this->m_sem->tryAcquire(1, 500)) { - qInfo() << "A job is already running"; + qInfo() << "[Pass] A job is already running"; return false; } auto job = new ImportKeyJob(this->m_gpg_home, url.toLocalFile()); @@ -140,26 +154,46 @@ bool Pass::importGPGKey(QUrl url) void Pass::slotImportGPGKeyError(rnp_result_t err) { - qDebug() << "Import GPG Key Failed"; + qInfo() << "[Pass] Import GPG Key Failed"; emit importGPGKeyFailed(rnp_result_to_string(err)); this->m_sem->release(1); } void Pass::slotImportGPGKeySucceed() { - qDebug() << "Import GPG Key Failed"; + qInfo() << "[Pass] Import GPG Key Succesfull"; emit importGPGKeySucceed(); this->m_sem->release(1); } -// bool Pass::getAllGPGKeys() -// { -// if (!this->m_sem->tryAcquire(1, 500)) { -// return false; -// } -// qInfo() << "Get GPG keys"; -// return this->m_gpg->getAllKeys(); -// } +bool Pass::getAllGPGKeys() +{ + qInfo() << "[Pass] Get all GPG Keys"; + if (!this->m_sem->tryAcquire(1, 500)) { + qInfo() << "[Pass] A job is already running"; + return false; + } + auto job = new GetKeysJob(this->m_gpg_home); + QObject::connect(job, &GetKeysJob::resultError, this, &Pass::slotGetAllGPGKeysError); + QObject::connect(job, &GetKeysJob::resultSuccess, this, &Pass::slotGetAllGPGKeysSucceed); + connect(job, &ImportKeyJob::finished, job, &QObject::deleteLater); + job->start(); + return true; +} + +void Pass::slotGetAllGPGKeysError(rnp_result_t err) +{ + qInfo() << "[Pass] Get all GPG Keys Failed"; + emit getAllGPGKeysFailed(rnp_result_to_string(err)); + this->m_sem->release(1); +} + +void Pass::slotGetAllGPGKeysSucceed(QSet result) +{ + qInfo() << "[Pass] Get all GPG Keys Succeed"; + emit getAllGPGKeysSucceed(result.values()); + this->m_sem->release(1); +} // void Pass::getAllGPGKeysResult(Error err, std::vector keys_info) // { diff --git a/plugins/Pass/pass.h b/plugins/Pass/pass.h index fcf176e..5452989 100644 --- a/plugins/Pass/pass.h +++ b/plugins/Pass/pass.h @@ -49,15 +49,19 @@ private slots: /** * @brief Slot to handle the succeed result of a GPG key import operation. - * @param err The error that occurred during the operation. */ void slotImportGPGKeySucceed(); + /** * @brief Slot to handle the result of retrieving all GPG keys. * @param err The error that occurred during the operation. - * @param keys_info The list of GPG keys retrieved. */ - void getAllGPGKeysResult(Error err, std::vector keys_info); + void slotGetAllGPGKeysError(rnp_result_t err); + + /** + * @brief Slot to handle the succeed result of a GPG key get all keys operation. + */ + void slotGetAllGPGKeysSucceed(QSet result); /** * @brief Slot to handle the result of a delete Password Store operation. @@ -93,7 +97,7 @@ signals: * @brief Emitted when all GPG keys are successfully retrieved. * @param keys_info The list of retrieved keys. */ - void getAllGPGKeysSucceed(QVariant keys_info); + void getAllGPGKeysSucceed(QList keys_info); /** * @brief Emitted when retrieving GPG keys fails. @@ -142,10 +146,21 @@ signals: private: QString m_password_store; /**< The path to the password store. */ QString m_gpg_home; /**< The path to the gpg home. */ - PassphraseProvider* m_passphrase_provider; /**< Semaphore for managing concurrent operations. */ + PassphraseProvider *m_passphrase_provider; /**< Semaphore for managing concurrent operations. */ std::unique_ptr m_sem; /**< Semaphore for managing concurrent operations. */ QString m_show_filename; /**< The filename associated with the password to show. */ + + /** + * @brief Initialize gpg home. + */ + void initGpgHome(); + + /** + * @brief Initialize password store. + */ + void initPasswordStore(); + public: /** * @brief Constructs the Pass object. @@ -167,7 +182,7 @@ public: */ void set_password_store(QString password_store) { - qInfo() << "Password Store changed to :" << password_store; + qInfo() << "[Pass] Password Store changed to :" << password_store; this->m_password_store = password_store; }; @@ -186,7 +201,7 @@ public: */ void set_gpg_home(QString gpg_home) { - qInfo() << "GNUPG Home changed to :" << gpg_home; + qInfo() << "[Pass] GPG Home changed to :" << gpg_home; this->m_gpg_home = gpg_home; }; diff --git a/po/utpass.qrouland.pot b/po/utpass.qrouland.pot index 12ea997..1ee8e3a 100644 --- a/po/utpass.qrouland.pot +++ b/po/utpass.qrouland.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: utpass.qrouland\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-01-29 16:33+0100\n" +"POT-Creation-Date: 2025-01-30 16:16+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/qml/Main.qml b/qml/Main.qml index 8ff6d36..d11ef96 100644 --- a/qml/Main.qml +++ b/qml/Main.qml @@ -21,15 +21,13 @@ MainView { pop.activateFocus(); } - objectName: "mainView" applicationName: "utpass.qrouland" automaticOrientation: true width: units.gu(45) height: units.gu(75) - Component.onCompleted: { - myWorker.sendMessage("Hello World !") + myWorker.sendMessage("Hello World !"); } PageStack { diff --git a/qml/dialogs/SimpleValidationDialog.qml b/qml/dialogs/SimpleValidationDialog.qml index 086835c..bc05a10 100644 --- a/qml/dialogs/SimpleValidationDialog.qml +++ b/qml/dialogs/SimpleValidationDialog.qml @@ -11,7 +11,6 @@ Dialog { signal validated() signal canceled() - Button { id: continueButton diff --git a/qml/pages/settings/DeleteRepo.qml b/qml/pages/settings/DeleteRepo.qml index fc30fa0..0fd91e3 100644 --- a/qml/pages/settings/DeleteRepo.qml +++ b/qml/pages/settings/DeleteRepo.qml @@ -29,7 +29,6 @@ Page { anchors.rightMargin: units.gu(2) spacing: units.gu(1) - Rectangle { width: parent.width height: units.gu(1) diff --git a/qml/pages/settings/InfoKeys.qml b/qml/pages/settings/InfoKeys.qml index d598909..8399bff 100644 --- a/qml/pages/settings/InfoKeys.qml +++ b/qml/pages/settings/InfoKeys.qml @@ -8,6 +8,7 @@ import QtQuick 2.4 Page { id: infoKeysPage + property list __keys property QtObject __currentKey @@ -15,7 +16,7 @@ Page { Pass.getAllGPGKeysSucceed.connect(function(keys_info) { infoKeysPage.__keys = keys_info; for (var i = 0; i < keys_info.length; ++i) { - console.debug("is secret " + keys_info[i].isSecret) + console.debug("is secret " + keys_info[i].isSecret); } }); Pass.getAllGPGKeysFailed.connect(function(message) { @@ -108,7 +109,6 @@ Page { userIdsModel.append({ "model": model.modelData.userIds[i] }); - } } } diff --git a/tests/assets/gpghome/pubring.pgp b/tests/assets/gpghome/pubring.pgp new file mode 100644 index 0000000000000000000000000000000000000000..384086d7544feaf2c748219afe7b870b56749acd GIT binary patch literal 425 zcmX?R%#z+1S|G-$&Bn;Wc*~uik&)fO(cs2^;jJwTZ!<36Tu?OWe|yx0kD)*K=l$tC zT4=pWWA$0t(2#({;$nr6)Z!8ao6?d3AjhEu$kEF$O1C>SlSNpJi@{M$k>zJe*|uL1 zE4&%}+{CWz`uKPIu6buznLsu&Ni(x@vM!$0z{<_Z!Op~_#Kb1b#mT|V#3IJT%*Z6i z&LrNzz{M#5(`8Y_$Z)}}aO!ju_b(M1z85>@-&|&%JGH+(=*+RRw_gdlOgV9bkzwK# z-%IN&SLIut*z@6T{VjuU4?j2TTvFw*ch~vYjm$H-&sl)IE5rr%?(|SbR*-)g59nO~ zeZ@iT{prb3VPZjDdBO=FdtDxHY+0YwcHLewhnbOs{ZJi?1kh7z1U$t9@f5Plf30Hp z@4tW1yvSu+_U1TA{8N6&TlKJc+6{Y+a=DMLuQlEtV%o#-KZRqm^Fg^hi-47m%~vMO WmyOtHH08RoyxdFUwCTzjoJ;@>{;iMz literal 0 HcmV?d00001 diff --git a/tests/assets/gpghome/secring.pgp b/tests/assets/gpghome/secring.pgp new file mode 100644 index 0000000000000000000000000000000000000000..f9abbe5478fdc9dca2a1a7ee9c54883dfe3d1e64 GIT binary patch literal 591 zcmX@w#**F`S|G-$&Bn;Wc*~uik&)fO(cs2^;jJwTZ!<36Tu?OWe|yx0kD)*K=l$tC zT4=pWWA#6FW+uUk%f&8_E>8HLpY!VJ&7|YYc5m3SW1-$l>n8n4ABYo0cb$t&xC;)*P;^2{02MK_;$cyhaLU-rz1Iq_#@Lqh@*i;ERP zQj1FzY)VTCfEW!rv5tng;=a}&F=>*L?;yXKu? zWdeDENt&6JlXdZ|23BrP4t6FcB_=jeE=~?^CKfR!W=1ACb|&!#1};tkm@bPVMurP+ zg;S@SxPPh8@V(eE|K>9D+^PNTL1&Jgz5PnaWy*;gj0_W}_+DCHxhmiC#GVg#>u(u+ zd-%Cw=aMRiy}QoGZe*UxeY_hSfdw+UzRG3&$SDtXf z$6lAm8(Y>VwOzND%wcBaUmy(My} zjzt0(PHF_gi3bu+$bs=|6~ll3{fp*BF59v<$4TO!@%EtaNO?GGV@K#73hj*OlevUK*!OSI*#M0svR9{UQJW literal 0 HcmV?d00001 diff --git a/tests/plugins/TestsUtils/passphraseprovider.h b/tests/plugins/TestsUtils/passphraseprovider.h index 5ebbf29..78631ab 100644 --- a/tests/plugins/TestsUtils/passphraseprovider.h +++ b/tests/plugins/TestsUtils/passphraseprovider.h @@ -7,13 +7,14 @@ class TesTPassphraseProvider : public QObject, public GpgME::PassphraseProvider { -Q_OBJECT + Q_OBJECT public: char *getPassphrase(const char *useridHint, const char *description, bool previousWasBad, - bool &canceled) override { + bool &canceled) override + { char *ret; gpgrt_asprintf(&ret, "%s", "utpasspassphrase"); diff --git a/tests/plugins/TestsUtils/utils.cpp b/tests/plugins/TestsUtils/utils.cpp index 57003f7..16206af 100644 --- a/tests/plugins/TestsUtils/utils.cpp +++ b/tests/plugins/TestsUtils/utils.cpp @@ -10,7 +10,7 @@ #include "utils.h" TestsUtils::TestsUtils(): -m_passphrase_povider(std::unique_ptr(new TesTPassphraseProvider())) + m_passphrase_povider(std::unique_ptr(new TesTPassphraseProvider())) {} @@ -28,12 +28,47 @@ QString TestsUtils::getTempPath() QDir dir; dir.mkpath(newTempDir); - qDebug() << "TempDir : " << newTempDir; + qDebug() << "[TestUtils] TempDir : " << newTempDir; return newTempDir; } +bool TestsUtils::fileExists(QUrl path) +{ + QString p = path.toLocalFile(); + auto ret = QFileInfo::exists(p) && QFileInfo(p).isFile(); + qDebug() << "[TestUtils]" << p << "is existing file :" << ret; + return ret; +} -QObject* TestsUtils::getTestPassphraseProvider() +void TestsUtils::copyFolder(QUrl sourceFolderUrl, QUrl destFolderUrl) +{ + auto sourceFolder = sourceFolderUrl.toLocalFile(); + auto destFolder = destFolderUrl.toLocalFile(); + QDir sourceDir(sourceFolder); + if (!sourceDir.exists()) + return; + QDir destDir(destFolder); + if (!destDir.exists()) { + destDir.mkdir(destFolder); + } + qDebug() << "[TestUtils]" << "Copy files from" << sourceFolder << "to" << destFolder; + QStringList files = sourceDir.entryList(QDir::Files); + for (int i = 0; i < files.count(); i++) { + QString srcName = sourceFolder + "/" + files[i]; + QString destName = destFolder + "/" + files[i]; + QFile::copy(srcName, destName); + qDebug() << "[TestUtils]" << "Copy file from" << srcName << "to" << destName; + } + files.clear(); + files = sourceDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); + for (int i = 0; i < files.count(); i++) { + QString srcName = sourceFolder + "/" + files[i]; + QString destName = destFolder + "/" + files[i]; + this->copyFolder(srcName, destName); + } +} + +QObject *TestsUtils::getTestPassphraseProvider() { return this->m_passphrase_povider.get(); } diff --git a/tests/plugins/TestsUtils/utils.h b/tests/plugins/TestsUtils/utils.h index 43fb66c..d4b1c24 100644 --- a/tests/plugins/TestsUtils/utils.h +++ b/tests/plugins/TestsUtils/utils.h @@ -19,7 +19,10 @@ public: ~TestsUtils() override = default; Q_INVOKABLE QString getTempPath(); - Q_INVOKABLE QObject* getTestPassphraseProvider(); + Q_INVOKABLE bool fileExists(QUrl path); + Q_INVOKABLE void copyFolder(QUrl sourceFolder, QUrl destFolder); + Q_INVOKABLE QObject *getTestPassphraseProvider(); + }; #endif diff --git a/tests/units/pass/PassTestCase.qml b/tests/units/pass/PassTestCase.qml index 823db22..6db3197 100644 --- a/tests/units/pass/PassTestCase.qml +++ b/tests/units/pass/PassTestCase.qml @@ -1,13 +1,19 @@ +import Pass 1.0 import QtQuick 2.9 import QtTest 1.2 import TestsUtils 1.0 -import Pass 1.0 TestCase { + property string password_store + property string gpg_home + function init() { Pass.initialize(null); - Pass.gpg_home = TestsUtils.getTempPath(); - Pass.password_store = TestsUtils.getTempPath(); + gpg_home = TestsUtils.getTempPath(); + Pass.gpg_home = gpg_home; + password_store = TestsUtils.getTempPath(); + Pass.password_store = password_store; Pass.passphrase_provider = TestsUtils.getTestPassphraseProvider(); } + } diff --git a/tests/units/pass/tst_get_keys.qml b/tests/units/pass/tst_get_keys.qml new file mode 100644 index 0000000..9ba8d8b --- /dev/null +++ b/tests/units/pass/tst_get_keys.qml @@ -0,0 +1,52 @@ +import Pass 1.0 +import QtQuick 2.9 +import QtTest 1.2 +import TestsUtils 1.0 + +PassTestCase { + function init_data() { + //TODO some additionanl error test + + return [{ + "spy": getAllGPGKeysSucceed, + "signal": Pass.getAllGPGKeysSucceed, + "err_msg": null, + "add_home_gpg_data": false, + "nb_keys": 0 + }, { + "spy": getAllGPGKeysSucceed, + "signal": Pass.getAllGPGKeysSucceed, + "err_msg": null, + "add_home_gpg_data": true, + "nb_keys": 2 + }]; + } + + function test_get_keys(data) { + if (data.add_home_gpg_data === true) + TestsUtils.copyFolder(Qt.resolvedUrl("../../assets/gpghome"), Qt.resolvedUrl(gpg_home)); + + var keys; + data.signal.connect(function(keys_info) { + keys = keys_info; + }); + Pass.getAllGPGKeys(); + data.spy.wait(); + verify(keys.length === data.nb_keys, "Nb keys %1 but was excepted %2".arg(keys.length).arg(data.nb_keys)); + } + + SignalSpy { + id: getAllGPGKeysSucceed + + target: Pass + signalName: "getAllGPGKeysSucceed" + } + + SignalSpy { + id: getAllGPGKeysFailed + + target: Pass + signalName: "getAllGPGKeysFailed" + } + +} diff --git a/tests/units/pass/tst_import_key.qml b/tests/units/pass/tst_import_key.qml index ab0fb3f..b468d93 100644 --- a/tests/units/pass/tst_import_key.qml +++ b/tests/units/pass/tst_import_key.qml @@ -1,47 +1,56 @@ +import Pass 1.0 import QtQuick 2.9 import QtTest 1.2 import TestsUtils 1.0 -import Pass 1.0 PassTestCase { function init_data() { - return [ - { file: Qt.resolvedUrl("../../assets/gpg/test_key.gpg"), spy: importGPGKeySucceed, signal: Pass.importGPGKeySucceed, err_msg : null } - , { file: Qt.resolvedUrl("../../assets/gpg/test_key_do_not_exist.gpg"), spy: importGPGKeyFailed, signal: Pass.importGPGKeyFailed, err_msg : "Error reading file" } - , { file: Qt.resolvedUrl("../../assets/gpg/test_key_invalid.gpg"), spy: importGPGKeyFailed, signal: Pass.importGPGKeyFailed, err_msg : "Bad format" } - ]; + return [{ + "file": Qt.resolvedUrl("../../assets/gpg/test_key.gpg"), + "spy": importGPGKeySucceed, + "signal": Pass.importGPGKeySucceed, + "err_msg": null + }, { + "file": Qt.resolvedUrl("../../assets/gpg/test_key_do_not_exist.gpg"), + "spy": importGPGKeyFailed, + "signal": Pass.importGPGKeyFailed, + "err_msg": "Error reading file" + }, { + "file": Qt.resolvedUrl("../../assets/gpg/test_key_invalid.gpg"), + "spy": importGPGKeyFailed, + "signal": Pass.importGPGKeyFailed, + "err_msg": "Bad state" + }]; + } + + function test_import_key(data) { + var err_msg; + data.signal.connect(function(message) { + err_msg = message; + }); + Pass.importGPGKey(data.file); + data.spy.wait(); + if (data.err_msg) { + verify(err_msg === data.err_msg, "Should return arg msg %1 but return %2".arg(data.err_msg).arg(err_msg)); + } else { + console.info(Qt.resolvedUrl("%1/pubkeyring.pgp".arg(gpg_home))); + verify(TestsUtils.fileExists(Qt.resolvedUrl("%1/pubring.pgp".arg(gpg_home))), "%1/pubring.pgp should be created".arg(gpg_home)); + verify(TestsUtils.fileExists(Qt.resolvedUrl("%1/secring.pgp".arg(gpg_home))), "%1/secring.pgp should be created".arg(gpg_home)); } + } SignalSpy { id: importGPGKeySucceed + target: Pass signalName: "importGPGKeySucceed" } SignalSpy { id: importGPGKeyFailed + target: Pass signalName: "importGPGKeyFailed" } - SignalSpy { - id: importGPGKeyCancelled - target: Pass - signalName: "importGPGKeyCancelled" - } - - function test_import_key(data) { - var err_msg; - - data.signal.connect(function(message) { - err_msg = message; - }); - - Pass.importGPGKey(data.file); - - data.spy.wait(); - if(data.err_msg) { - verify(err_msg === data.err_msg, "Should return arg msg %1 but return %2".arg(data.err_msg).arg(err_msg)); - } - } } diff --git a/tests/units/tst_git.qml b/tests/units/tst_git.qml index 7d4c30a..810fe16 100644 --- a/tests/units/tst_git.qml +++ b/tests/units/tst_git.qml @@ -3,9 +3,9 @@ import QtQuick 2.9 import QtTest 1.2 TestCase { - function test_import_key(){ + function test_import_key() { var homedir = TestUtils.getTempPath(); - Pass + Pass; verify(false); }