File: model.cpp

package info (click to toggle)
kdenetwork-filesharing 4%3A20.12.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 2,892 kB
  • sloc: cpp: 993; xml: 107; makefile: 3; sh: 1
file content (164 lines) | stat: -rw-r--r-- 5,931 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*
    SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
    SPDX-FileCopyrightText: 2011 Rodrigo Belem <rclbelem@gmail.com>
    SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
*/

#include <kuser.h>

#include <QFile>
#include <QRegularExpression>
#include <QMetaEnum>

#include <sys/stat.h>

#include "model.h"
#include "usermanager.h"

UserPermissionModel::UserPermissionModel(const KSambaShareData &shareData, UserManager *userManager, QObject *parent)
    : QAbstractTableModel(parent)
    , m_userManager(userManager)
    , m_shareData(shareData)
    , m_usersAcl()
{
    QMetaObject::invokeMethod(this, &UserPermissionModel::setupData);
}

void UserPermissionModel::setupData()
{
    const QStringList acl = m_shareData.acl().split(QLatin1Char(','),
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
                                            QString::SkipEmptyParts);
#else
                                            Qt::SkipEmptyParts);
#endif

    QList<QString>::const_iterator itr;
    for (itr = acl.constBegin(); itr != acl.constEnd(); ++itr) {
        const QStringList userInfo = (*itr).trimmed().split(QLatin1Char(':'));
        m_usersAcl.insert(userInfo.at(0), userInfo.at(1));
    }
    if (m_usersAcl.isEmpty()) {
        m_usersAcl.insert(QStringLiteral("Everyone"), QStringLiteral("R"));
    }
}




int UserPermissionModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return m_userManager->users().count();
}

int UserPermissionModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return QMetaEnum::fromType<Column>().keyCount();
}

QVariant UserPermissionModel::data(const QModelIndex &index, int role) const
{
    if ((role == Qt::DisplayRole) && (index.column() == ColumnUsername)) {
        return QVariant(m_userManager->users().at(index.row())->name());
    }

    if ((role == Qt::DisplayRole || role == Qt::EditRole) && (index.column() == ColumnAccess)) {
        QMap<QString, QVariant>::ConstIterator itr;
        for (itr = m_usersAcl.constBegin(); itr != m_usersAcl.constEnd(); ++itr) {
            if (itr.key().endsWith(m_userManager->users().at(index.row())->name())) {
                return itr.value();
            }
        }
    }

    return QVariant();
}

Qt::ItemFlags UserPermissionModel::flags(const QModelIndex &index) const
{
    if (index.column() == ColumnUsername) {
        return Qt::ItemIsSelectable;
    }

    if (index.column() == ColumnAccess) {
        return (Qt::ItemIsEnabled | Qt::ItemIsEditable);
    }

    return Qt::NoItemFlags;
}

bool UserPermissionModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if ((role != Qt::EditRole) || (index.column() != ColumnAccess)) {
        return false;
    }

    QString key;
    QMap<QString, QVariant>::ConstIterator itr;
    for (itr = m_usersAcl.constBegin(); itr != m_usersAcl.constEnd(); ++itr) {
        if (itr.key().endsWith(m_userManager->users().at(index.row())->name())) {
            key = itr.key();
            break;
        }
    }

    if (key.isEmpty()) {
        key = m_userManager->users().at(index.row())->name();
    }

    if (value.isNull()) {
        m_usersAcl.take(key);
    } else {
        m_usersAcl.insert(key, value);
    }

    Q_EMIT dataChanged(index, index);
    return true;
}

QString UserPermissionModel::getAcl() const
{
    // ACE order matters. Based on testing samba behaves the following way when checking if a user may do something:
    //   - rights granted stack up until the first applicable denial (r+f = f)
    //   - denials end the lookup BUT all permissions until then are applied!
    // We always put Everyone at the beginning and do not support groups so effectively we behave the same way
    // as on Windows. Everyone is to mean **everyone** and the user rules add on top but the Everyone ACE is the
    // baseline permission for everyone. By extension because of the samba resolution behavior Everyone:D will disable
    // the share pretty much.
    // This has another more important element though: Everyone:R must not be before denials because samba applies
    // any matched reads when it encounters a denial. e.g. Everyone:R,foo:D means foo can still read because D merely
    // ends the lookup, it doesn't take already granted permissions away. With that in mind we need to reshuffle
    // the ACEs so *all* D come first followed by all R and last all F.
    // https://docs.microsoft.com/en-us/windows/win32/secauthz/how-dacls-control-access-to-an-object
    // https://docs.microsoft.com/en-us/windows/win32/secauthz/order-of-aces-in-a-dacl

    // NOTE: D < R < F ordering would also be fine should we ever grow group support I think

    QStringList denials;
    QStringList readables;
    QStringList fulls;
    for (auto it = m_usersAcl.constBegin(); it != m_usersAcl.constEnd(); ++it) {
        const QString &userName = it.key();
        const QString access = it->value<QString>();
        if (access.isEmpty()) {
            continue; // --- undefined (no access)
        }
        // NB: we do not append access because samba is being inconsistent with itself. `net usershare info`
        // uses capital letters, but `net usershare add` will (for example) not accept capital D, so if you were to
        // take the output of info and feed it to add it'd error out -.- ... force everything lower case here
        // so it definitely works with add
        if (access == QLatin1String("D")) {
            denials << userName + QStringLiteral(":d");
        } else if (access == QLatin1String("R")) {
            readables << userName + QStringLiteral(":r");
        } else if (access == QLatin1String("F")) {
            fulls << userName + QStringLiteral(":f");
        } else {
            Q_UNREACHABLE(); // unmapped value WTH
        }
    }

    return (denials + readables + fulls).join(QLatin1Char(','));
}