From b9b038b1aeae17ce568f2bcde87ac68ed4e483bd Mon Sep 17 00:00:00 2001 From: Quentin Rouland Date: Sat, 1 Feb 2025 13:45:55 +0100 Subject: [PATCH] Rewrite get all key with rnp --- plugins/Pass/jobs/decryptjob.cpp | 1 + plugins/Pass/jobs/decryptjob.h | 35 +++-- plugins/Pass/jobs/getkeysjob.cpp | 17 ++- plugins/Pass/jobs/getkeysjob.h | 39 ++++-- plugins/Pass/jobs/importkeyjob.h | 27 ++-- plugins/Pass/jobs/rmjob.h | 26 ++-- plugins/Pass/jobs/rnpjob.cpp | 41 +++--- plugins/Pass/jobs/rnpjob.h | 125 +++++++++++++---- plugins/Pass/pass.cpp | 6 +- plugins/Pass/pass.h | 5 +- plugins/Pass/passkeymodel.h | 220 ++++++++++++------------------ po/utpass.qrouland.pot | 2 +- tests/units/pass/tst_get_keys.qml | 23 +++- 13 files changed, 327 insertions(+), 240 deletions(-) diff --git a/plugins/Pass/jobs/decryptjob.cpp b/plugins/Pass/jobs/decryptjob.cpp index b135147..23cd022 100644 --- a/plugins/Pass/jobs/decryptjob.cpp +++ b/plugins/Pass/jobs/decryptjob.cpp @@ -9,6 +9,7 @@ DecryptJob::DecryptJob(QString path, QString keyfile): void DecryptJob::run() { + this->load_sec_keyring(); rnp_input_from_path(&keyfile, "secring.pgp")); qFatal("To be implemented !") } diff --git a/plugins/Pass/jobs/decryptjob.h b/plugins/Pass/jobs/decryptjob.h index 2094fd0..2b185d1 100644 --- a/plugins/Pass/jobs/decryptjob.h +++ b/plugins/Pass/jobs/decryptjob.h @@ -1,47 +1,54 @@ #ifndef DECRYPTJOB_H #define DECRYPTJOB_H +#include "rnpjob.h" #include #include /** * @class DecryptJob - * @brief A class to handle decrypt a file in a separate thread. + * @brief A job to handle the decryption of a file in a separate thread. * */ -class DecryptJob : public QThread +class DecryptJob : public RnpJob { Q_OBJECT /** - * @brief The main function that performs the decrypt operation. + * @brief Executes the decryption operation. * - * Handles the process of removing recursively a target path. + * This method performs the actual decryption of the encrypted file specified during + * object construction. The operation is carried out in a separate background thread + * to prevent blocking the main application thread. */ void run() override; signals: /** - * @brief Signal emitted when the decrypt operation is complete. + * @brief Emitted when the decryption operation is complete. * - * @param err A boolean indicating whether an error occurred during removing. - * `true` if an error occurred, `false` if the clone was successful. + * This signal is emitted once the decryption operation finishes, providing the results. + * It indicates whether the decryption was successful and provides the clear-text output + * if the decryption was successful. + * + * @param encrypted_file_path The path to the encrypted file that was decrypted. + * @param clear_txt The decrypted content in clear-text. If an error occurs, this may be empty. */ - void resultReady(const bool err); + void resultReady(QString encrypted_file_path, QString clear_txt); private: - QString m_encryped_file_path; ///< The path of the encrypted file. - QString m_keyfile_path; ///< The path of the key file. + QString m_encrypted_file_path; /**< The path to the encrypted file that is to be decrypted. */ public: /** - * @brief Constructor for the RmJob class. + * @brief Constructs a DecryptJob object with the specified encrypted file. * - * Initializes the DecryptJob with the specified file to decrypt. + * This constructor initializes the DecryptJob with the encrypted file path. The decryption + * operation will be executed in a background thread when the job is started. * - * @param path Path of the file to decrypt. + * @param path The path to the encrypted file that needs to be decrypted. */ DecryptJob(QString path); }; -#endif // DECRYPTJO_H +#endif // DECRYPTJOB_H diff --git a/plugins/Pass/jobs/getkeysjob.cpp b/plugins/Pass/jobs/getkeysjob.cpp index a2fc9a7..135d914 100644 --- a/plugins/Pass/jobs/getkeysjob.cpp +++ b/plugins/Pass/jobs/getkeysjob.cpp @@ -2,6 +2,7 @@ #include "getkeysjob.h" #include #include + extern "C" { #include #include @@ -14,6 +15,14 @@ GetKeysJob::GetKeysJob(QDir rnp_homedir): } +QJsonDocument GetKeysJob::fingerprint_map_key_info(const QString fingerprint) +{ + rnp_key_handle_t handle; + rnp_locate_key(this->m_ffi, "fingerprint", fingerprint.toLocal8Bit().data(), &handle); + char *result; + rnp_key_to_json( handle, 0, &result); + return QJsonDocument::fromJson(result); +} void GetKeysJob::run() { @@ -23,7 +32,13 @@ void GetKeysJob::run() QSet fingerprints = QSet(); this->load_full_keyring(&fingerprints); + auto key_infos = QList(); + QList::iterator i; + for (auto i = fingerprints.begin(), end = fingerprints.end(); i != end; ++i) { + key_infos.append(this->fingerprint_map_key_info(*i)); + } + //Get all infos keys - emit resultSuccess(fingerprints); + emit resultSuccess(key_infos); qDebug() << "[GetKeysJob] Finished Successfully "; } diff --git a/plugins/Pass/jobs/getkeysjob.h b/plugins/Pass/jobs/getkeysjob.h index 1dcdf2d..7ff7413 100644 --- a/plugins/Pass/jobs/getkeysjob.h +++ b/plugins/Pass/jobs/getkeysjob.h @@ -2,13 +2,11 @@ #define GETKEYSJOB_H #include - - #include "rnpjob.h" /** * @class GetKeysJob - * @brief A class to handle get all gpg keys from rings in a separate thread. + * @brief A job to retrieve all GPG keys from keyrings in a separate thread. * */ class GetKeysJob : public RnpJob @@ -16,21 +14,44 @@ class GetKeysJob : public RnpJob Q_OBJECT /** - * @brief The main function that performs the get all keys operation. + * @brief Executes the process of fetching all GPG keys. * - * Handles the process of removing recursively a target path. + * This function performs the task of retrieving all keys from the keyrings. It is executed + * in a separate thread to avoid blocking the main application thread. */ void run() override; signals: - void resultError(const rnp_result_t err); - void resultSuccess(const QSet result); + /** + * @brief Emitted when the key retrieval operation completes successfully. + * + * This signal is emitted when the keys are successfully fetched. It passes a list of + * JSON documents representing the retrieved keys. + * + * @param result A list of QJsonDocument objects containing the key information. + */ + void resultSuccess(const QList result); + +private: + /** + * @brief Retrieves key information for a specific key fingerprint. + * + * This helper function fetches the key data corresponding to the given fingerprint. + * The returned information is packaged in a JSON document. + * + * @param fingerprint The fingerprint of the key to fetch information for. + * @return A QJsonDocument containing the key's information. + */ + QJsonDocument fingerprint_map_key_info(const QString fingerprint); public: /** - * @brief Constructor for the GetKeysJob class. + * @brief Constructs a GetKeysJob object with the specified keyring directory. * - * @param rnp_homedir Rnp home dir that contains the keyrings. + * This constructor initializes the job with the directory containing the keyrings to + * search for GPG keys. + * + * @param rnp_homedir The directory that contains the keyrings. */ GetKeysJob(QDir rnp_homedir); }; diff --git a/plugins/Pass/jobs/importkeyjob.h b/plugins/Pass/jobs/importkeyjob.h index ebaa19b..5b7241d 100644 --- a/plugins/Pass/jobs/importkeyjob.h +++ b/plugins/Pass/jobs/importkeyjob.h @@ -1,12 +1,11 @@ #ifndef IMPORTKEYJOB_H #define IMPORTKEYJOB_H - - #include "rnpjob.h" + /** * @class ImportKeyJob - * @brief A class to handle import a key file in a separate thread. + * @brief A job to handle the import of a key file in a separate thread. * */ class ImportKeyJob : public RnpJob @@ -14,26 +13,34 @@ class ImportKeyJob : public RnpJob Q_OBJECT /** - * @brief The main function that performs the import operation. + * @brief Executes the key import operation. * - * Handles the process of removing recursively a target path. + * This function handles the actual process of importing the GPG key file into the + * keyring. It is executed in a separate thread to prevent UI freezing or blocking + * of the main application thread during the import. */ void run() override; signals: + /** + * @brief Emitted when the key import operation is successful. + * + * This signal is emitted when the key file is successfully imported into the keyring. + */ void resultSuccess(); private: - QString m_key_file_path; ///< The path of the key file to import. + QString m_key_file_path; /**< The path of the key file to import. */ public: /** - * @brief Constructor for the ImportKeyJob class. + * @brief Constructs an ImportKeyJob object with the specified key file and keyring directory. * - * Initializes the ImportKeyJob with the file to import. + * This constructor initializes the ImportKeyJob instance with the directory containing + * the keyrings and the file path of the GPG key to import. * - * @param rnp_homedir Rnp home dir that contains the keyrings. - * @param path Path of the key file to import. + * @param rnp_homedir The directory containing the keyrings. + * @param path The path to the key file to import. */ ImportKeyJob(QDir rnp_homedir, QString path); }; diff --git a/plugins/Pass/jobs/rmjob.h b/plugins/Pass/jobs/rmjob.h index e1c2694..57292e3 100644 --- a/plugins/Pass/jobs/rmjob.h +++ b/plugins/Pass/jobs/rmjob.h @@ -6,7 +6,7 @@ /** * @class RmJob - * @brief A class to handle removing recursively a path in a separate thread. + * @brief A job to handle the recursive removal of a path in a separate thread. * */ class RmJob : public QThread @@ -14,31 +14,37 @@ class RmJob : public QThread Q_OBJECT /** - * @brief The main function that performs the rm operation. + * @brief Executes the recursive remove operation. * - * Handles the process of removing recursively a target path. + * This method performs the recursive removal of the specified target path. + * The operation is performed in the background to prevent blocking of the main + * application thread. */ void run() override; signals: /** - * @brief Signal emitted when the rm operation is complete. + * @brief Emitted when the remove operation completes. * - * @param err A boolean indicating whether an error occurred during removing. - * `true` if an error occurred, `false` if the clone was successful. + * This signal is emitted once the removal process is complete, indicating + * whether the operation succeeded or failed. + * + * @param err A boolean indicating whether an error occurred during the removal. + * `true` if an error occurred, `false` if the operation was successful. */ void resultReady(const bool err); private: - QString m_path; ///< The path to be removed. + QString m_path; /**< The path to be removed. */ public: /** - * @brief Constructor for the RmJob class. + * @brief Constructs an RmJob object with the specified path. * - * Initializes the RmJob with the specified path to be removed. + * This constructor initializes the job with the path of the directory or file to be + * removed. The job will be executed in a separate thread when started. * - * @param path Path to be remove. + * @param path The path to the file or directory that should be removed. */ RmJob(QString path); }; diff --git a/plugins/Pass/jobs/rnpjob.cpp b/plugins/Pass/jobs/rnpjob.cpp index 6f46f2f..e01d7dc 100644 --- a/plugins/Pass/jobs/rnpjob.cpp +++ b/plugins/Pass/jobs/rnpjob.cpp @@ -13,13 +13,13 @@ RnpJob::RnpJob(QDir rnp_homedir): m_rnp_homedir(rnp_homedir) { qRegisterMetaType("rnp_result_t"); - qRegisterMetaType> ("QSet"); - + qRegisterMetaType> ("QList"); + qRegisterMetaType("QString*"); auto ret = rnp_ffi_create(&this->m_ffi, RNP_KEYSTORE_GPG, RNP_KEYSTORE_GPG); - if(ret != RNP_SUCCESS) { + if (ret != RNP_SUCCESS) { qDebug() << "[RnpJob] Err : " << ret; qFatal("Error on rnp ffi init!"); } @@ -28,7 +28,7 @@ RnpJob::RnpJob(QDir rnp_homedir): RnpJob::~RnpJob() { auto ret = rnp_ffi_destroy(this->m_ffi); - if(ret != RNP_SUCCESS) { + if (ret != RNP_SUCCESS) { qDebug() << "[RnpJob] Err : " << ret; qFatal("Something go wrong on rnp ffi detroy"); } @@ -50,7 +50,7 @@ bool RnpJob::passProvider(rnp_ffi_t ffi, } -void RnpJob::load_key_file(QSet *fingerprints, const QString path, const uint32_t flags) +void RnpJob::load_key_file(QSet *result_fingerprints, const QString path, const uint32_t flags) { qDebug() << "[RnpJob] load keyring at" << path; rnp_input_t input = NULL; @@ -62,15 +62,14 @@ void RnpJob::load_key_file(QSet *fingerprints, const QString path, cons 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()); + if(result_fingerprints) { + foreach (const QJsonValue fingerprint, json_document.object()["keys"].toArray()) { + qDebug() << "[RnpJob] Add fingerprint" << fingerprint["fingerprint"].toString(); + result_fingerprints->insert(fingerprint["fingerprint"].toString()); + } } rnp_input_destroy(input); @@ -83,21 +82,21 @@ void RnpJob::load_key_file(QSet *fingerprints, const QString path, cons } -void RnpJob::load_pub_keyring(QSet *fingerprints) +void RnpJob::load_pub_keyring(QSet *result_fingerprints = NULL) { - this->load_key_file(fingerprints, this->pubringPath(), RNP_LOAD_SAVE_PUBLIC_KEYS); - qDebug() << "[RnpJob] pub fingerprints" << *fingerprints; + this->load_key_file(result_fingerprints, this->pubringPath(), RNP_LOAD_SAVE_PUBLIC_KEYS); + qDebug() << "[RnpJob] pub fingerprints" << *result_fingerprints; } -void RnpJob::load_sec_keyring(QSet *fingerprints) +void RnpJob::load_sec_keyring(QSet *result_fingerprints = NULL) { - this->load_key_file(fingerprints, this->secringPath(), RNP_LOAD_SAVE_SECRET_KEYS); - qDebug() << "[RnpJob] sec fingerprints" << *fingerprints; + this->load_key_file(result_fingerprints, this->secringPath(), RNP_LOAD_SAVE_SECRET_KEYS); + qDebug() << "[RnpJob] sec fingerprints" << *result_fingerprints; } -void RnpJob::load_full_keyring(QSet *fingerprints) +void RnpJob::load_full_keyring(QSet *result_fingerprints = NULL) { - this->load_pub_keyring(fingerprints); - this->load_sec_keyring(fingerprints); - qDebug() << "[RnpJob] full fingerprints" << *fingerprints; + this->load_pub_keyring(result_fingerprints); + this->load_sec_keyring(result_fingerprints); + qDebug() << "[RnpJob] full fingerprints" << *result_fingerprints; } diff --git a/plugins/Pass/jobs/rnpjob.h b/plugins/Pass/jobs/rnpjob.h index a87afe3..f8fe877 100644 --- a/plugins/Pass/jobs/rnpjob.h +++ b/plugins/Pass/jobs/rnpjob.h @@ -8,46 +8,83 @@ extern "C" { } #include - #define terminateOnError(ret) \ -if(ret != RNP_SUCCESS) { \ - qDebug() << "[RnpJob] Err : " << ret; \ - qDebug() << "[RnpJob] Err Msg : " << rnp_result_to_string(ret); \ - emit resultError(ret); \ - return; \ -} \ +if(ret != RNP_SUCCESS) { \ + qDebug() << "[RnpJob] Err : " << ret; \ + qDebug() << "[RnpJob] Err Msg : " << rnp_result_to_string(ret); \ + emit resultError(ret); \ + return; \ +} \ -/** - * @class RmpJob - * @brief A class that manages Git-related tasks using libgit2. + /** + * @class RnpJob + * @brief A base class that manages OpenPGP-related tasks using the librnp library. * - * The GitJob class is used abstraction class to perform rnp (opengpg) operations, - * such as decrypt, encrypt, key managments operations + * The RnpJob class serves as an abstraction for performing OpenPGP (RNP) operations, such as + * encryption, decryption, and key management, using the RNP library. */ -class RnpJob : public QThread + class RnpJob : public QThread { Q_OBJECT signals: + /** + * @brief Signal emitted when an error occurs in the RNP job. + * + * This signal is emitted when an error occurs during an RNP operation, such as key loading + * or encryption/decryption failure. The error code is passed to indicate the specific issue. + * + * @param err The error code returned by the RNP operation. + */ void resultError(const rnp_result_t err); private: + /** + * @brief A callback function for providing the passphrase to RNP. + * + * This static function is used as a callback to provide a passphrase to RNP when required + * during key operations such as decryption or signing. It allows the library to continue the + * operation with the necessary passphrase. + * + * @param ffi The RNP FFI handle. + * @param app_ctx The application context, used for accessing application-specific data. + * @param key The key for which the passphrase is required. + * @param pgp_context The context string (e.g., "decrypt"). + * @param buf The buffer to fill with the passphrase. + * @param buf_len The length of the buffer. + * + * @return true if the passphrase was successfully provided, false otherwise. + */ 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); - 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. + QDir m_rnp_homedir; /**< Directory that contains the keyrings and RNP configuration. */ /** - * @brief Get the path to public keys keyring. + * @brief Loads a key file into the keyring. * - * @return The path to public keys keyring + * This method loads a key file into the keyring, adding keys specified by their fingerprints. + * + * @param result_fingerprints A set to hold the fingerprints of the keys loaded into the keyring. + * @param path The path to the key file. + * @param flags Flags specifying options for loading keys (e.g., overwrite, secret keys, etc.). + */ + void load_key_file(QSet *result_fingerprints, const QString path, const uint32_t flags); + +protected: + rnp_ffi_t m_ffi; /**< RNP FFI (Foreign Function Interface) handle, used for interacting with the RNP library. */ + + /** + * @brief Get the path to the public keyring. + * + * This method returns the file path to the public keyring (where public keys are stored). + * It combines the directory and file name to provide the full path. + * + * @return The path to the public keyring file. */ QString pubringPath() { @@ -55,33 +92,65 @@ protected: } /** - * @brief Get the path to secret keys keyring. + * @brief Get the path to the secret keyring. * - * @return The path to secret keys keyring + * This method returns the file path to the secret keyring (where private keys are stored). + * It combines the directory and file name to provide the full path. + * + * @return The path to the secret keyring file. */ 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); + /** + * @brief Loads the secret keyring into the RNP system. + * + * This method loads the secret keyring file (secring.pgp) into the system, adding keys + * to the keyring based on their fingerprints. + * + * @param result_fingerprints A set that will hold the fingerprints of the loaded secret keys. + */ + void load_sec_keyring(QSet *result_fingerprints); + + /** + * @brief Loads the public keyring into the RNP system. + * + * This method loads the public keyring file (pubring.pgp) into the system, adding keys + * to the keyring based on their fingerprints. + * + * @param result_fingerprints A set that will hold the fingerprints of the loaded public keys. + */ + void load_pub_keyring(QSet *result_fingerprints); + + /** + * @brief Loads both the public and secret keyrings into the RNP system. + * + * This method loads both the public and secret keyring files into the system, adding keys + * to the keyring based on their fingerprints. It is a combined operation for full keyring loading. + * + * @param result_fingerprints A set that will hold the fingerprints of all loaded keys. + */ + void load_full_keyring(QSet *result_fingerprints); public: /** - * @brief Constructor for the RnpJob class. + * @brief Constructs an RnpJob object with the specified RNP home directory. * - * Initializes the RnpJob instance. + * This constructor initializes the RnpJob instance with the directory that contains the + * keyrings and RNP configuration. Keyring files (pubring.pgp and secring.pgp) will be found + * in this directory. * - * @param rnp_homedir Rnp home dir that contains the keyrings. + * @param rnp_homedir The directory containing the RNP keyrings and configuration files. */ RnpJob(QDir rnp_homedir); /** * @brief Destructor for the RnpJob class. * - * Cleans up any resources used by the RnpJob. + * The destructor cleans up any resources used by the RnpJob instance, including releasing + * the RNP FFI handle. */ ~RnpJob(); }; diff --git a/plugins/Pass/pass.cpp b/plugins/Pass/pass.cpp index b0314bf..85ac847 100644 --- a/plugins/Pass/pass.cpp +++ b/plugins/Pass/pass.cpp @@ -140,7 +140,7 @@ bool Pass::importGPGKey(QUrl url) { qInfo() << "[Pass] Import GPG Key from " << url; if (!this->m_sem->tryAcquire(1, 500)) { - qInfo() << "[Pass] A job is already running"; + qInfo() << "[Pass] A command is already running"; return false; } auto job = new ImportKeyJob(this->m_gpg_home, url.toLocalFile()); @@ -169,7 +169,7 @@ bool Pass::getAllGPGKeys() { qInfo() << "[Pass] Get all GPG Keys"; if (!this->m_sem->tryAcquire(1, 500)) { - qInfo() << "[Pass] A job is already running"; + qInfo() << "[Pass] A command is already running"; return false; } this->m_keyring_model = nullptr; @@ -189,7 +189,7 @@ void Pass::slotGetAllGPGKeysError(rnp_result_t err) this->m_sem->release(1); } -void Pass::slotGetAllGPGKeysSucceed(QSet result) +void Pass::slotGetAllGPGKeysSucceed(QList result) { qInfo() << "[Pass] Get all GPG Keys Succeed"; this->m_keyring_model = std::unique_ptr(new PassKeyringModel(result)); diff --git a/plugins/Pass/pass.h b/plugins/Pass/pass.h index 520ed2c..6fc6a0c 100644 --- a/plugins/Pass/pass.h +++ b/plugins/Pass/pass.h @@ -61,7 +61,7 @@ private slots: /** * @brief Slot to handle the succeed result of a GPG key get all keys operation. */ - void slotGetAllGPGKeysSucceed(QSet result); + void slotGetAllGPGKeysSucceed(QList result); /** * @brief Slot to handle the result of a delete Password Store operation. @@ -146,7 +146,8 @@ signals: private: QString m_password_store; /**< The path to the password store. */ QString m_gpg_home; /**< The path to the gpg home. */ - std::unique_ptr m_keyring_model; /**< Meta data on the keyring uid, name, secrecy ... of the availble keys. */ + std::unique_ptr + m_keyring_model; /**< Meta data on the keyring uid, name, secrecy ... of the availble keys. */ PassphraseProvider *m_passphrase_provider; /**< Pointer on passphrase povider for operations using secret keys. */ std::unique_ptr m_sem; /**< Semaphore for managing concurrent operations. */ diff --git a/plugins/Pass/passkeymodel.h b/plugins/Pass/passkeymodel.h index 88d0d5d..618eaf8 100644 --- a/plugins/Pass/passkeymodel.h +++ b/plugins/Pass/passkeymodel.h @@ -3,185 +3,133 @@ #include #include +#include +#include #include -#include - -using namespace GpgME; - -/** - * @class UserIdModel - * @brief A model representing a user ID associated with a GPG key. - * - * This class encapsulates the user ID information (UID) for a GPG key, providing access - * to the UID's identifier, name, and email. - */ -class UserIdModel : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString uid READ uid CONSTANT) - Q_PROPERTY(QString name READ name CONSTANT) - Q_PROPERTY(QString email READ email CONSTANT) - -private: - UserID m_user_id; /**< The GPG UserID associated with the model. */ - -public: - /** - * @brief Constructs a UserIdModel for the given UserID. - * @param key The GPG UserID to model. - */ - UserIdModel(UserID key) : m_user_id(key) {} - - /** - * @brief Gets the unique identifier (UID) for this user ID. - * @return The UID as a QString. - */ - QString uid() const - { - return QString::fromUtf8(m_user_id.id()); - }; - - /** - * @brief Gets the name associated with this user ID. - * @return The name as a QString. - */ - QString name() const - { - return QString::fromUtf8(m_user_id.name()); - }; - - /** - * @brief Gets the email associated with this user ID. - * @return The email as a QString. - */ - QString email() const - { - return QString::fromUtf8(m_user_id.email()); - }; -}; /** * @class PassKeyModel - * @brief A model representing a GPG key. + * @brief A model representing a GPG (GNU Privacy Guard) key. * - * This class encapsulates the properties of a GPG key, including its key ID, associated - * user IDs, secret key status, and expiration status. It is used as a model for managing - * GPG keys within an application, providing access to the key's data and its associated user IDs. + * This class encapsulates the properties of a GPG key, such as its key ID, associated + * user IDs, secret key status, and expiration status. It is used within an application + * to manage and represent GPG keys, providing easy access to key data and related user information. + * + * This class supports properties such as the key's fingerprint, key ID, user IDs, and whether + * the key has a secret key associated with it. */ class PassKeyModel : public QObject { Q_OBJECT - Q_PROPERTY(QString fingeprint READ fingeprint MEMBER m_fingeprint CONSTANT) - // Q_PROPERTY(QString uid READ uid CONSTANT) - // Q_PROPERTY(QList userIds READ userIds CONSTANT) - // Q_PROPERTY(bool isSecret READ isSecret CONSTANT) - // Q_PROPERTY(bool isExpired READ isExpired CONSTANT) + + Q_PROPERTY(QString fingerprint MEMBER m_fingerprint CONSTANT) + Q_PROPERTY(QString keyid MEMBER m_keyid CONSTANT) + Q_PROPERTY(QVariant userids MEMBER m_userids CONSTANT) + Q_PROPERTY(bool hasSecret MEMBER m_hasSecret CONSTANT) private: - QString m_fingeprint; /**< The key fingeprint. */ + QString m_fingerprint; /**< The fingerprint of the GPG key, used to uniquely identify the key. */ + QString m_keyid; /**< The unique ID associated with the GPG key. */ + QVariant m_userids; /**< A list of user IDs associated with the GPG key. */ + bool m_hasSecret; /**< Indicates whether the GPG key has an associated secret key. */ public: /** - * @brief Constructs a PassKeyModel for the given GPG key. - * @param key The GPG key to model. + * @brief Constructs a PassKeyModel object using the provided GPG key information. + * + * This constructor initializes the PassKeyModel based on a JSON document containing GPG key data. + * The key data typically includes the key's fingerprint, key ID, associated user IDs, and secret key status. + * + * @param key_info A JSON document containing the GPG key data. */ - PassKeyModel(QString fingeprint) : m_fingeprint(fingeprint) {} - - - QString fingeprint() const + PassKeyModel(QJsonDocument key_info) { - return m_fingeprint; - }; + this->m_fingerprint = key_info["fingerprint"].toString(); + qDebug() << "fingerprint : " << this->m_fingerprint; - // /** - // * @brief Gets the GPG key associated with this model. - // * @return The GPG key. - // */ - // Key key() const - // { - // return m_key; - // }; + this->m_keyid = key_info["keyid"].toString(); + qDebug() << "keyid : " << this->m_keyid; - // /** - // * @brief Gets the unique identifier (UID) for this GPG key. - // * @return The UID as a QString. - // */ - // QString uid() const - // { - // return QString::fromUtf8(m_key.keyID()); - // }; + auto user_ids_json_array = key_info["userids"].toArray(); + auto userids = QList(); + for (auto i = user_ids_json_array.begin(), end = user_ids_json_array.end(); i != end; ++i) { + userids.append((*i).toString()); + } + this->m_userids = QVariant(userids); + qDebug() << "userids : " << this->m_userids; - // /** - // * @brief Gets the list of user IDs associated with this GPG key. - // * @return A list of UserIdModel objects representing the user IDs. - // */ - // QList userIds() const - // { - // auto user_ids = m_key.userIDs(); - // QList ret; - // std::for_each(user_ids.begin(), user_ids.end(), [&ret](UserID k) { - // ret.append(new UserIdModel(k)); - // }); - // return ret; - // }; - - // /** - // * @brief Checks if the GPG key is a secret key. - // * @return True if the key is a secret key, false otherwise. - // */ - // bool isSecret() const - // { - // return m_key.hasSecret(); - // }; - - // /** - // * @brief Checks if the GPG key is expired. - // * @return True if the key is expired, false otherwise. - // */ - // bool isExpired() const - // { - // return m_key.isExpired(); - // }; + this->m_hasSecret = key_info["secret key"]["present"].toBool(); + qDebug() << "hasSecret : " << this->m_hasSecret; + } }; /** - * @class PassKeyModel - * @brief A model representing a GPG key. + * @class PassKeyringModel + * @brief A model representing a collection of GPG keys. * - * This class encapsulates the properties of a GPG key, including its key ID, associated - * user IDs, secret key status, and expiration status. It is used as a model for managing - * GPG keys within an application, providing access to the key's data and its associated user IDs. + * This class serves as a container for multiple GPG keys, typically representing an entire + * keyring. It provides functionality to manage and retrieve keys, such as fetching all keys + * in the keyring and determining the length of the keyring. + * + * The class also includes logic to distinguish between primary and sub keys, with an option + * to ignore subkeys if desired. */ class PassKeyringModel : public QObject { Q_OBJECT + + Q_PROPERTY(QList keys MEMBER m_keys CONSTANT) Q_PROPERTY(int length READ length CONSTANT) private: - QList m_keys; + QList m_keys; /**< A list of PassKeyModel objects representing the keys in the keyring. */ public: /** - * @brief Constructs a PassKeyModel for the given GPG key. - * @param key The GPG key to model. + * @brief Constructs a PassKeyringModel from a list of GPG key JSON documents. + * + * This constructor initializes the PassKeyringModel by parsing a list of JSON documents + * that represent multiple GPG keys. It filters out subkeys and only retains primary keys + * for inclusion in the keyring. + * + * @param key_infos A list of JSON documents representing GPG keys. */ - PassKeyringModel(QSet fingeprints) + PassKeyringModel(QList key_infos) { - QSet::iterator i; - for (auto i = fingeprints.begin(), end = fingeprints.end(); i != end; ++i) { - this->m_keys.append(new PassKeyModel(*i)); + for (auto i = key_infos.begin(), end = key_infos.end(); i != end; ++i) { + qDebug() << *i; + + // Ignore subkeys and only add primary keys to the model. + if ((*i)["primary key grip"].isUndefined()) { + this->m_keys.append(new PassKeyModel(*i)); + } else { + qDebug() << "Subkey info " << (*i)["keyid"].toString() << "ignored"; + } } - qDebug() << "Test : " << this->m_keys; } - ~PassKeyringModel() { + /** + * @brief Destructor for PassKeyringModel. + * + * Cleans up dynamically allocated PassKeyModel objects within the keyring. + */ + ~PassKeyringModel() + { qDeleteAll(this->m_keys); } - int length() { - qDebug() << "Test2 : " << this->m_keys.length(); + /** + * @brief Retrieves the number of keys in the keyring. + * + * This function returns the number of primary keys present in the keyring. + * + * @return The number of keys in the keyring. + */ + int length() + { return this->m_keys.length(); } }; + #endif diff --git a/po/utpass.qrouland.pot b/po/utpass.qrouland.pot index ff6280d..056400a 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-30 22:45+0100\n" +"POT-Creation-Date: 2025-02-01 14:08+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/tests/units/pass/tst_get_keys.qml b/tests/units/pass/tst_get_keys.qml index 9ba8d8b..71c04e2 100644 --- a/tests/units/pass/tst_get_keys.qml +++ b/tests/units/pass/tst_get_keys.qml @@ -4,21 +4,26 @@ import QtTest 1.2 import TestsUtils 1.0 PassTestCase { - function init_data() { - //TODO some additionanl error test + //TODO some additionanl error test + function init_data() { return [{ "spy": getAllGPGKeysSucceed, "signal": Pass.getAllGPGKeysSucceed, "err_msg": null, "add_home_gpg_data": false, - "nb_keys": 0 + "keys": [] }, { "spy": getAllGPGKeysSucceed, "signal": Pass.getAllGPGKeysSucceed, "err_msg": null, "add_home_gpg_data": true, - "nb_keys": 2 + "keys": [{ + "fingerprint": "F97476B6FA58A84B004E4616D4BAF1FDB7BA9ECC", + "keyid": "D4BAF1FDB7BA9ECC", + "userids": "UTPass Test ", + "hasSecret": true + }] }]; } @@ -32,7 +37,15 @@ PassTestCase { }); 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)); + verify(keys.length === data.keys.length, "Nb keys %1 but was excepted %2".arg(keys.length).arg(data.nb_keys)); + for (var i = 0; i < keys.length; i++) { + console.info(keys.keys[i]); + console.info(keys.keys[i].keyid); + verify(keys.keys[i].fingerprint === data.keys[i].fingerprint, "fingerprint is %1 but was excepted %2".arg(keys.keys[i].fingerprint).arg(data.keys[i].fingerprint)); + verify(keys.keys[i].keyid === data.keys[i].keyid, "keyid is %1 but was excepted %2".arg(keys.keys[i].keyid).arg(data.keys[i].keyid)); + verify(keys.keys[i].userids[0] === data.keys[i].userids, "userids is %1 but was excepted %2".arg(keys.keys[i].userids[0]).arg(data.keys[i].userids)); + verify(keys.keys[i].hasSecret === data.keys[i].hasSecret, "hasSecret is %1 but was excepted %2".arg(keys.keys[i].hasSecret).arg(data.keys[i].hasSecret)); + } } SignalSpy {