File indexing completed on 2025-01-05 04:35:36

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0003     SPDX-FileCopyrightText: 2011 Rodrigo Belem <rclbelem@gmail.com>
0004     SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
0005     SPDX-FileCopyrightText: 2021 Slava Aseev <nullptrnine@basealt.ru>
0006 */
0007 
0008 #include <kuser.h>
0009 
0010 #include <QMetaEnum>
0011 
0012 #include <sys/stat.h>
0013 
0014 #include "model.h"
0015 #include "usermanager.h"
0016 
0017 UserPermissionModel::UserPermissionModel(const KSambaShareData &shareData, UserManager *userManager, QObject *parent)
0018     : QAbstractTableModel(parent)
0019     , m_userManager(userManager)
0020     , m_shareData(shareData)
0021     , m_usersAcl()
0022 {
0023     QMetaObject::invokeMethod(this, &UserPermissionModel::setupData);
0024 }
0025 
0026 void UserPermissionModel::setupData()
0027 {
0028     const QStringList acl = m_shareData.acl().split(QLatin1Char(','),
0029                                             Qt::SkipEmptyParts);
0030 
0031     QList<QString>::const_iterator itr;
0032     for (itr = acl.constBegin(); itr != acl.constEnd(); ++itr) {
0033         const QStringList userInfo = (*itr).trimmed().split(QLatin1Char(':'));
0034         m_usersAcl.insert(userInfo.at(0), userInfo.at(1));
0035     }
0036     if (m_usersAcl.isEmpty()) {
0037         m_usersAcl.insert(QStringLiteral("Everyone"), QStringLiteral("R"));
0038     }
0039 }
0040 
0041 
0042 
0043 
0044 int UserPermissionModel::rowCount(const QModelIndex &parent) const
0045 {
0046     Q_UNUSED(parent)
0047     return m_userManager->users().count();
0048 }
0049 
0050 int UserPermissionModel::columnCount(const QModelIndex &parent) const
0051 {
0052     Q_UNUSED(parent)
0053     return QMetaEnum::fromType<Column>().keyCount();
0054 }
0055 
0056 QVariant UserPermissionModel::data(const QModelIndex &index, int role) const
0057 {
0058     if ((role == Qt::DisplayRole) && (index.column() == ColumnUsername)) {
0059         return QVariant(m_userManager->users().at(index.row())->name());
0060     }
0061 
0062     if ((role == Qt::DisplayRole || role == Qt::EditRole) && (index.column() == ColumnAccess)) {
0063         QMap<QString, QVariant>::ConstIterator itr;
0064         for (itr = m_usersAcl.constBegin(); itr != m_usersAcl.constEnd(); ++itr) {
0065             if (itr.key().endsWith(m_userManager->users().at(index.row())->name())) {
0066                 return itr.value();
0067             }
0068         }
0069     }
0070 
0071     return QVariant();
0072 }
0073 
0074 Qt::ItemFlags UserPermissionModel::flags(const QModelIndex &index) const
0075 {
0076     if (index.column() == ColumnUsername) {
0077         return Qt::ItemIsSelectable;
0078     }
0079 
0080     if (index.column() == ColumnAccess) {
0081         return (Qt::ItemIsEnabled | Qt::ItemIsEditable);
0082     }
0083 
0084     return Qt::NoItemFlags;
0085 }
0086 
0087 bool UserPermissionModel::setData(const QModelIndex &index, const QVariant &value, int role)
0088 {
0089     if ((role != Qt::EditRole) || (index.column() != ColumnAccess)) {
0090         return false;
0091     }
0092 
0093     QString key;
0094     QMap<QString, QVariant>::ConstIterator itr;
0095     for (itr = m_usersAcl.constBegin(); itr != m_usersAcl.constEnd(); ++itr) {
0096         if (itr.key().endsWith(m_userManager->users().at(index.row())->name())) {
0097             key = itr.key();
0098             break;
0099         }
0100     }
0101 
0102     if (key.isEmpty()) {
0103         key = m_userManager->users().at(index.row())->name();
0104     }
0105 
0106     if (value.isNull()) {
0107         m_usersAcl.take(key);
0108     } else {
0109         m_usersAcl.insert(key, value);
0110     }
0111 
0112     Q_EMIT dataChanged(index, index);
0113     return true;
0114 }
0115 
0116 QString UserPermissionModel::getAcl() const
0117 {
0118     // ACE order matters. Based on testing samba behaves the following way when checking if a user may do something:
0119     //   - rights granted stack up until the first applicable denial (r+f = f)
0120     //   - denials end the lookup BUT all permissions until then are applied!
0121     // We always put Everyone at the beginning and do not support groups so effectively we behave the same way
0122     // as on Windows. Everyone is to mean **everyone** and the user rules add on top but the Everyone ACE is the
0123     // baseline permission for everyone. By extension because of the samba resolution behavior Everyone:D will disable
0124     // the share pretty much.
0125     // This has another more important element though: Everyone:R must not be before denials because samba applies
0126     // any matched reads when it encounters a denial. e.g. Everyone:R,foo:D means foo can still read because D merely
0127     // ends the lookup, it doesn't take already granted permissions away. With that in mind we need to reshuffle
0128     // the ACEs so *all* D come first followed by all R and last all F.
0129     // https://docs.microsoft.com/en-us/windows/win32/secauthz/how-dacls-control-access-to-an-object
0130     // https://docs.microsoft.com/en-us/windows/win32/secauthz/order-of-aces-in-a-dacl
0131 
0132     // NOTE: D < R < F ordering would also be fine should we ever grow group support I think
0133 
0134     QStringList denials;
0135     QStringList readables;
0136     QStringList fulls;
0137     for (auto it = m_usersAcl.constBegin(); it != m_usersAcl.constEnd(); ++it) {
0138         const QString &userName = it.key();
0139         const QString access = it->value<QString>();
0140         if (access.isEmpty()) {
0141             continue; // --- undefined (no access)
0142         }
0143         // NB: we do not append access because samba is being inconsistent with itself. `net usershare info`
0144         // uses capital letters, but `net usershare add` will (for example) not accept capital D, so if you were to
0145         // take the output of info and feed it to add it'd error out -.- ... force everything lower case here
0146         // so it definitely works with add
0147         if (access == QLatin1String("D")) {
0148             denials << userName + QStringLiteral(":d");
0149         } else if (access == QLatin1String("R")) {
0150             readables << userName + QStringLiteral(":r");
0151         } else if (access == QLatin1String("F")) {
0152             fulls << userName + QStringLiteral(":f");
0153         } else {
0154             Q_UNREACHABLE(); // unmapped value WTH
0155         }
0156     }
0157 
0158     return (denials + readables + fulls).join(QLatin1Char(','));
0159 }
0160 
0161 UserPermissionModel::SambaACEHashMap UserPermissionModel::getUsersACEs() const {
0162     SambaACEHashMap result;
0163     for (auto it = m_usersAcl.constBegin(); it != m_usersAcl.constEnd(); ++it) {
0164         result.insert(it.key(), it->value<QString>());
0165     }
0166     return result;
0167 }