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. "2409f33f5959a24dadfe0e0b086522c61a560398" and "50441e0daf58de4b251f55431d0d7904f2feb6bf" have entirely different histories.

15 changed files with 91 additions and 252 deletions

View File

@ -2,7 +2,7 @@
- Port app to Focal - Port app to Focal
- Improve UI : - Improve UI :
* Follow human interface guidelines * Follow human interface guidelines
* Fix various components color to work with dark theme * Fix various components color to work with black theme
- Rewrite of Pass Plugin: - Rewrite of Pass Plugin:
* Move from GPGMe to RNP for GPG operations due to issues running GPG agent in a confined app * Move from GPGMe to RNP for GPG operations due to issues running GPG agent in a confined app
* Improve multithreading code for GPG operations * Improve multithreading code for GPG operations

View File

@ -4,7 +4,7 @@ kill: UTPass
scripts: scripts:
style: >- style: >-
echo 'Running Astyle :' && astyle --options=.astylerc --recursive '*.cpp,*.h' --exclude=build --exclude=libs && echo 'Running QmlFormat' && find . -name "*.qml" -exec qmlformat -i {} \; && echo 'Success' echo 'Running Astyle :' && astyle --options=.astylerc --recursive '*.cpp,*.h' --exclude=build && echo 'Running QmlFormat' && find . -name "*.qml" -exec qmlformat -i {} \; && echo 'Success'
dependencies_target: dependencies_target:

View File

