Pimcommon

aclmanager.cpp
1/*
2 * SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
3 * SPDX-FileCopyrightText: 2010 Tobias Koenig <tokoe@kdab.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include "aclmanager.h"
9#include "aclentrydialog_p.h"
10#include "aclmodifyjob.h"
11#include "aclutils_p.h"
12#include "imapaclattribute.h"
13#include "imapresourcesettings.h"
14#include "pimcommonakonadi_debug.h"
15#include "util/pimutil.h"
16
17#include <Akonadi/CollectionFetchJob>
18#include <Akonadi/ServerManager>
19
20#include <KEmailAddress>
21#include <KLocalizedString>
22#include <KMessageBox>
23
24#include <QAbstractListModel>
25#include <QAction>
26#include <QDBusInterface>
27#include <QDBusReply>
28#include <QItemSelectionModel>
29
30using namespace PimCommon;
31
32class AclModel : public QAbstractListModel
33{
34public:
35 enum Role {
36 UserIdRole = Qt::UserRole + 1,
37 PermissionsRole,
38 PermissionsTextRole,
39 };
40
41 AclModel(QObject *parent = nullptr)
43 {
44 }
45
46 [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
47 {
48 if (index.row() < 0 || index.row() >= mRights.count()) {
49 return {};
50 }
51
52 const QPair<QByteArray, KIMAP::Acl::Rights> right = mRights.at(index.row());
53 switch (role) {
54 case Qt::DisplayRole:
55 return QStringLiteral("%1: %2").arg(QString::fromLatin1(right.first), AclUtils::permissionsToUserString(right.second));
56 case UserIdRole:
57 return QString::fromLatin1(right.first);
58 case PermissionsRole:
59 return {static_cast<int>(right.second)};
60 case PermissionsTextRole:
61 return AclUtils::permissionsToUserString(right.second);
62 default:
63 return {};
64 }
65 }
66
67 bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
68 {
69 if (index.row() < 0 || index.row() >= mRights.count()) {
70 return false;
71 }
72
73 QPair<QByteArray, KIMAP::Acl::Rights> &right = mRights[index.row()];
74 switch (role) {
75 case UserIdRole:
76 right.first = value.toByteArray();
78 return true;
79 case PermissionsRole:
80 right.second = static_cast<KIMAP::Acl::Rights>(value.toInt());
82 return true;
83 default:
84 return false;
85 }
86
87 return false;
88 }
89
90 [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override
91 {
92 if (parent.isValid()) {
93 return 0;
94 } else {
95 return mRights.count();
96 }
97 }
98
99 void setRights(const QMap<QByteArray, KIMAP::Acl::Rights> &rights)
100 {
102
103 mRights.clear();
104
107 for (; it != itEnd; ++it) {
108 mRights.append(qMakePair(it.key(), it.value()));
109 }
110
112 }
113
114 [[nodiscard]] QMap<QByteArray, KIMAP::Acl::Rights> rights() const
115 {
117
118 using RightPair = QPair<QByteArray, KIMAP::Acl::Rights>;
119 for (const RightPair &right : std::as_const(mRights)) {
120 result.insert(right.first, right.second);
121 }
122
123 return result;
124 }
125
126protected:
127 bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
128 {
129 beginInsertRows(parent, row, row + count - 1);
130 for (int i = 0; i < count; ++i) {
131 mRights.insert(row, qMakePair(QByteArray(), KIMAP::Acl::Rights()));
132 }
134
135 return true;
136 }
137
138 bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
139 {
140 beginRemoveRows(parent, row, row + count - 1);
141 for (int i = 0; i < count; ++i) {
142 mRights.remove(row, count);
143 }
145
146 return true;
147 }
148
149private:
151};
152
153class Q_DECL_HIDDEN PimCommon::AclManager::AclManagerPrivate
154{
155public:
156 AclManagerPrivate(AclManager *qq)
157 : q(qq)
158 {
159 mAddAction = new QAction(i18n("Add Entry..."), q);
160 q->connect(mAddAction, &QAction::triggered, q, [this]() {
161 addAcl();
162 });
163
164 mEditAction = new QAction(i18n("Edit Entry..."), q);
165 mEditAction->setEnabled(false);
166 q->connect(mEditAction, &QAction::triggered, q, [this]() {
167 editAcl();
168 });
169
170 mDeleteAction = new QAction(i18n("Remove Entry"), q);
171 mDeleteAction->setEnabled(false);
172 q->connect(mDeleteAction, &QAction::triggered, q, [this]() {
173 deleteAcl();
174 });
175
176 mModel = new AclModel(q);
177
178 mSelectionModel = new QItemSelectionModel(mModel);
179 q->connect(mSelectionModel, &QItemSelectionModel::selectionChanged, q, [this]() {
180 selectionChanged();
181 });
182 }
183
184 ~AclManagerPrivate() = default;
185
186 void selectionChanged()
187 {
188 const bool itemSelected = !mSelectionModel->selectedIndexes().isEmpty();
189
190 bool canAdmin = (mUserRights & KIMAP::Acl::Admin);
191
192 bool canAdminThisItem = canAdmin;
193 if (canAdmin && itemSelected) {
194 const QModelIndex index = mSelectionModel->selectedIndexes().first();
195 const QString userId = index.data(AclModel::UserIdRole).toString();
196 const KIMAP::Acl::Rights rights = static_cast<KIMAP::Acl::Rights>(index.data(AclModel::PermissionsRole).toInt());
197
198 // Don't allow users to remove their own admin permissions - there's no way back
199 if (mImapUserName == userId && (rights & KIMAP::Acl::Admin)) {
200 canAdminThisItem = false;
201 }
202 }
203
204 mAddAction->setEnabled(canAdmin);
205 mEditAction->setEnabled(itemSelected && canAdminThisItem);
206 mDeleteAction->setEnabled(itemSelected && canAdminThisItem);
207 }
208
209 void addAcl()
210 {
211 AclEntryDialog dlg;
212 dlg.setWindowTitle(i18nc("@title:window", "Add ACL"));
213
214 if (!dlg.exec()) {
215 return;
216 }
217 const QString userId = dlg.userId();
218 const QStringList lstAddresses = KEmailAddress::splitAddressList(userId);
219 for (const QString &addr : lstAddresses) {
220 if (mModel->insertRow(mModel->rowCount())) {
221 const QModelIndex index = mModel->index(mModel->rowCount() - 1, 0);
222 const QString extractedAddress = KEmailAddress::extractEmailAddress(addr);
223 mModel->setData(index, extractedAddress, AclModel::UserIdRole);
224 mModel->setData(index, static_cast<int>(dlg.permissions()), AclModel::PermissionsRole);
225
226 mChanged = true;
227 }
228 }
229 }
230
231 void editAcl()
232 {
233 if (mEditAction->isEnabled()) {
234 const QModelIndex index = mSelectionModel->selectedIndexes().first();
235 const QString userId = index.data(AclModel::UserIdRole).toString();
236 const KIMAP::Acl::Rights permissions = static_cast<KIMAP::Acl::Rights>(index.data(AclModel::PermissionsRole).toInt());
237
238 AclEntryDialog dlg;
239 dlg.setWindowTitle(i18nc("@title:window", "Edit ACL"));
240 dlg.setUserId(userId);
241 dlg.setPermissions(permissions);
242
243 if (!dlg.exec()) {
244 return;
245 }
246 const QStringList lstAddresses = KEmailAddress::splitAddressList(dlg.userId());
247 if (lstAddresses.count() == 1) {
248 mModel->setData(index, KEmailAddress::extractEmailAddress(lstAddresses.at(0)), AclModel::UserIdRole);
249 mModel->setData(index, static_cast<int>(dlg.permissions()), AclModel::PermissionsRole);
250 mChanged = true;
251 } else {
252 bool firstElement = true;
253 for (const QString &addr : lstAddresses) {
254 if (firstElement) {
255 mModel->setData(index, KEmailAddress::extractEmailAddress(lstAddresses.at(0)), AclModel::UserIdRole);
256 mModel->setData(index, static_cast<int>(dlg.permissions()), AclModel::PermissionsRole);
257 firstElement = false;
258 } else {
259 if (mModel->insertRow(mModel->rowCount())) {
260 const QModelIndex rowindex = mModel->index(mModel->rowCount() - 1, 0);
261 mModel->setData(rowindex, KEmailAddress::extractEmailAddress(addr), AclModel::UserIdRole);
262 mModel->setData(rowindex, static_cast<int>(dlg.permissions()), AclModel::PermissionsRole);
263 }
264 }
265 }
266 mChanged = true;
267 }
268 }
269 }
270
271 void deleteAcl()
272 {
273 const QModelIndex index = mSelectionModel->selectedIndexes().first();
274 const QString userId = index.data(AclModel::UserIdRole).toString();
275
276 if (mImapUserName == userId) {
279 i18n("Do you really want to remove your own permissions for this folder? "
280 "You will not be able to access it afterwards."),
281 i18nc("@title:window", "Remove"))) {
282 return;
283 }
284 } else {
287 i18n("Do you really want to remove these permissions for this folder?"),
288 i18nc("@title:window", "Remove"))) {
289 return;
290 }
291 }
292
293 mModel->removeRow(index.row(), QModelIndex());
294 mChanged = true;
295 }
296
297 void setCollection(const Akonadi::Collection &collection)
298 {
299 mCollection = collection;
300 mChanged = false;
301
302 const auto attribute = collection.attribute<PimCommon::ImapAclAttribute>();
303 const QMap<QByteArray, KIMAP::Acl::Rights> rights = attribute->rights();
304
305 QString resource = collection.resource();
306 if (resource.contains(QLatin1StringView("akonadi_kolabproxy_resource"))) {
307 const QString basename = Akonadi::ServerManager::agentServiceName(Akonadi::ServerManager::Agent, QStringLiteral("akonadi_kolabproxy_resource"));
308
309 QDBusInterface interface(basename, QStringLiteral("/KolabProxy"));
310 if (interface.isValid()) {
311 QDBusReply<QString> reply = interface.call(QStringLiteral("imapResourceForCollection"), collection.remoteId().toLongLong());
312 if (reply.isValid()) {
313 resource = reply;
314 }
315 }
316 }
317 OrgKdeAkonadiImapSettingsInterface *imapSettingsInterface = PimCommon::Util::createImapSettingsInterface(resource);
318
319 QString loginName;
320 QString serverName;
321 if (imapSettingsInterface && imapSettingsInterface->isValid()) {
322 QDBusReply<QString> reply = imapSettingsInterface->userName();
323 if (reply.isValid()) {
324 loginName = reply;
325 }
326
327 reply = imapSettingsInterface->imapServer();
328 if (reply.isValid()) {
329 serverName = reply;
330 }
331 } else {
332 qCDebug(PIMCOMMONAKONADI_LOG) << " collection has not imap as resources: " << collection.resource();
333 }
334 delete imapSettingsInterface;
335
336 mImapUserName = loginName;
337 if (!rights.contains(loginName.toUtf8())) {
338 const QString guessedUserName = AclUtils::guessUserName(loginName, serverName);
339 if (rights.contains(guessedUserName.toUtf8())) {
340 mImapUserName = guessedUserName;
341 }
342 }
343
344 mUserRights = rights[mImapUserName.toUtf8()];
345
346 mModel->setRights(rights);
347 selectionChanged();
348 }
349
350 AclManager *const q;
351 AclModel *mModel = nullptr;
352 QItemSelectionModel *mSelectionModel = nullptr;
353 QAction *mAddAction = nullptr;
354 QAction *mEditAction = nullptr;
355 QAction *mDeleteAction = nullptr;
356
357 Akonadi::Collection mCollection;
358 QString mImapUserName;
359 KIMAP::Acl::Rights mUserRights;
360 bool mChanged = false;
361};
362
363AclManager::AclManager(QObject *parent)
364 : QObject(parent)
365 , d(new AclManagerPrivate(this))
366{
367}
368
369AclManager::~AclManager() = default;
370
371void AclManager::setCollection(const Akonadi::Collection &collection)
372{
373 d->setCollection(collection);
374 Q_EMIT collectionChanged(d->mCollection);
375 Q_EMIT collectionCanBeAdministrated(d->mUserRights & KIMAP::Acl::Admin);
376}
377
378Akonadi::Collection AclManager::collection() const
379{
380 return d->mCollection;
381}
382
383QAbstractItemModel *AclManager::model() const
384{
385 return d->mModel;
386}
387
388QItemSelectionModel *AclManager::selectionModel() const
389{
390 return d->mSelectionModel;
391}
392
393QAction *AclManager::addAction() const
394{
395 return d->mAddAction;
396}
397
398QAction *AclManager::editAction() const
399{
400 return d->mEditAction;
401}
402
403QAction *AclManager::deleteAction() const
404{
405 return d->mDeleteAction;
406}
407
408void AclManager::save(bool recursive)
409{
410 if (!d->mCollection.isValid() || !d->mChanged) {
411 return;
412 }
413
414 // refresh the collection, it might be outdated in the meantime
416 if (!job->exec()) {
417 qCDebug(PIMCOMMONAKONADI_LOG) << " collection Fetch error" << job->errorString();
418 return;
419 }
420
421 if (job->collections().isEmpty()) {
422 qCDebug(PIMCOMMONAKONADI_LOG) << " collection list Fetched is Empty ";
423 return;
424 }
425
426 d->mCollection = job->collections().at(0);
427
428 d->mChanged = false;
429
430 auto modifyAclJob = new PimCommon::AclModifyJob;
431 modifyAclJob->setCurrentRight(d->mModel->rights());
432 modifyAclJob->setTopLevelCollection(d->mCollection);
433 modifyAclJob->setRecursive(recursive);
434 modifyAclJob->start();
435}
436
437void AclManager::setChanged(bool b)
438{
439 d->mChanged = b;
440}
441
442#include "moc_aclmanager.cpp"
QString resource() const
const T * attribute() const
QString remoteId() const
static QString agentServiceName(ServiceAgentType agentType, const QString &identifier)
The ImapAclAttribute class.
KCODECS_EXPORT QByteArray extractEmailAddress(const QByteArray &address)
KCODECS_EXPORT QStringList splitAddressList(const QString &aStr)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
folderdialogacltab.h
void beginInsertRows(const QModelIndex &parent, int first, int last)
void beginRemoveRows(const QModelIndex &parent, int first, int last)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
void triggered(bool checked)
bool isValid() const const
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
void clear()
qsizetype count() const const
iterator insert(const_iterator before, parameter_type value)
void remove(qsizetype i, qsizetype n)
const_iterator cbegin() const const
const_iterator cend() const const
iterator insert(const Key &key, const T &value)
QVariant data(int role) const const
int row() const const
Q_EMITQ_EMIT
QObject * parent() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromLatin1(QByteArrayView str)
qlonglong toLongLong(bool *ok, int base) const const
QByteArray toUtf8() const const
UserRole
QTextStream & right(QTextStream &stream)
QByteArray toByteArray() const const
int toInt(bool *ok) const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:17:23 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.