Line data Source code
1 : // SPDX-FileCopyrightText: 2015 Anne Jan Brouwer
2 : // SPDX-License-Identifier: GPL-3.0-or-later
3 : /**
4 : * @file
5 : * @brief UsersDialog, the recipient picker for a folder's `.gpg-id`.
6 : */
7 : #ifndef SRC_USERSDIALOG_H_
8 : #define SRC_USERSDIALOG_H_
9 :
10 : #include "appsettings.h"
11 : #include "userinfo.h"
12 :
13 : #include <QDialog>
14 : #include <QList>
15 : #include <QRegularExpression>
16 : #include <QScopedPointer>
17 :
18 : namespace Ui {
19 : class UsersDialog;
20 : }
21 :
22 : class Pass;
23 : class QKeyEvent;
24 : class QListWidgetItem;
25 :
26 : /**
27 : * @class UsersDialog
28 : * @ingroup keys
29 : * @brief Dialog for selecting GPG recipients for password encryption.
30 : *
31 : * UsersDialog allows users to select which GPG keys to encrypt passwords to.
32 : * It displays available keys, supports filtering, and shows key expiration
33 : * status.
34 : */
35 : class UsersDialog : public QDialog {
36 187 : Q_OBJECT
37 :
38 : public:
39 : /** @brief Whether the recipients on disk are ticked when it opens. */
40 : enum class Preselect {
41 : FromList, ///< The folder's recipient list, as the backend reads it.
42 : Nothing, ///< None: for a store that had no list when it was chosen.
43 : };
44 :
45 : /**
46 : * @brief Construct users dialog.
47 : * @param pass Active Pass backend.
48 : * @param s Application settings snapshot.
49 : * @param dir Password store directory path.
50 : * @param parent Parent widget.
51 : */
52 : explicit UsersDialog(Pass *pass, const AppSettings &s, QString dir,
53 : QWidget *parent = nullptr);
54 : /**
55 : * @brief Construct users dialog, saying what to preselect.
56 : * @param pass Active Pass backend.
57 : * @param s Application settings snapshot.
58 : * @param dir Password store directory path.
59 : * @param preselect What is ticked when it opens.
60 : * @param parent Parent widget.
61 : */
62 : UsersDialog(Pass *pass, const AppSettings &s, QString dir,
63 : Preselect preselect, QWidget *parent = nullptr);
64 : /**
65 : * @brief Destroys the dialog and its generated form.
66 : */
67 : ~UsersDialog() override;
68 :
69 : /**
70 : * @brief Choose whether OK runs Pass::Init on the folder.
71 : *
72 : * On by default. Off, the dialog only collects the selection; the caller
73 : * reads selectedUsers() and initialises the folder itself (new profiles,
74 : * which must not go through the backend of the active store).
75 : * @param initOnAccept false to skip Pass::Init.
76 : */
77 3 : void setInitOnAccept(bool initOnAccept) { m_initOnAccept = initOnAccept; }
78 :
79 : /**
80 : * @brief The key list with the user's choice in each entry's enabled flag.
81 : * @return All listed users; the selected ones have enabled == true.
82 : */
83 : [[nodiscard]] auto selectedUsers() const -> QList<UserInfo> {
84 : return m_userList;
85 : }
86 :
87 : /**
88 : * @brief Whether at least one key is ticked.
89 : * @return true if Init would get a recipient.
90 : */
91 : [[nodiscard]] auto hasSelection() const -> bool;
92 :
93 : public slots:
94 : /**
95 : * @brief Handle dialog acceptance.
96 : */
97 : void accept() override;
98 :
99 : protected:
100 : /**
101 : * @brief Handle key press.
102 : * @param event Key event.
103 : */
104 : void keyPressEvent(QKeyEvent *event) override;
105 :
106 : private slots:
107 : /**
108 : * @brief Handle user selection change.
109 : * @param item Changed list item.
110 : */
111 : void itemChange(QListWidgetItem *item);
112 : /**
113 : * @brief Filter user list.
114 : * @param filter Filter text.
115 : */
116 : void on_lineEdit_textChanged(const QString &filter);
117 : /**
118 : * @brief Handle select all checkbox.
119 : */
120 : void on_checkBox_clicked();
121 : /**
122 : * @brief Import a GPG key and refresh the list.
123 : */
124 : void on_importKeyButton_clicked();
125 : /**
126 : * @brief Make the key gpg just imported the current row. gpg reports a
127 : * 16-character long ID (IMPORTED) or a 40-character fingerprint
128 : * (IMPORT_OK), so the two are compared both ways, by suffix.
129 : * @param importedKey What gpg reported.
130 : */
131 : void selectImportedKey(const QString &importedKey);
132 :
133 : private:
134 : /// The widgets from usersdialog.ui.
135 : QScopedPointer<Ui::UsersDialog> ui;
136 : Pass *m_pass; /**< Active Pass backend */
137 : QString m_passStore; /**< Password store root path */
138 : QString m_gpgExe; /**< GPG executable path */
139 : QList<UserInfo> m_userList; /**< List of available GPG users */
140 : bool m_initOnAccept{true}; /**< Whether accept() runs Pass::Init. */
141 : /// Enable OK only while at least one key is ticked (hasSelection()).
142 : void updateOkButton();
143 : QString m_dir; /**< Password store directory */
144 : Preselect m_preselect = Preselect::FromList; /**< What opens ticked */
145 : QString m_lastFilter; /**< Last filter text for caching */
146 : QString m_cachedPatternString; /**< Cached pattern string */
147 : QRegularExpression m_cachedNameFilter; /**< Cached regex filter */
148 : mutable QDateTime m_cachedCurrentDateTime; /**< Cached current date/time for
149 : expiry checks */
150 : mutable bool m_cachedDateTimeValid =
151 : false; /**< Whether cached date/time is valid */
152 :
153 : /// Attach WindowStateStore, which restores the dialog's saved geometry
154 : /// now and saves it under "usersDialog" when the dialog closes.
155 : void restoreDialogState();
156 :
157 : /**
158 : * @brief Connect dialog signals.
159 : */
160 : void connectSignals();
161 :
162 : /**
163 : * @brief Load GPG keys and determine secret key status.
164 : * @return true if successful, false if keys could not be loaded.
165 : */
166 : auto loadGpgKeys() -> bool;
167 :
168 : /**
169 : * @brief Mark which keys have secret counterparts.
170 : * @param users List of users to mark.
171 : */
172 : void markSecretKeys(QList<UserInfo> &users);
173 :
174 : /**
175 : * @brief Load recipients and handle missing keys.
176 : */
177 : void loadRecipients();
178 : /**
179 : * @brief Show @p text above the key list in bold red, for a recipient list
180 : * that could not be verified.
181 : * @param text The warning, plain text.
182 : */
183 : void showRecipientWarning(const QString &text);
184 : /**
185 : * @brief Tick the keys the folder is encrypted to, and list recipients no
186 : * key in the keyring answers to, so saving does not drop them silently.
187 : * @param recipients The folder's recipients, as listed in its `.gpg-id`.
188 : */
189 : void selectRecipients(const QStringList &recipients);
190 :
191 : /**
192 : * @brief Populate user list.
193 : * @param filter Optional filter text.
194 : */
195 : void populateList(const QString &filter = QString());
196 : /**
197 : * @brief Check if user passes filter.
198 : * @param user User to check.
199 : * @param filter Text filter.
200 : * @param nameFilter Regex filter.
201 : * @return true if user passes.
202 : */
203 : bool passesFilter(const UserInfo &user, const QString &filter,
204 : const QRegularExpression &nameFilter) const;
205 : /**
206 : * @brief Check if GPG key is expired.
207 : * @param user User to check.
208 : * @return true if key is expired.
209 : */
210 : auto isUserExpired(const UserInfo &user) const -> bool;
211 : /**
212 : * @brief Build display text for user.
213 : * @param user User info.
214 : * @return Formatted text.
215 : */
216 : QString buildUserText(const UserInfo &user) const;
217 : /**
218 : * @brief Apply visual styling to user item.
219 : * @param item List item.
220 : * @param user User info.
221 : */
222 : void applyUserStyling(QListWidgetItem *item, const UserInfo &user) const;
223 : };
224 :
225 : #endif // SRC_USERSDIALOG_H_
|