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 }