@ -1,8 +1,7 @@
#include <QUrl> #include <QUrl>
#include <QtCore/QStandardPaths> #include <QtCore/QStandardPaths>
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QDirIterator>
#include <QtConcurrent/QtConcurrent>
#include "jobs/decryptjob.h" #include "jobs/decryptjob.h"
#include "jobs/deletekeyjob.h" #include "jobs/deletekeyjob.h"
#include "jobs/getkeysjob.h" #include "jobs/getkeysjob.h"
@ -62,35 +61,8 @@ void Pass::initPasswordStore()
qInfo() << "[Pass] Password Store is :" << m_password_store; qInfo() << "[Pass] Password Store is :" << m_password_store;
} }
void Pass::lsJob()
{
QDirIterator it(this->m_password_store, QStringList() << "*.gpg", QDir::Files, QDirIterator::Subdirectories);
QList<QString> ret;
while (it.hasNext()) {
QFile f(it.next());
QString fname = f.fileName();
fname.remove(0, this->m_password_store.length() + 1); // remove system path
ret.append(fname);
}
qInfo() << "[Pass] ls Succeed";
emit lsSucceed(ret);
this->m_sem->release(1);
}
bool Pass::ls()
{
qInfo() << "[Pass] ls";
if (!this->m_sem->tryAcquire(1, 500)) {
qInfo() << "[Pass] A command is already running";
return false;
}
QtConcurrent::run(this, &Pass::lsJob );
return true;
}
bool Pass::show(QUrl url) bool Pass::show(QUrl url)
{ {
qInfo() << "[Pass] Show";
if (!this->m_sem->tryAcquire(1, 500)) { if (!this->m_sem->tryAcquire(1, 500)) {
qInfo() << "[Pass] A command is already running"; qInfo() << "[Pass] A command is already running";
return false; return false;
@ -139,7 +111,7 @@ void Pass::slotDeletePasswordStoreResult(bool err)
{ {
this->initPasswordStore(); // reinit an empty password-store this->initPasswordStore(); // reinit an empty password-store
if (err) { if (err) {
qInfo() << "[Pass] Delete Password Store Failed"; qInfo() << "[Pass] delete Password Store Failed";
emit deletePasswordStoreFailed("failed to delete password store"); emit deletePasswordStoreFailed("failed to delete password store");
} else { } else {
qInfo() << "[Pass] Delete Password Store Succeed"; qInfo() << "[Pass] Delete Password Store Succeed";

View File

@ -118,9 +118,6 @@ signals:
*/ */
void showSucceed(QString name, QString text); void showSucceed(QString name, QString text);
void lsSucceed(QList<QString>);
/** /**
* @brief Emitted when showing a password fails. * @brief Emitted when showing a password fails.
* @param message The error message describing the failure. * @param message The error message describing the failure.
@ -163,8 +160,6 @@ private:
*/ */
void initPasswordStore(); void initPasswordStore();
void lsJob();
public: public:
/** /**
* @brief Constructs the Pass object. * @brief Constructs the Pass object.
@ -234,12 +229,6 @@ public:
// Password store-related methods // Password store-related methods
/**
* @brief Get the list of password.
* @return The list of password in the password store.
*/
Q_INVOKABLE bool ls();
/** /**
* @brief Launch the job to shows the password associated with the specified URL. * @brief Launch the job to shows the password associated with the specified URL.
* @param url The URL pointing to the password store entry. * @param url The URL pointing to the password store entry.

View File

@ -6,33 +6,15 @@ import Lomiri.Components.Themes 1.3
import Pass 1.0 import Pass 1.0
import QtQuick 2.4 import QtQuick 2.4
Item { Component {
//property string folder
id: fileDir
property string fName
property bool fIsDir
property bool commonBorder: true
property int lBorderwidth: 0
property int rBorderwidth: 0
property int tBorderwidth: 0
property int bBorderwidth: 0
property int commonBorderWidth: 0
property string borderColor: LomiriColors.warmGrey
signal clicked()
anchors.right: parent.right
anchors.left: parent.left
height: units.gu(5)
Rectangle { Rectangle {
anchors.fill: parent anchors.right: parent.right
anchors.left: parent.left
height: units.gu(5)
color: theme.palette.normal.background color: theme.palette.normal.background
Text { Text {
text: fileDir.fIsDir ? fileDir.fName : fileDir.fName.slice(0, -4) // remove .gpg if it's a file text: fileIsDir ? fileName : fileName.slice(0, -4) // remove .gpg if it's a file
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: units.gu(2) anchors.leftMargin: units.gu(2)
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -44,36 +26,32 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: units.gu(2) anchors.rightMargin: units.gu(2)
height: units.gu(4) height: units.gu(4)
name: fileDir.fIsDir ? "go-next" : "lock" name: fileIsDir ? "go-next" : "lock"
color: LomiriColors.orange color: LomiriColors.orange
} }
MouseArea { MouseArea {
// onClicked: {
// var path = fileDir.fdfolder + "/" + fileName;
// if (fileIsDir) {
// fileDir.fdfolder = path;
// //backAction.visible = true;
// // passwordListHeader.title = fileName;
// } else {
// console.debug("pass show %1".arg(path));
// Pass.show(path);
// }
// }
anchors.fill: parent anchors.fill: parent
onClicked: fileDir.clicked() onClicked: {
var path = folderModel.folder + "/" + fileName;
if (fileIsDir) {
folderModel.folder = path;
backAction.visible = true;
passwordListHeader.title = fileName;
} else {
console.debug("pass show %1".arg(path));
Pass.show(path);
}
}
} }
CustomBorder { CustomBorder {
id: cb commonBorder: false
lBorderwidth: 0
commonBorder: fileDir.commonBorder rBorderwidth: 0
lBorderwidth: fileDir.lBorderwidth tBorderwidth: 0
rBorderwidth: fileDir.rBorderwidth bBorderwidth: 1
tBorderwidth: fileDir.tBorderwidth borderColor: LomiriColors.warmGrey
bBorderwidth: fileDir.bBorderwidth
borderColor: fileDir.borderColor
} }
} }

View File

@ -10,36 +10,11 @@ import "headers"
Page { Page {
id: passwordListPage id: passwordListPage
property string __passwordStorePath property string passwordStorePath
property var __passwords
function __searchPasswords(filter) {
var ret = [];
if (__passwords) {
for (var i = 0; i < __passwords.length; i++) {
if (__passwords[i].toUpperCase().indexOf(filter.toUpperCase()) > -1)
ret.push(__passwords[i]);
}
}
return ret;
}
function __searchUpdateModel(text) {
var ret = __searchPasswords(text);
passwordListSearch.model.clear();
for (var i = 0; i < ret.length; i++) {
if (ret[i])
passwordListSearch.model.append({
"fileName": ret[i]
});
}
}
anchors.fill: parent anchors.fill: parent
Component.onCompleted: { Component.onCompleted: {
__passwordStorePath = "file:" + Pass.password_store; passwordStorePath = "file:" + Pass.password_store;
Pass.onShowSucceed.connect(function(filename, text) { Pass.onShowSucceed.connect(function(filename, text) {
pageStack.push(Qt.resolvedUrl("../pages/Password.qml"), { pageStack.push(Qt.resolvedUrl("../pages/Password.qml"), {
"plainText": text, "plainText": text,
@ -49,22 +24,16 @@ Page {
Pass.onShowFailed.connect(function(message) { Pass.onShowFailed.connect(function(message) {
PopupUtils.open(passwordPageDecryptError); PopupUtils.open(passwordPageDecryptError);
}); });
Pass.onLsSucceed.connect(function(passwords) {
__passwords = passwords;
});
Pass.ls();
} }
Column { Column {
id: passwordListEmpty
anchors.top: passwordListHeader.bottom anchors.top: passwordListHeader.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: units.gu(2) anchors.leftMargin: units.gu(2)
anchors.rightMargin: units.gu(2) anchors.rightMargin: units.gu(2)
visible: passwordListNav.model.count === 0 visible: folderModel.count == 0
Rectangle { Rectangle {
width: parent.width width: parent.width
@ -102,66 +71,24 @@ Page {
} }
ListView { ListView {
id: passwordListNav
anchors.top: passwordListHeader.bottom anchors.top: passwordListHeader.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
spacing: 1 spacing: 1
visible: passwordListNav.model.count !== 0 && passwordListHeader.searchBarIsActive visible: folderModel.count != 0
model: FolderListModel { model: FolderListModel {
id: folderModel
nameFilters: ["*.gpg"] nameFilters: ["*.gpg"]
rootFolder: __passwordStorePath rootFolder: passwordStorePath
folder: __passwordStorePath folder: passwordStorePath
showDirs: true showDirs: true
} }
delegate: Component { delegate: FileDir {
FileDir { id: fileDelegate
fName: fileName
fIsDir: fileIsDir
onClicked: {
var path = passwordListNav.model.folder + "/" + fileName;
if (fileIsDir) {
passwordListNav.model.folder = path;
backAction.visible = true;
passwordListHeader.title = fileName;
} else {
console.debug("pass show %1".arg(path));
Pass.show(path);
}
}
}
}
}
ListView {
id: passwordListSearch
anchors.top: passwordListHeader.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
visible: passwordListNav.model.count !== 0 && !passwordListHeader.searchBarIsActive
model: ListModel {
}
delegate: Component {
FileDir {
fName: fileName
fIsDir: false
onClicked: {
var path = __passwordStorePath + "/" + fileName;
console.debug("pass show %1".arg(path));
Pass.show(path);
}
}
} }
} }
@ -178,8 +105,6 @@ Page {
header: MainHeader { header: MainHeader {
id: passwordListHeader id: passwordListHeader
onSearchBarActived: __searchUpdateModel("")
onSearchBarTextChanged: __searchUpdateModel(text)
leadingActionBar.height: units.gu(4) leadingActionBar.height: units.gu(4)
leadingActionBar.actions: [ leadingActionBar.actions: [
Action { Action {
@ -189,9 +114,9 @@ Page {
text: i18n.tr("Back") text: i18n.tr("Back")
visible: false visible: false
onTriggered: { onTriggered: {
passwordListNav.model.folder = passwordListNav.model.parentFolder; folderModel.folder = folderModel.parentFolder;
console.debug(passwordListNav.model.folder); console.debug(folderModel.folder);
if (passwordListNav.model.rootFolder === passwordListNav.model.folder) { if (folderModel.rootFolder === folderModel.folder) {
backAction.visible = false; backAction.visible = false;
passwordListHeader.title = i18n.tr("UTPass"); passwordListHeader.title = i18n.tr("UTPass");
} else { } else {

View File

@ -4,29 +4,23 @@ import QtQuick 2.4
PageHeader { PageHeader {
id: mainHeader id: mainHeader
readonly property bool searchBarIsActive: !searchBar.visible
signal searchBarActived()
signal searchBarTextChanged(string text)
width: parent.width width: parent.width
height: units.gu(6) height: units.gu(6)
title: i18n.tr("UTPass") title: i18n.tr("UTPass")
trailingActionBar.height: units.gu(4) trailingActionBar.height: units.gu(4)
trailingActionBar.numberOfSlots: 2 trailingActionBar.numberOfSlots: 2
trailingActionBar.actions: [ trailingActionBar.actions: [
Action { /*Action { TODO
iconName: searchBarIsActive ? "search" : "close" iconName: "search"
text: i18n.tr("Search") text: i18n.tr("Search")
onTriggered: { onTriggered: {
searchBar.visible = !searchBar.visible; searchBar.visible = !searchBar.visible
labelTitle.visible = !searchBar.visible; labelTitle.visible = !searchBar.visible
if (searchBar.visible === true) { if (searchBar.visible === true) {
searchBar.focus = true; searchBar.focus = true
searchBarActived();
} }
} }
}, },*/
Action { Action {
iconName: "settings" iconName: "settings"
text: i18n.tr("Settings") text: i18n.tr("Settings")
@ -64,7 +58,8 @@ PageHeader {
height: units.gu(4) height: units.gu(4)
visible: false visible: false
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
onContentWidthChanged: searchBarTextChanged(searchBar.text) onFocusChanged: {
}
} }
} }

View File

@ -16,5 +16,5 @@ int main(int argc, char *argv[])
QGuiApplication::setApplicationName("utpass.qrouland"); QGuiApplication::setApplicationName("utpass.qrouland");
return quick_test_main(argc, argv, "@TESTS_PATH@", "@TESTS_PATH@"); return quick_test_main(argc, argv, @TESTS_PATH@, @TESTS_PATH@);
} }

View File

@ -5,6 +5,7 @@ set(
SRC SRC
plugin.cpp plugin.cpp
utils.cpp utils.cpp
passphraseprovider.h
) )
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
@ -22,6 +23,30 @@ endif()
add_library(${PLUGIN} MODULE ${SRC}) add_library(${PLUGIN} MODULE ${SRC})
set_target_properties(${PLUGIN} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGIN}) set_target_properties(${PLUGIN} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGIN})
qt5_use_modules(${PLUGIN} Qml Quick DBus) qt5_use_modules(${PLUGIN} Qml Quick DBus)
set(RNP_BUILD_DIR "${CMAKE_SOURCE_DIR}/build/${ARCH_TRIPLET}/rnp/install")
INCLUDE_DIRECTORIES(${RNP_BUILD_DIR}/include)
add_library(rnp STATIC IMPORTED)
set_property(TARGET rnp PROPERTY IMPORTED_LOCATION "${RNP_BUILD_DIR}/lib/librnp.a")
add_library(gpgerror SHARED IMPORTED)
set_property(TARGET gpgerror PROPERTY IMPORTED_LOCATION "/usr/lib/${ARCH_TRIPLET}/libgpg-error.so.0.28.0")
add_library(libassuan SHARED IMPORTED)
set_property(TARGET libassuan PROPERTY IMPORTED_LOCATION "/usr/lib/${ARCH_TRIPLET}/libassuan.so")
add_library(libgpgme SHARED IMPORTED)
set_property(TARGET libgpgme PROPERTY IMPORTED_LOCATION "/usr/lib/${ARCH_TRIPLET}/libgpgme.so")
add_library(libgpgmepp SHARED IMPORTED)
set_property(TARGET libgpgmepp PROPERTY IMPORTED_LOCATION "/usr/lib/${ARCH_TRIPLET}/libgpgmepp.so")
add_library(libqgpgme SHARED IMPORTED)
set_property(TARGET libqgpgme PROPERTY IMPORTED_LOCATION "/usr/lib/${ARCH_TRIPLET}/libqgpgme.so")
target_link_libraries(${PLUGIN} rnp gpgerror libassuan libgpgme libgpgmepp libqgpgme)
set(QT_IMPORTS_DIR "/lib/${ARCH_TRIPLET}") set(QT_IMPORTS_DIR "/lib/${ARCH_TRIPLET}")

View File

@ -3,9 +3,9 @@
#include <QObject> #include <QObject>
#include <gpg-error.h> #include <gpg-error.h>
// extern "C" { extern "C" {
// #include <rnp/rnp.h> #include <rnp/rnp.h>
// } }
class TesTPassphraseProvider : public QObject class TesTPassphraseProvider : public QObject
{ {

View File

@ -6,11 +6,11 @@
#include <memory> #include <memory>
#include <quazip5/JlCompress.h> #include <quazip5/JlCompress.h>
//#include "passphraseprovider.h" #include "passphraseprovider.h"
#include "utils.h" #include "utils.h"
TestsUtils::TestsUtils() TestsUtils::TestsUtils():
//m_passphrase_povider(std::unique_ptr<TesTPassphraseProvider>(new TesTPassphraseProvider())) m_passphrase_povider(std::unique_ptr<TesTPassphraseProvider>(new TesTPassphraseProvider()))
{} {}
@ -68,9 +68,9 @@ void TestsUtils::copyFolder(QUrl sourceFolderUrl, QUrl destFolderUrl)
} }
} }
// QObject *TestsUtils::getTestPassphraseProvider() QObject *TestsUtils::getTestPassphraseProvider()
// { {
// return &TesTPassphraseProvider::instance(); return &TesTPassphraseProvider::instance();
// } }

