diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | mainwindow.cpp | 130 | ||||
-rw-r--r-- | mainwindow.h | 7 | ||||
-rw-r--r-- | mainwindow.ui | 7 | ||||
-rw-r--r-- | qtpass.pro | 9 | ||||
-rw-r--r-- | usersdialog.cpp | 41 | ||||
-rw-r--r-- | usersdialog.h | 38 | ||||
-rw-r--r-- | usersdialog.ui | 64 |
8 files changed, 265 insertions, 33 deletions
@@ -15,7 +15,7 @@ TODO 2. ~~multi-lingual~~ 3. ~~filtering and autocomplete~~ 4. ~~edit, insert~~ -5. gpg-id management (per-folder) +5. ~~gpg-id management (per-folder)~~ Instalation ----------- diff --git a/mainwindow.cpp b/mainwindow.cpp index 44003833..03899c66 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,6 +1,7 @@ #include "mainwindow.h" #include "ui_mainwindow.h" #include "dialog.h" +#include "usersdialog.h" #include "util.h" #include <QClipboard> #include <QInputDialog> @@ -271,7 +272,9 @@ void MainWindow::executeWrapper(QString app, QString args, QString input) { * @brief MainWindow::readyRead */ void MainWindow::readyRead(bool finished = false) { - QString output = process->readAllStandardOutput(); + QString output; + if (currentAction != GPG_INTERNAL) { + output = process->readAllStandardOutput(); if (finished && currentAction == GPG) { lastDecrypt = output; if (useClipboard) { @@ -294,6 +297,7 @@ void MainWindow::readyRead(bool finished = false) { } output.replace(QRegExp("<"), "<"); output.replace(QRegExp(">"), ">"); + } QString error = process->readAllStandardError(); if (error.size() > 0) { @@ -347,6 +351,7 @@ void MainWindow::enableUiElements(bool state) { ui->treeView->setEnabled(state); ui->lineEdit->setEnabled(state); ui->addButton->setEnabled(state); + ui->usersButton->setEnabled(state); state &= ui->treeView->currentIndex().isValid(); ui->deleteButton->setEnabled(state); ui->editButton->setEnabled(state); @@ -480,6 +485,35 @@ void MainWindow::on_clearButton_clicked() ui->lineEdit->clear(); } +QString MainWindow::getRecipientString(QString for_file, QString separator) +{ + QDir gpgIdPath(QFileInfo(for_file.startsWith(passStore) ? for_file : passStore + for_file).absoluteDir()); + bool found = false; + while (gpgIdPath.exists() && gpgIdPath.absolutePath().startsWith(passStore)) + { + if (QFile(gpgIdPath.absoluteFilePath(".gpg-id")).exists()) { + found = true; + break; + } + if (!gpgIdPath.cdUp()) { + break; + } + } + QFile gpgId(found ? gpgIdPath.absoluteFilePath(".gpg-id") : passStore + ".gpg-id"); + if (!gpgId.open(QIODevice::ReadOnly | QIODevice::Text)) { + return QString(); + } + QString recipients; + while (!gpgId.atEnd()) { + QString recipient(gpgId.readLine()); + recipient = recipient.trimmed(); + if (!recipient.isEmpty()) { + recipients += separator + '"' + recipient + '"'; + } + } + return recipients; +} + void MainWindow::setPassword(QString file, bool overwrite) { bool ok; @@ -500,35 +534,10 @@ void MainWindow::setPassword(QString file, bool overwrite) QString force(overwrite ? " -f " : " "); executePass("insert" + force + "-m \"" + file + '"', newValue); } else { - QDir gpgIdPath(QFileInfo(file.startsWith(passStore) ? file : passStore + file).absoluteDir()); - bool found = false; - while (gpgIdPath.exists() && gpgIdPath.absolutePath().startsWith(passStore)) - { - if (QFile(gpgIdPath.absoluteFilePath(".gpg-id")).exists()) { - found = true; - break; - } - if (!gpgIdPath.cdUp()) { - break; - } - } - QFile gpgId(found ? gpgIdPath.absoluteFilePath(".gpg-id") : passStore + ".gpg-id"); - if (!gpgId.open(QIODevice::ReadOnly | QIODevice::Text)) { - QMessageBox::critical(this, tr("Can not edit"), - tr("Password store lacks .gpg-id specifying encryption key")); - return; - } - QString recipients; - while (!gpgId.atEnd()) { - QString recipient(gpgId.readLine()); - recipient = recipient.trimmed(); - if (!recipient.isEmpty()) { - recipients += " -r \"" + recipient + '"'; - } - } + QString recipients = getRecipientString(file, " -r "); if (recipients.isEmpty()) { QMessageBox::critical(this, tr("Can not edit"), - tr("Could not read encryption key to use")); + tr("Could not read encryption key to use, .gpg-id file missing or invalid.")); return; } QString force(overwrite ? " --yes " : " "); @@ -538,6 +547,8 @@ void MainWindow::setPassword(QString file, bool overwrite) void MainWindow::on_addButton_clicked() { + + bool ok; QString file = QInputDialog::getText(this, tr("New file"), tr("New password file:"), QLineEdit::Normal, @@ -582,6 +593,69 @@ void MainWindow::on_editButton_clicked() setPassword(file, true); } +QList<UserInfo> MainWindow::listKeys(QString keystring) +{ + QList<UserInfo> users; + currentAction = GPG_INTERNAL; + executeWrapper(gpgExecutable , "--no-tty --with-colons --list-keys " + keystring); + process->waitForFinished(2000); + if (process->exitStatus() != QProcess::NormalExit) { + return users; + } + QStringList keys = QString(process->readAllStandardOutput()).split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + foreach (QString key, keys) { + QStringList props = key.split(':'); + if (props.size() < 10 || props[0] != "pub") { + continue; + } + UserInfo i; + i.name = props[9]; + i.key_id = props[4]; + users.append(i); + } + return users; +} + +void MainWindow::on_usersButton_clicked() +{ + QList<UserInfo> users = listKeys(); + if (users.size() == 0) { + QMessageBox::critical(this, tr("Can not get key list"), + tr("Unable to get list of available gpg keys")); + return; + } + QList<UserInfo> selected_users; + QString dir = getDir(ui->treeView->currentIndex(), false); + QString recipients = getRecipientString(dir.isEmpty() ? "" : dir); + if (!recipients.isEmpty()) { + selected_users = listKeys(recipients); + } + foreach (const UserInfo &sel, selected_users) { + for (QList<UserInfo>::iterator it = users.begin(); it != users.end(); ++it) { + if (sel.key_id == it->key_id) it->enabled = true; + } + } + UsersDialog d(this); + d.setUsers(&users); + if (!d.exec()) { + d.setUsers(NULL); + return; + } + d.setUsers(NULL); + QFile gpgId(dir + ".gpg-id"); + if (!gpgId.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(this, tr("Cannot update"), + tr("Failed to open .gpg-id for writing.")); + return; + } + foreach (const UserInfo &user, users) { + if (user.enabled) { + gpgId.write((user.key_id + "\n").toUtf8()); + } + } + gpgId.close(); +} + /** * @brief MainWindow::setApp * @param app diff --git a/mainwindow.h b/mainwindow.h index 66c05a8b..ccbd55a2 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -13,11 +13,13 @@ namespace Ui { class MainWindow; } +struct UserInfo; + class MainWindow : public QMainWindow { Q_OBJECT -enum actionType { GPG, GIT, EDIT, DELETE }; +enum actionType { GPG, GIT, EDIT, DELETE, GPG_INTERNAL }; public: explicit MainWindow(QWidget *parent = 0); @@ -43,6 +45,7 @@ private slots: void on_addButton_clicked(); void on_deleteButton_clicked(); void on_editButton_clicked(); + void on_usersButton_clicked(); void messageAvailable(QString message); private: @@ -78,6 +81,8 @@ private: void setPassword(QString, bool); void normalizePassStore(); QSettings &getSettings(); + QList<UserInfo> listKeys(QString keystring = ""); + QString getRecipientString(QString for_file, QString separator = " "); }; #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index fee1d276..96d23633 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -77,6 +77,13 @@ </property> </widget> </item> + <item> + <widget class="QToolButton" name="usersButton"> + <property name="text"> + <string>U</string> + </property> + </widget> + </item> </layout> </item> <item row="2" column="1"> @@ -21,16 +21,19 @@ SOURCES += main.cpp\ dialog.cpp \ storemodel.cpp \ singleapplication.cpp \ - util.cpp + util.cpp \ + usersdialog.cpp HEADERS += mainwindow.h \ dialog.h \ storemodel.h \ singleapplication.h \ - util.h + util.h \ + usersdialog.h FORMS += mainwindow.ui \ - dialog.ui + dialog.ui \ + usersdialog.ui TRANSLATIONS += localization/localization_nl_NL.ts \ localization/localization_de_DE.ts \ diff --git a/usersdialog.cpp b/usersdialog.cpp new file mode 100644 index 00000000..767c7486 --- /dev/null +++ b/usersdialog.cpp @@ -0,0 +1,41 @@ +#include "usersdialog.h" +#include "ui_usersdialog.h" + +UsersDialog::UsersDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::UsersDialog) +{ + ui->setupUi(this); + connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(ui->listWidget, SIGNAL(itemChanged(QListWidgetItem *)), this, SLOT(itemChange(QListWidgetItem *))); +} + +UsersDialog::~UsersDialog() +{ + delete ui; +} + +Q_DECLARE_METATYPE(UserInfo *) + +void UsersDialog::itemChange(QListWidgetItem *item) +{ + if (!item) return; + UserInfo *info = item->data(Qt::UserRole).value<UserInfo *>(); + if (!info) return; + info->enabled = item->checkState() == Qt::Checked; +} + +void UsersDialog::setUsers(QList<UserInfo> *users) +{ + ui->listWidget->clear(); + if (users) { + for (QList<UserInfo>::iterator it = users->begin(); it != users->end(); ++it) { + UserInfo &user(*it); + QListWidgetItem *item = new QListWidgetItem(user.name + "\n" + user.key_id, ui->listWidget); + item->setCheckState(user.enabled ? Qt::Checked : Qt::Unchecked); + item->setData(Qt::UserRole, QVariant::fromValue(&user)); + ui->listWidget->addItem(item); + } + } +} diff --git a/usersdialog.h b/usersdialog.h new file mode 100644 index 00000000..54fadfa8 --- /dev/null +++ b/usersdialog.h @@ -0,0 +1,38 @@ +#ifndef USERSDIALOG_H +#define USERSDIALOG_H + +//#include <QAbstractListModel> +#include <QDialog> +#include <QList> +#include <QStandardItemModel> + +namespace Ui { +class UsersDialog; +} + +class QListWidgetItem; + +struct UserInfo { + UserInfo() : enabled(false) {} + QString name; + QString key_id; + bool enabled; +}; + +class UsersDialog : public QDialog +{ + Q_OBJECT + +public: + explicit UsersDialog(QWidget *parent = 0); + ~UsersDialog(); + void setUsers(QList<UserInfo> *); + +private slots: + void itemChange(QListWidgetItem *); + +private: + Ui::UsersDialog *ui; +}; + +#endif // USERSDIALOG_H diff --git a/usersdialog.ui b/usersdialog.ui new file mode 100644 index 00000000..232b9426 --- /dev/null +++ b/usersdialog.ui @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>UsersDialog</class> + <widget class="QDialog" name="UsersDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>599</width> + <height>583</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Read access users</string> + </property> + <property name="modal"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Select which users should be able to decrypt passwords stored in this folder. +Note: Existing files will not be modified and retain the old permissions until you edit them.</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QListWidget" name="listWidget"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="sortingEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> |