1
0
mirror of https://github.com/QRouland/UTPass.git synced 2025-04-21 21:46:31 +00:00

Compare commits

..

No commits in common. "0cf07b1b7ad0acb14e882841ed01595d357a9994" and "ba52ddac5cf202799cd58179b53843a5af6a16db" have entirely different histories.

21 changed files with 93 additions and 132 deletions

View File

@ -1,24 +0,0 @@
# 0.0.3: Git Initial Support and Move to RNP
- Port app to Focal
- Improve UI :
- Follow human interface guidelines
- Fix various components color to work with black theme
- Rewrite of Pass Plugin:
- Move from GPGMe to RNP for GPG operations due to issues running GPG agent in a confined app
- Improve multithreading code for GPG operations
- Add Git HTTP and HTTP AUTH clone for password store import feature
- Add delete password store feature
# 0.0.2 : Added translations
- Added French by Anne17 and Reda
- Added Catalan by Joan CiberSheep
- Added Spanish by Advocatux and Reda
Thanks to all the translators !
# 0.0.1 : Initial Release
- Import of gpg keys via file
- Suppression of gpg keys
- Import of password via a password store zip
- Password decryption
- Password copy to clipboard

View File

@ -20,13 +20,10 @@ Assuming that there are already passwords in another device using [ZX2C4s pas
Export gpg private keys in order to decrypt passwords:
```
gpg --output keys.gpg --export-secret-keys <key>
gpg --output keys.gpg --export-secret-keys
```
If your password store is already hosted in a Git repository that provides HTTP or HTTP with authentication for cloning (we're working to have support for SSH soon), you can clone your password store directly from the app.
Otherwise, follow these steps to export it to a ZIP file for importing.
Export passwords to a ZIP archive, assuming they reside in the *.password-store* folder:
Export passwords, assuming they reside in *.password-store* folder:
```
zip passwords.zip -r .password-store/
```
@ -47,9 +44,11 @@ See [Contributing wiki page](https://github.com/QRouland/UTPass/wiki/Contributin
Some useful links related to UTPass development :
* [Ubports](https://ubports.com/) : Ubuntu Touch Community
* [ZX2C4s pass command line application](https://www.passwordstore.org/) : The standard unix password manager.
* [ZX2C4s pass command line application](https://www.passwordstore.org/) : the standard unix password manager.
* [Clickable](https://github.com/bhdouglass/clickable) : Compile, build, and deploy Ubuntu Touch click packages
* [Rnp](https://github.com/rnpgp/rnp) : High performance C++ OpenPGP library used by Mozilla Thunderbird
* [GnuPG](https://gnupg.org/): The GNU Privacy Guard
* [Gpgme](https://www.gnupg.org/software/gpgme/index.html) : GnuPG Made Easy (GPGME) is a library designed to make access to GnuPG easier for applications
## License

View File

@ -10,7 +10,7 @@
"content-hub": "UTPass.contenthub"
}
},
"version": "0.0.3",
"version": "0.0.3-dev",
"maintainer": "Quentin Rouland <quentin@qrouland.com>",
"framework" : "ubuntu-sdk-20.04"
}

View File

@ -22,13 +22,13 @@ CloneJob::CloneJob(QString url, QString path, cred_type cred):
void CloneJob::run()
{
auto tmp_dir = this->cloneSetup();
auto ret = this->clone(this->m_url, tmp_dir.absolutePath(), this->m_cred, this->credentialsCB);
if (ret) {
auto err = this->clone(this->m_url, tmp_dir.absolutePath(), this->m_cred, this->credentialsCB);
if (!err) {
this->moveToDestination(tmp_dir, this->m_path);
}
this->cloneCleanUp(tmp_dir);
this->cloneTearDown(tmp_dir);
emit resultReady(!ret); // TODO Clean error handling to return specifics errors for the ui
emit resultReady(err); // TODO Clean error handling to return specifics errors for the ui
}
@ -37,26 +37,26 @@ QDir CloneJob::cloneSetup()
QDir tmp_dir(QStandardPaths::writableLocation( QStandardPaths::CacheLocation).append("/clone"));
tmp_dir.removeRecursively();
qDebug() << "[CloneJob]Temp dir path is " << tmp_dir.absolutePath();
qDebug() << "Temp dir path is " << tmp_dir.absolutePath();
return tmp_dir;
}
bool CloneJob::cloneCleanUp(QDir tmp_dir)
bool CloneJob::cloneTearDown(QDir tmp_dir)
{
return tmp_dir.removeRecursively();
}
bool CloneJob::moveToDestination(QDir tmp_dir, QString path)
{
qDebug() << "[CloneJob] Removing password_store " << path;
qDebug() << "Removing password_store " << path;
QDir destination_dir(path);
destination_dir.removeRecursively();
qDebug() << "[CloneJob] Moving cloned content to destination dir";
qDebug() << "Moving cloned content to destination dir";
QDir dir;
qDebug() << "[CloneJob]" << tmp_dir.absolutePath() << " to " << destination_dir.absolutePath();
qDebug() << tmp_dir.absolutePath() << " to " << destination_dir.absolutePath();
return dir.rename(tmp_dir.absolutePath(), destination_dir.absolutePath()); // TODO Better error handling
}
@ -69,17 +69,12 @@ bool CloneJob::clone(QString url, QString path, cred_type cred, git_cred_acquire
opts.fetch_opts.callbacks.payload = &cred;
int ret = git_clone(&repo, url.toLocal8Bit().data(), path.toLocal8Bit().data(), &opts);
if (ret == GIT_EUSER ) {
qDebug() << "[CloneJob] CallBack Error";
} else if (ret != 0) {
auto err = git_error_last(); // TODO Better error handling for return ui messages
if (err) {
qDebug() << "[CloneJob]" << git_error_last()->message;
}
if (ret != 0) {
qDebug() << git_error_last()->message;
}
if (repo) {
git_repository_free(repo);
}
return ret == 0;
}// TODO Better error handling
return ret != 0;
}

View File

@ -70,7 +70,7 @@ private:
* @param tmp_dir The temporary directory to tear down.
* @return `true` if the teardown was successful, `false` otherwise.
*/
static bool cloneCleanUp(QDir tmp_dir);
static bool cloneTearDown(QDir tmp_dir);
/**
* @brief Clones a repository from a specified URL.

View File

@ -25,27 +25,27 @@ int GitJob::credentialsCB(git_cred **out, const char *url, const char *username_
auto v = overload {
[](const HTTP & x)
{
qDebug() << "[GitJob] credentialsCB : HTTP ";
qWarning() << "[GitJob] credentialsCB : callback should never be call for HTTP ";
qDebug() << "credentialsCB : HTTP ";
qWarning() << "credentialsCB : callback should never be call for HTTP ";
return (int) GIT_EUSER;
},
[&out, &username_from_url](const HTTPUserPass & x)
{
qDebug() << "[GitJob] credentialsCB : HTTPUserPass ";
qDebug() << "credentialsCB : HTTPUserPass ";
if (!username_from_url) {
qWarning() << "[GitJob] credentials_cb : no username provided ";
qWarning() << "credentials_cb : no username provided ";
return (int) GIT_EUSER;
}
return git_cred_userpass_plaintext_new(out, username_from_url, x.pass.toLocal8Bit().constData());
},
[](const SSHPass & x)
{
qWarning() << "[GitJob] credentials_cb : SSHAuth to be implemented ";
qWarning() << "credentials_cb : SSHAuth to be implemented ";
return (int) GIT_EUSER;
}, // TODO
[](const SSHKey & x)
{
qWarning() << "[GitJob] credentials_cb : SSHKey to be implemented ";
qWarning() << "credentials_cb : SSHKey to be implemented ";
return (int) GIT_EUSER;
} // TODO
};

View File

@ -35,7 +35,7 @@ void DecryptJob::run()
ret = rnp_output_memory_get_buf(output, &buf, &buf_len, false);
}
if (ret == RNP_SUCCESS) {
data = QString::fromUtf8((char*)buf, buf_len);
data = QString::fromUtf8((char*)buf);
}
rnp_input_destroy(input);

View File

@ -95,12 +95,12 @@ void Pass::slotShowSucceed(QString encrypted_file_path, QString plain_text)
bool Pass::deletePasswordStore()
{
qInfo() << "[Pass] Delete Password Store at" << this->m_password_store;
qInfo() << "[Pass] Delete Password Store at" << this->password_store();
if (!this->m_sem->tryAcquire(1, 500)) {
qInfo() << "[Pass] A command is already running";
return false;
}
auto job = new RmJob(this->m_password_store);
auto job = new RmJob(this->password_store());
connect(job, &RmJob::resultReady, this, &Pass::slotDeletePasswordStoreResult);
connect(job, &RmJob::finished, job, &QObject::deleteLater);
job->start();
@ -109,7 +109,6 @@ bool Pass::deletePasswordStore()
void Pass::slotDeletePasswordStoreResult(bool err)
{
this->initPasswordStore(); // reinit an empty password-store
if (err) {
qInfo() << "[Pass] delete Password Store Failed";
emit deletePasswordStoreFailed("failed to delete password store");

View File

@ -22,8 +22,8 @@ extern "C" {
class Pass : public QObject
{
Q_OBJECT
Q_PROPERTY(QString password_store MEMBER m_password_store WRITE set_password_store )
Q_PROPERTY(QString gpg_home MEMBER m_gpg_home WRITE set_gpg_home )
Q_PROPERTY(QString password_store MEMBER m_password_store READ password_store WRITE set_password_store )
Q_PROPERTY(QString gpg_home MEMBER m_gpg_home READ gpg_home WRITE set_gpg_home )
private slots:
/**
@ -166,6 +166,15 @@ public:
*/
Pass();
/**
* @brief Gets the path to the password store.
* @return The path to the password store.
*/
QString password_store() const
{
return this->m_password_store;
};
/**
* @brief Set the path to the password store.
* @param The path to the password store.
@ -176,6 +185,15 @@ public:
this->m_password_store = password_store;
};
/**
* @brief Gets the path to the gpg home.
* @return The path to the gpg home.
*/
QString gpg_home() const
{
return this->m_gpg_home;
};
/**
* @brief Set the path to the gpg hom.
* @param The path to the gpg hom

View File

@ -52,5 +52,6 @@ void UnzipJob::run()
qDebug() << dir_import_path << " to " << this->m_dir_out;
auto ret = dir.rename(dir_import_path, this->m_dir_out.absolutePath());
tmp_dir.removeRecursively();;
emit resultReady(!ret);
emit resultReady(ret);
}

View File

@ -29,7 +29,7 @@ void Utils::unzipResult(bool err)
qDebug() << "Unzip Result";
if (err) {
qInfo() << "Unzip Failed";
emit unzipFailed();
emit unzipFailed("failed to unzip archive");
} else {
qInfo() << "Unzip Succeed";
@ -45,14 +45,3 @@ QString Utils::manifestPath()
qInfo() << "Manifest path : " << path;
return path;
}
bool Utils::rmFile(QUrl file_url)
{
return QFile::remove(file_url.toLocalFile());
}
bool Utils::rmDir(QUrl dir_url)
{
QDir dir(dir_url.toLocalFile());
return dir.removeRecursively();
}

View File

@ -34,8 +34,9 @@ signals:
/**
* @brief Emitted when the unzipping operation fails.
* @param message The error message describing the failure.
*/
void unzipFailed();
void unzipFailed(QString message);
private:
std::unique_ptr<QSemaphore> m_sem; /**< Semaphore for managing concurrent operations. */
@ -47,14 +48,19 @@ public:
Utils();
/**
* @brief Start a job to unzips a ZIP file to the specified output directory.
* @brief Unzips a ZIP file to the specified output directory.
*
* This method extracts the contents of a ZIP file from the specified URL and saves them to the provided
* output directory path.
*
* @param zip_url The URL of the ZIP file to unzip.
* @param dir_out The output directory where the contents of the ZIP file should be extracted.
* @return `true` if the unzipping job is started successfullly, `false` otherwise.
* @return `true` if the unzipping operation was successful, `false` otherwise.
*/
Q_INVOKABLE bool unzip(QUrl zip_url, QString dir_out);
/**
* @brief Retrieves the path to the manifest data.
*
@ -64,22 +70,6 @@ public:
*/
Q_INVOKABLE QString manifestPath();
/**
* @brief Removes a file located at the specified URL.
*
* @param file_url The URL of the file to remove.
* @return `true` if the file was successfully removed; `false` otherwise.
*/
Q_INVOKABLE bool rmFile(QUrl file_url);
/**
* @brief Removes a directory located at the specified URL.
*
* @param dir_url The URL of the directory to remove.
* @return `true` if the directory was successfully removed; `false` otherwise.
*/
Q_INVOKABLE bool rmDir(QUrl dir_url);
};
#endif

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: utpass.qrouland\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-04 17:49+0100\n"
"POT-Creation-Date: 2025-02-03 21:35+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -122,8 +122,8 @@ msgid "You're are about to delete<br>the current Password Store.<br>Continue ?"
msgstr ""
#: ../qml/pages/settings/DeleteRepo.qml:56
#: ../qml/pages/settings/ImportZip.qml:66
#: ../qml/pages/settings/InfoKeys.qml:174
#: ../qml/pages/settings/ImportZip.qml:64
#: ../qml/pages/settings/InfoKeys.qml:170
#: ../qml/pages/settings/git/ImportGitClone.qml:56
msgid "Yes"
msgstr ""
@ -137,37 +137,37 @@ msgid "Password Store deleted !"
msgstr ""
#: ../qml/pages/settings/DeleteRepo.qml:90
#: ../qml/pages/settings/InfoKeys.qml:216
#: ../qml/pages/settings/InfoKeys.qml:212
msgid "Info Keys"
msgstr ""
#: ../qml/pages/settings/ImportKeyFile.qml:61
#: ../qml/pages/settings/ImportKeyFile.qml:59
msgid "Key import failed !"
msgstr ""
#: ../qml/pages/settings/ImportKeyFile.qml:70
#: ../qml/pages/settings/ImportKeyFile.qml:68
msgid "Key successfully imported !"
msgstr ""
#: ../qml/pages/settings/ImportKeyFile.qml:81
#: ../qml/pages/settings/ImportKeyFile.qml:79
msgid "GPG Key Import"
msgstr ""
#: ../qml/pages/settings/ImportZip.qml:65
#: ../qml/pages/settings/ImportZip.qml:63
msgid ""
"Importing a new zip will delete<br>any existing password store!<br>Continue ?"
msgstr ""
#: ../qml/pages/settings/ImportZip.qml:79
#: ../qml/pages/settings/ImportZip.qml:77
msgid "Password store import failed !"
msgstr ""
#: ../qml/pages/settings/ImportZip.qml:88
#: ../qml/pages/settings/ImportZip.qml:86
#: ../qml/pages/settings/git/ImportGitClone.qml:78
msgid "Password store sucessfully imported !"
msgstr ""
#: ../qml/pages/settings/ImportZip.qml:100
#: ../qml/pages/settings/ImportZip.qml:98
msgid "Zip Password Store Import"
msgstr ""
@ -179,27 +179,27 @@ msgstr ""
msgid "Key ID :"
msgstr ""
#: ../qml/pages/settings/InfoKeys.qml:124
msgid "User IDs : "
#: ../qml/pages/settings/InfoKeys.qml:120
msgid "Users IDs : "
msgstr ""
#: ../qml/pages/settings/InfoKeys.qml:151
#: ../qml/pages/settings/InfoKeys.qml:147
msgid "Delete this key"
msgstr ""
#: ../qml/pages/settings/InfoKeys.qml:173
#: ../qml/pages/settings/InfoKeys.qml:169
msgid "You're are about to delete<br>%1.<br>Continue ?"
msgstr ""
#: ../qml/pages/settings/InfoKeys.qml:187
#: ../qml/pages/settings/InfoKeys.qml:183
msgid "Key removal failed !"
msgstr ""
#: ../qml/pages/settings/InfoKeys.qml:196
#: ../qml/pages/settings/InfoKeys.qml:192
msgid "Key successfully deleted !"
msgstr ""
#: ../qml/pages/settings/InfoKeys.qml:208
#: ../qml/pages/settings/InfoKeys.qml:204
msgid "An Error occured getting GPG keys !"
msgstr ""

View File

@ -19,14 +19,12 @@ Item {
Rectangle {
anchors.fill: parent
color: theme.palette.normal.background
Text {
text: copyText.text
anchors.left: parent.left
anchors.leftMargin: units.gu(2)
anchors.verticalCenter: parent.verticalCenter
color: theme.palette.normal.backgroundText
}
Icon {

View File

@ -14,7 +14,7 @@ Component {
color: theme.palette.normal.background
Text {
text: fileIsDir ? fileName : fileName.slice(0, -4) // remove .gpg if it's a file
text: fileBaseName
anchors.left: parent.left
anchors.leftMargin: units.gu(2)
anchors.verticalCenter: parent.verticalCenter

View File

@ -26,7 +26,6 @@ Page {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
color: theme.palette.normal.background
Flow {
id: container

View File

@ -77,8 +77,8 @@ Page {
SuccessDialog {
textSuccess: i18n.tr("Password Store deleted !")
onDialogClosed: {
pageStack.clear();
pageStack.push(Qt.resolvedUrl("../PasswordList.qml"));
pageStack.pop();
pageStack.pop();
}
}

View File

@ -30,12 +30,10 @@ Page {
console.log(importKeyFilePage.activeTransfer.items[0].url);
var status = Pass.importGPGKey(importKeyFilePage.activeTransfer.items[0].url);
Pass.importGPGKeySucceed.connect(function() {
Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url);
importKeyFilePage.activeTransfer = null;
PopupUtils.open(dialogImportKeyPageSucess);
});
Pass.importGPGKeyFailed.connect(function(message) {
Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url);
importKeyFilePage.activeTransfer = null;
PopupUtils.open(dialogImportKeyPageError);
});

View File

@ -32,14 +32,12 @@ Page {
if (importZipPage.activeTransfer.state === ContentTransfer.Charged) {
console.log("Charged");
console.log(importZipPage.activeTransfer.items[0].url);
var status = Utils.unzip(importZipPage.activeTransfer.items[0].url, Pass.password_store);
var status = Utils.unzip(importZipPage.activeTransfer.items[0].url, Pass.getPasswordStore());
Utils.unzipSucceed.connect(function() {
Utils.rmFile(importZipPage.activeTransfer.items[0].url);
importZipPage.activeTransfer = null;
PopupUtils.open(dialogImportZipPageSuccess);
});
Utils.unzipFailed.connect(function() {
Utils.rmFile(importZipPage.activeTransfer.items[0].url);
Utils.unzipFailed.connect(function(message) {
importZipPage.activeTransfer = null;
PopupUtils.open(dialogImportZipPageError);
});
@ -87,8 +85,8 @@ Page {
SuccessDialog {
textSuccess: i18n.tr("Password store sucessfully imported !")
onDialogClosed: {
pageStack.clear();
pageStack.push(Qt.resolvedUrl("../PasswordList.qml"));
pageStack.pop();
pageStack.pop();
}
}

View File

@ -89,10 +89,11 @@ Page {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: {
if (!model.modelData)
if (!model.modelData) {
"";
else
} else {
model.modelData.keyid;
}
}
color: theme.palette.normal.backgroundText
}
@ -121,7 +122,7 @@ Page {
width: parent.width
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: i18n.tr('User IDs : ')
text: i18n.tr('Users IDs : ')
color: theme.palette.normal.backgroundText
}

View File

@ -77,8 +77,8 @@ Page {
SuccessDialog {
textSuccess: i18n.tr("Password store sucessfully imported !")
onDialogClosed: {
pageStack.clear();
pageStack.push(Qt.resolvedUrl("../../PasswordList.qml"));
pageStack.pop();
pageStack.pop();
}
}