View File

@ -1,7 +1,7 @@
#ifndef TESTSUTILS_H #ifndef TESTSUTILS_H
#define TESTSUTILS_H #define TESTSUTILS_H
//#include "passphraseprovider.h" #include "passphraseprovider.h"
#include <QObject> #include <QObject>
#include <QUrl> #include <QUrl>
#include <QQuickWindow> #include <QQuickWindow>
@ -18,7 +18,7 @@ public:
Q_INVOKABLE QString getTempPath(); Q_INVOKABLE QString getTempPath();
Q_INVOKABLE bool fileExists(QUrl path); Q_INVOKABLE bool fileExists(QUrl path);
Q_INVOKABLE void copyFolder(QUrl sourceFolder, QUrl destFolder); Q_INVOKABLE void copyFolder(QUrl sourceFolder, QUrl destFolder);
//Q_INVOKABLE QObject *getTestPassphraseProvider(); Q_INVOKABLE QObject *getTestPassphraseProvider();
}; };

View File

@ -4,8 +4,6 @@ import QtTest 1.2
import TestsUtils 1.0 import TestsUtils 1.0
TestCase { TestCase {
//Pass.passphrase_provider = TestsUtils.getTestPassphraseProvider();
property string password_store property string password_store
property string gpg_home property string gpg_home
@ -15,6 +13,7 @@ TestCase {
Pass.gpg_home = gpg_home; Pass.gpg_home = gpg_home;
password_store = TestsUtils.getTempPath(); password_store = TestsUtils.getTempPath();
Pass.password_store = password_store; Pass.password_store = password_store;
Pass.passphrase_provider = TestsUtils.getTestPassphraseProvider();
} }
} }

