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

KDE's Doxygen guidelines are available online.