View File

@ -1,44 +0,0 @@
import Pass 1.0
import QtQuick 2.9
import QtTest 1.2
import TestsUtils 1.0
PassTestCase {
//TODO some additionanl error test
function init_data() {
return [{
"spy": lsSucceed,
"add_home_gpg_data": true,
"passwords": ["test.gpg"]
}, {
"spy": lsSucceed,
"add_home_gpg_data": false,
"passwords": []
}];
}
function test_ls(data) {
if (data.add_home_gpg_data === true)
TestsUtils.copyFolder(Qt.resolvedUrl("../../assets/password-store"), Qt.resolvedUrl(password_store));
var passwords;
Pass.lsSucceed.connect(function(ret) {
passwords = ret;
});
Pass.ls();
data.spy.wait();
verify(passwords.length === data.passwords.length, "Should return %1 password(s) but return %2 password(s)".arg(data.nb_password).arg(passwords.length));
for (var i = 0; data.passwords.length; i++) {
verify(passwords[i] === data.passwords[i], "%1 name should be %2 but is %3".arg(i).arg(data.passwords[i]).arg(passwords[i]));
}
}
SignalSpy {
id: lsSucceed
target: Pass
signalName: "lsSucceed"
}
}

View File

@ -4,24 +4,24 @@ import QtTest 1.2
import TestsUtils 1.0 import TestsUtils 1.0
PassTestCase { PassTestCase {
// TODO This test need to fixed by providing custom stub password provider to the pass plugin for succeed tests
//TODO some additionanl error test //TODO some additionanl error test
function init_data() { function init_data() {
return [{ return [{
"spy": showFailed, "spy": showFailed,
"err_msg": "Bad password", "err_msg": "Bad password",
"add_password_store_data": true, "add_home_gpg_data": true,
"file": "../../assets/gpg/clear_text.txt.gpg" "file": "../../assets/gpg/clear_text.txt.gpg"
}, { }, {
"spy": showFailed, "spy": showFailed,
"err_msg": "No suitable key", "err_msg": "No suitable key",
"add_password_store_data": false, "add_home_gpg_data": false,
"file": "../../assets/gpg/clear_text.txt.gpg" "file": "../../assets/gpg/clear_text.txt.gpg"
}]; }];
} }
function test_pass_show(data) { function test_pass_show(data) {
if (data.add_password_store_data === true) if (data.add_home_gpg_data === true)
TestsUtils.copyFolder(Qt.resolvedUrl("../../assets/gpghome"), Qt.resolvedUrl(gpg_home)); TestsUtils.copyFolder(Qt.resolvedUrl("../../assets/gpghome"), Qt.resolvedUrl(gpg_home));
var fname, ctext; var fname, ctext;