Warning, file /plasma/flatpak-kcm/autotests/flatpakpermissiontest.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0002 // SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
0003 // SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
0004 
0005 #include <QAbstractItemModel>
0006 #include <QDebug>
0007 #include <QMetaEnum>
0008 #include <QTest>
0009 
0010 #include <KConfig>
0011 #include <KConfigGroup>
0012 #include <KLocalizedString>
0013 
0014 #include <array>
0015 
0016 #include "flatpakcommon.h"
0017 #include "flatpakpermission.h"
0018 
0019 class FlatpakPermissionModelTest : public QObject
0020 {
0021     Q_OBJECT
0022 private:
0023     /** KConfig comparison with a grain of Flatpak specifics. */
0024     bool operatorFlatpakConfigEquals(const KConfig &actual, const KConfig &expected)
0025     {
0026         auto actualGroups = actual.groupList();
0027         actualGroups.sort();
0028         auto expectedGroups = expected.groupList();
0029         expectedGroups.sort();
0030 
0031         if (actualGroups != expectedGroups) {
0032             return false;
0033         }
0034 
0035         for (const auto &group : std::as_const(actualGroups)) {
0036             const auto actualGroup = actual.group(group);
0037             const auto expectedGroup = expected.group(group);
0038 
0039             auto actualKeys = actualGroup.keyList();
0040             actualKeys.sort();
0041             auto expectedKeys = expectedGroup.keyList();
0042             expectedKeys.sort();
0043 
0044             if (actualKeys != expectedKeys) {
0045                 return false;
0046             }
0047 
0048             for (const auto &key : std::as_const(actualKeys)) {
0049                 if (group == QLatin1String(FLATPAK_METADATA_GROUP_CONTEXT)) {
0050                     // Treat everything in [Context] as XDG lists
0051                     auto actualValues = actualGroup.readXdgListEntry(key);
0052                     actualValues.sort();
0053                     auto expectedValues = expectedGroup.readXdgListEntry(key);
0054                     expectedValues.sort();
0055 
0056                     if (actualValues != expectedValues) {
0057                         return false;
0058                     }
0059                 } else {
0060                     auto actualValue = actualGroup.readEntry(key);
0061                     auto expectedValue = expectedGroup.readEntry(key);
0062 
0063                     if (actualValue != expectedValue) {
0064                         return false;
0065                     }
0066                 }
0067             }
0068         }
0069         return true;
0070     }
0071 
0072     bool modelContains(const QAbstractItemModel *model, const QVariant &value, int role = Qt::DisplayRole)
0073     {
0074         for (int i = 0; i < model->rowCount(); i++) {
0075             const auto &data = model->data(model->index(i, 0), role);
0076             if (data == value) {
0077                 return true;
0078             }
0079         }
0080         return false;
0081     }
0082 
0083     QStringList mockMetadataAndOverridesFiles(const QString &flatpakName) const
0084     {
0085         const auto metadata = QFINDTESTDATA(QStringLiteral("fixtures/metadata/%1").arg(flatpakName));
0086         // Metadata should exist, but overrides file might be created on the fly.
0087         static const auto overridesDirectory = QFINDTESTDATA(QStringLiteral("fixtures/overrides"));
0088         const auto override = QStringLiteral("%1/%2").arg(overridesDirectory, flatpakName);
0089         return {metadata, override};
0090     }
0091 
0092     // These are little helpers to avoid writing out default-constructed flags
0093     // every time. These flags are for pure in-memory configs to avoid any
0094     // possible surprizes from KConfig infrastructure.
0095 
0096     static void write(KConfigGroup &group, const QString &key, const QString &value)
0097     {
0098         group.writeEntry(key, value, KConfig::WriteConfigFlags());
0099     };
0100 
0101     static void write(KConfigGroup &group, const QString &key, const QStringList &values)
0102     {
0103         group.writeXdgListEntry(key, values, KConfig::WriteConfigFlags());
0104     };
0105 
0106 private Q_SLOTS:
0107     void init()
0108     {
0109         QDir().rmdir(QFINDTESTDATA("fixtures/overrides/"));
0110     }
0111 
0112     void testCompareFlatpakConfigsEqual()
0113     {
0114         KConfig configA(QString(), KConfig::SimpleConfig);
0115         KConfig configB(QString(), KConfig::SimpleConfig);
0116 
0117         const std::array configs = {&configA, &configB};
0118         for (const auto &config : configs) {
0119             auto group1 = config->group(QLatin1String(FLATPAK_METADATA_GROUP_ENVIRONMENT));
0120             auto group2 = config->group(QLatin1String(FLATPAK_METADATA_GROUP_CONTEXT));
0121 
0122             write(group1, QStringLiteral("key1"), QStringLiteral("value1"));
0123             write(group1, QStringLiteral("key2"), QStringLiteral("value2"));
0124 
0125             auto values = QStringList{QStringLiteral("itemA"), QStringLiteral("itemB")};
0126             write(group2, QStringLiteral("key3"), values);
0127             // Reorder list entries
0128             if (config == &configB) {
0129                 values = QStringList{QStringLiteral("itemC"), QStringLiteral("itemD")};
0130             } else {
0131                 values = QStringList{QStringLiteral("itemD"), QStringLiteral("itemC")};
0132             }
0133             write(group2, QStringLiteral("key4"), values);
0134         }
0135 
0136         QVERIFY(operatorFlatpakConfigEquals(configA, configB));
0137     }
0138 
0139     void testCompareFlatpakConfigsWhereXDGListsDiffer()
0140     {
0141         KConfig configA(QString(), KConfig::SimpleConfig);
0142         KConfig configB(QString(), KConfig::SimpleConfig);
0143 
0144         auto groupA = configA.group(QLatin1String(FLATPAK_METADATA_GROUP_CONTEXT));
0145         auto groupB = configB.group(QLatin1String(FLATPAK_METADATA_GROUP_CONTEXT));
0146 
0147         QStringList values = QStringList{QStringLiteral("itemA"), QStringLiteral("itemB")};
0148         write(groupA, QStringLiteral("key4"), values);
0149         values.append(QStringLiteral("itemC"));
0150         write(groupB, QStringLiteral("key4"), values);
0151 
0152         QVERIFY(!operatorFlatpakConfigEquals(configA, configB));
0153     }
0154 
0155     void testCompareFlatpakConfigsWhereNumberOfGroupsDiffers()
0156     {
0157         KConfig configA(QString(), KConfig::SimpleConfig);
0158         KConfig configB(QString(), KConfig::SimpleConfig);
0159 
0160         const std::array configs = {&configA, &configB};
0161         for (const auto &config : configs) {
0162             const std::array groupNames = {QLatin1String("G1"), QLatin1String("G2")};
0163             for (const auto &groupName : groupNames) {
0164                 auto group = config->group(groupName);
0165                 write(group, QStringLiteral("key1"), QStringLiteral("value1"));
0166             }
0167         }
0168         QVERIFY(operatorFlatpakConfigEquals(configA, configB));
0169         const auto extraGroupName = QLatin1String("GExtra");
0170         {
0171             auto groupA3 = configA.group(extraGroupName);
0172             write(groupA3, QStringLiteral("key1"), QStringLiteral("value1"));
0173         }
0174         QVERIFY(!operatorFlatpakConfigEquals(configA, configB));
0175         configA.deleteGroup(extraGroupName);
0176         QVERIFY(operatorFlatpakConfigEquals(configA, configB));
0177         {
0178             // Test the other way around too
0179             auto groupB3 = configB.group(extraGroupName);
0180             write(groupB3, QStringLiteral("key1"), QStringLiteral("value1"));
0181         }
0182         QVERIFY(!operatorFlatpakConfigEquals(configA, configB));
0183         configB.deleteGroup(extraGroupName);
0184         QVERIFY(operatorFlatpakConfigEquals(configA, configB));
0185     }
0186 
0187     void testMergeConfigObjectsWithBareTarget()
0188     {
0189         KConfig target(QString(), KConfig::SimpleConfig);
0190         KConfig source(QString(), KConfig::SimpleConfig);
0191         KConfig expected(QString(), KConfig::SimpleConfig);
0192 
0193         for (const auto config : {&source, &expected}) {
0194             auto group = config->group(QLatin1String(FLATPAK_METADATA_GROUP_CONTEXT));
0195             write(group, QLatin1String(FLATPAK_METADATA_KEY_SHARED), {QStringLiteral("network")});
0196             write(group, QLatin1String(FLATPAK_METADATA_KEY_SOCKETS), {QStringLiteral("x11"), QStringLiteral("wayland"), QStringLiteral("fallback-x11")});
0197             write(group, QLatin1String(FLATPAK_METADATA_KEY_DEVICES), {QStringLiteral("dri"), QStringLiteral("kvm")});
0198             write(group, QLatin1String(FLATPAK_METADATA_KEY_FEATURES), {QStringLiteral("bluetooth"), QStringLiteral("devel")});
0199             write(group, QLatin1String(FLATPAK_METADATA_KEY_FILESYSTEMS), {QStringLiteral("xdg-download"), QStringLiteral("~/path")});
0200 
0201             for (const auto &groupName : {
0202                      QLatin1String(FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY),
0203                      QLatin1String(FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY),
0204                      QLatin1String(FLATPAK_METADATA_GROUP_ENVIRONMENT),
0205                  }) {
0206                 auto group = config->group(groupName);
0207                 write(group, QStringLiteral("abc.def"), QStringLiteral("talk"));
0208                 write(group, QStringLiteral("xyz.xyz"), QStringLiteral("see"));
0209             }
0210         }
0211 
0212         FlatpakOverrides::merge(target, source);
0213         QVERIFY(operatorFlatpakConfigEquals(target, expected));
0214     }
0215 
0216     void testMergeConfigObjectsNonOverlapping()
0217     {
0218         // In this test case entries are not overlapping, i.e. they are unique to source and target.
0219 
0220         KConfig target(QString(), KConfig::SimpleConfig);
0221         KConfig source(QString(), KConfig::SimpleConfig);
0222         KConfig expected(QString(), KConfig::SimpleConfig);
0223 
0224         // put some stuff in target, some in source, and both in the expected config.
0225         {
0226             auto group = target.group(QLatin1String(FLATPAK_METADATA_GROUP_CONTEXT));
0227             write(group, QLatin1String(FLATPAK_METADATA_KEY_SHARED), {QStringLiteral("network")});
0228             write(group, QLatin1String(FLATPAK_METADATA_KEY_SOCKETS), {QStringLiteral("fallback-x11")});
0229             write(group, QLatin1String(FLATPAK_METADATA_KEY_DEVICES), {QStringLiteral("dri")});
0230             write(group, QLatin1String(FLATPAK_METADATA_KEY_FEATURES), {QStringLiteral("devel")});
0231             write(group, QLatin1String(FLATPAK_METADATA_KEY_FILESYSTEMS), {QStringLiteral("~/path")});
0232 
0233             for (const auto &groupName : {
0234                      QLatin1String(FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY),
0235                      QLatin1String(FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY),
0236                      QLatin1String(FLATPAK_METADATA_GROUP_ENVIRONMENT),
0237                  }) {
0238                 auto group = target.group(groupName);
0239                 write(group, QStringLiteral("abc.def"), QStringLiteral("talk"));
0240             }
0241         }
0242         {
0243             auto group = source.group(QLatin1String(FLATPAK_METADATA_GROUP_CONTEXT));
0244             write(group, QLatin1String(FLATPAK_METADATA_KEY_SHARED), {QStringLiteral("ipc")});
0245             write(group, QLatin1String(FLATPAK_METADATA_KEY_SOCKETS), {QStringLiteral("x11"), QStringLiteral("wayland")});
0246             write(group, QLatin1String(FLATPAK_METADATA_KEY_DEVICES), {QStringLiteral("kvm")});
0247             write(group, QLatin1String(FLATPAK_METADATA_KEY_FEATURES), {QStringLiteral("bluetooth")});
0248             write(group, QLatin1String(FLATPAK_METADATA_KEY_FILESYSTEMS), {QStringLiteral("xdg-download")});
0249 
0250             for (const auto &groupName : {
0251                      QLatin1String(FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY),
0252                      QLatin1String(FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY),
0253                      QLatin1String(FLATPAK_METADATA_GROUP_ENVIRONMENT),
0254                  }) {
0255                 auto group = source.group(groupName);
0256                 write(group, QStringLiteral("xyz.xyz"), QStringLiteral("see"));
0257             }
0258         }
0259         {
0260             auto group = expected.group(QLatin1String(FLATPAK_METADATA_GROUP_CONTEXT));
0261             write(group, QLatin1String(FLATPAK_METADATA_KEY_SHARED), {QStringLiteral("network"), QStringLiteral("ipc")});
0262             write(group, QLatin1String(FLATPAK_METADATA_KEY_SOCKETS), {QStringLiteral("x11"), QStringLiteral("wayland"), QStringLiteral("fallback-x11")});
0263             write(group, QLatin1String(FLATPAK_METADATA_KEY_DEVICES), {QStringLiteral("dri"), QStringLiteral("kvm")});
0264             write(group, QLatin1String(FLATPAK_METADATA_KEY_FEATURES), {QStringLiteral("bluetooth"), QStringLiteral("devel")});
0265             write(group, QLatin1String(FLATPAK_METADATA_KEY_FILESYSTEMS), {QStringLiteral("xdg-download"), QStringLiteral("~/path")});
0266 
0267             for (const auto &groupName : {
0268                      QLatin1String(FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY),
0269                      QLatin1String(FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY),
0270                      QLatin1String(FLATPAK_METADATA_GROUP_ENVIRONMENT),
0271                  }) {
0272                 auto group = expected.group(groupName);
0273                 write(group, QStringLiteral("abc.def"), QStringLiteral("talk"));
0274                 write(group, QStringLiteral("xyz.xyz"), QStringLiteral("see"));
0275             }
0276         }
0277 
0278         FlatpakOverrides::merge(target, source);
0279         QVERIFY(operatorFlatpakConfigEquals(target, expected));
0280     }
0281 
0282     void testMergeConfigObjectsOverlapping()
0283     {
0284         // In this test some entries in target are overridden by source.
0285 
0286         KConfig target(QString(), KConfig::SimpleConfig);
0287         KConfig source(QString(), KConfig::SimpleConfig);
0288         KConfig expected(QString(), KConfig::SimpleConfig);
0289 
0290         {
0291             auto group = target.group(QLatin1String(FLATPAK_METADATA_GROUP_CONTEXT));
0292             write(group, QLatin1String(FLATPAK_METADATA_KEY_SHARED), {QStringLiteral("!network"), QStringLiteral("ipc")});
0293             write(group, QLatin1String(FLATPAK_METADATA_KEY_SOCKETS), {QStringLiteral("x11"), QStringLiteral("wayland"), QStringLiteral("fallback-x11")});
0294             write(group, QLatin1String(FLATPAK_METADATA_KEY_DEVICES), {QStringLiteral("dri"), QStringLiteral("kvm")});
0295             write(group, QLatin1String(FLATPAK_METADATA_KEY_FEATURES), {QStringLiteral("bluetooth"), QStringLiteral("!devel")});
0296             write(group, QLatin1String(FLATPAK_METADATA_KEY_FILESYSTEMS), {QStringLiteral("xdg-download"), QStringLiteral("~/path")});
0297 
0298             for (const auto &groupName : {
0299                      QLatin1String(FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY),
0300                      QLatin1String(FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY),
0301                      QLatin1String(FLATPAK_METADATA_GROUP_ENVIRONMENT),
0302                  }) {
0303                 auto group = target.group(groupName);
0304                 write(group, QStringLiteral("abc.def"), QStringLiteral("talk"));
0305                 write(group, QStringLiteral("xyz.xyz"), QStringLiteral("own"));
0306             }
0307         }
0308         {
0309             auto group = source.group(QLatin1String(FLATPAK_METADATA_GROUP_CONTEXT));
0310             write(group, QLatin1String(FLATPAK_METADATA_KEY_SHARED), {QStringLiteral("network"), QStringLiteral("!ipc")});
0311             write(group, QLatin1String(FLATPAK_METADATA_KEY_SOCKETS), {QStringLiteral("!fallback-x11"), QStringLiteral("pulseaudio")});
0312             write(group, QLatin1String(FLATPAK_METADATA_KEY_DEVICES), {QStringLiteral("!dri")});
0313             write(group, QLatin1String(FLATPAK_METADATA_KEY_FEATURES), {QStringLiteral("devel")});
0314             write(group, QLatin1String(FLATPAK_METADATA_KEY_FILESYSTEMS), {QStringLiteral("!xdg-download"), QStringLiteral("host-etc")});
0315 
0316             for (const auto &groupName : {
0317                      QLatin1String(FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY),
0318                      QLatin1String(FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY),
0319                      QLatin1String(FLATPAK_METADATA_GROUP_ENVIRONMENT),
0320                  }) {
0321                 auto group = source.group(groupName);
0322                 write(group, QStringLiteral("xyz.xyz"), QStringLiteral("none"));
0323                 write(group, QStringLiteral("org.kde.*"), QStringLiteral("see"));
0324             }
0325         }
0326         {
0327             auto group = expected.group(QLatin1String(FLATPAK_METADATA_GROUP_CONTEXT));
0328             write(group, QLatin1String(FLATPAK_METADATA_KEY_SHARED), {QStringLiteral("network"), QStringLiteral("!ipc")});
0329             write(group,
0330                   QLatin1String(FLATPAK_METADATA_KEY_SOCKETS),
0331                   {QStringLiteral("x11"), QStringLiteral("wayland"), QStringLiteral("!fallback-x11"), QStringLiteral("pulseaudio")});
0332             write(group, QLatin1String(FLATPAK_METADATA_KEY_DEVICES), {QStringLiteral("!dri"), QStringLiteral("kvm")});
0333             write(group, QLatin1String(FLATPAK_METADATA_KEY_FEATURES), {QStringLiteral("bluetooth"), QStringLiteral("devel")});
0334             write(group,
0335                   QLatin1String(FLATPAK_METADATA_KEY_FILESYSTEMS),
0336                   {QStringLiteral("!xdg-download"), QStringLiteral("~/path"), QStringLiteral("host-etc")});
0337 
0338             for (const auto &groupName : {
0339                      QLatin1String(FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY),
0340                      QLatin1String(FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY),
0341                      QLatin1String(FLATPAK_METADATA_GROUP_ENVIRONMENT),
0342                  }) {
0343                 auto group = expected.group(groupName);
0344                 write(group, QStringLiteral("abc.def"), QStringLiteral("talk"));
0345                 write(group, QStringLiteral("xyz.xyz"), QStringLiteral("none"));
0346                 write(group, QStringLiteral("org.kde.*"), QStringLiteral("see"));
0347             }
0348         }
0349 
0350         FlatpakOverrides::merge(target, source);
0351         QVERIFY(operatorFlatpakConfigEquals(target, expected));
0352     }
0353 
0354     void testMergeConfigFiles()
0355     {
0356         const QStringList overridesFiles = {
0357             QFINDTESTDATA(QStringLiteral("fixtures/metadata/com.example.cascade.metadata")),
0358             QFINDTESTDATA(QStringLiteral("fixtures/overrides.in/com.example.cascade.system-global")),
0359             QFINDTESTDATA(QStringLiteral("fixtures/overrides.in/com.example.cascade.user-global")),
0360             QFINDTESTDATA(QStringLiteral("fixtures/overrides.in/com.example.cascade.user-app")),
0361         };
0362         const auto expectedFilepath = QFINDTESTDATA(QStringLiteral("fixtures/overrides.out/com.example.cascade.final"));
0363 
0364         const auto actual = FlatpakOverrides::loadAndMerge(overridesFiles);
0365         const KConfig expected(expectedFilepath, KConfig::SimpleConfig);
0366 
0367         QVERIFY(operatorFlatpakConfigEquals(*actual, expected));
0368     }
0369 
0370     void testReadMergedMultiLevelOverrides()
0371     {
0372         const QStringList overrideFiles = {
0373             QFINDTESTDATA(QStringLiteral("fixtures/metadata/com.example.cascade.metadata")),
0374             QFINDTESTDATA(QStringLiteral("fixtures/overrides.in/com.example.cascade.system-global")),
0375             QFINDTESTDATA(QStringLiteral("fixtures/overrides.in/com.example.cascade.user-global")),
0376             QFINDTESTDATA(QStringLiteral("fixtures/overrides.in/com.example.cascade.user-app")),
0377         };
0378         FlatpakPermissionModel model;
0379         FlatpakReferencesModel referencesModel;
0380         FlatpakReference
0381             reference(&referencesModel, "com.example.cascade.metadata", "x86_64", "stable", "1.0.0", "com.example.cascade.metadata", QUrl(), overrideFiles);
0382         model.setReference(&reference);
0383         model.setShowAdvanced(true);
0384         model.load();
0385 
0386         {
0387             const auto index = model.findPermissionIndex(FlatpakPermissionsSectionType::Sockets, QLatin1String("x11"));
0388             QVERIFY(index.isValid());
0389             QVERIFY(!model.data(index, FlatpakPermissionModel::IsDefaultEnabled).toBool());
0390             QVERIFY(model.data(index, FlatpakPermissionModel::IsEffectiveEnabled).toBool());
0391         }
0392         {
0393             const auto index = model.findPermissionIndex(FlatpakPermissionsSectionType::Filesystems, QLatin1String("xdg-pictures"));
0394             QVERIFY(index.isValid());
0395             QVERIFY(model.data(index, FlatpakPermissionModel::IsDefaultEnabled).toBool());
0396             QVERIFY(model.data(index, FlatpakPermissionModel::IsEffectiveEnabled).toBool());
0397             QCOMPARE(model.data(index, FlatpakPermissionModel::DefaultValue).value<FlatpakFilesystemsEntry::AccessMode>(),
0398                      FlatpakFilesystemsEntry::AccessMode::ReadOnly);
0399             QCOMPARE(model.data(index, FlatpakPermissionModel::EffectiveValue).value<FlatpakFilesystemsEntry::AccessMode>(),
0400                      FlatpakFilesystemsEntry::AccessMode::Create);
0401         }
0402         {
0403             const auto index = model.findPermissionIndex(FlatpakPermissionsSectionType::SystemBus, QLatin1String("com.example.system1"));
0404             QVERIFY(index.isValid());
0405             QCOMPARE(model.data(index, FlatpakPermissionModel::DefaultValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_TALK);
0406             QCOMPARE(model.data(index, FlatpakPermissionModel::EffectiveValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_SEE);
0407         }
0408         {
0409             const auto index = model.findPermissionIndex(FlatpakPermissionsSectionType::SystemBus, QLatin1String("com.example.system2"));
0410             QVERIFY(index.isValid());
0411             QVERIFY(!model.data(index, FlatpakPermissionModel::IsDefaultEnabled).toBool());
0412             QVERIFY(model.data(index, FlatpakPermissionModel::IsEffectiveEnabled).toBool());
0413             QCOMPARE(model.data(index, FlatpakPermissionModel::DefaultValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_OWN);
0414             QCOMPARE(model.data(index, FlatpakPermissionModel::EffectiveValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_OWN);
0415         }
0416         {
0417             const auto index = model.findPermissionIndex(FlatpakPermissionsSectionType::SessionBus, QLatin1String("com.example.session1"));
0418             QVERIFY(index.isValid());
0419             QCOMPARE(model.data(index, FlatpakPermissionModel::DefaultValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_TALK);
0420             QCOMPARE(model.data(index, FlatpakPermissionModel::EffectiveValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_NONE);
0421         }
0422         {
0423             const auto index = model.findPermissionIndex(FlatpakPermissionsSectionType::Environment, QLatin1String("EXAMPLE_NAME"));
0424             QVERIFY(index.isValid());
0425             QCOMPARE(model.data(index, FlatpakPermissionModel::DefaultValue).toString(), QLatin1String("user"));
0426             QCOMPARE(model.data(index, FlatpakPermissionModel::EffectiveValue).toString(), QLatin1String("user-app"));
0427         }
0428     }
0429 
0430     void testRead()
0431     {
0432         // The primary motivation behind this test is to make sure that translations aren't being pulled in for the raw names.
0433         FlatpakPermissionModel model;
0434         FlatpakReferencesModel referencesModel;
0435         FlatpakReference reference(&referencesModel,
0436                                    "com.discordapp.Discord",
0437                                    "x86_64",
0438                                    "stable",
0439                                    "0.0.24",
0440                                    "Discord",
0441                                    QUrl(),
0442                                    mockMetadataAndOverridesFiles("com.discordapp.Discord"));
0443         model.setReference(&reference);
0444         model.load();
0445         bool containsNetwork = false;
0446         bool containsXdgDownload = false;
0447         for (auto i = 0; i < model.rowCount(); ++i) {
0448             const QString name = model.data(model.index(i, 0), FlatpakPermissionModel::Name).toString();
0449             if (name == "network") {
0450                 containsNetwork = true;
0451             }
0452 
0453             if (name == "xdg-download") {
0454                 containsXdgDownload = true;
0455                 QVERIFY(!model.data(model.index(i, 0), FlatpakPermissionModel::CanBeDisabled).toBool());
0456                 QCOMPARE(model.data(model.index(i, 0), FlatpakPermissionModel::IsEffectiveEnabled), true);
0457                 QCOMPARE(model.data(model.index(i, 0), FlatpakPermissionModel::EffectiveValue).value<FlatpakFilesystemsEntry::AccessMode>(),
0458                          FlatpakFilesystemsEntry::AccessMode::ReadWrite);
0459             }
0460         }
0461 
0462         QVERIFY(containsNetwork);
0463         QVERIFY(containsXdgDownload);
0464         QVERIFY(model.permissionExists(FlatpakPermissionsSectionType::Basic, QLatin1String("network")));
0465         QVERIFY(!model.permissionExists(FlatpakPermissionsSectionType::Basic, QLatin1String("yolo-foobar")));
0466     }
0467 
0468     void testLoadSameSimpleOverride()
0469     {
0470         // Test that values that are for some reason enabled and present in
0471         // both defaults and overrides are parsed correctly.
0472         FlatpakPermissionModel model;
0473         FlatpakReferencesModel referencesModel;
0474         FlatpakReference reference(&referencesModel,
0475                                    "com.example.same.override",
0476                                    "x86_64",
0477                                    "stable",
0478                                    "0.0.24",
0479                                    "Test Same Simple Override",
0480                                    QUrl(),
0481                                    mockMetadataAndOverridesFiles("com.example.same.override"));
0482         model.setReference(&reference);
0483         model.setShowAdvanced(true);
0484         model.load();
0485 
0486         QStringList found;
0487 
0488         for (auto i = 0; i < model.rowCount(); ++i) {
0489             const auto name = model.data(model.index(i, 0), FlatpakPermissionModel::Name).toString();
0490 
0491             const auto isDefaultEnabled = model.data(model.index(i, 0), FlatpakPermissionModel::IsDefaultEnabled).toBool();
0492             const auto isEffectiveEnabled = model.data(model.index(i, 0), FlatpakPermissionModel::IsEffectiveEnabled).toBool();
0493 
0494             if (name == QLatin1String("ipc")) {
0495                 QVERIFY(isDefaultEnabled);
0496                 QVERIFY(isEffectiveEnabled);
0497             } else if (name == QLatin1String("pulseaudio")) {
0498                 QVERIFY(isDefaultEnabled);
0499                 QVERIFY(isEffectiveEnabled);
0500             } else if (name == QLatin1String("dri")) {
0501                 QVERIFY(!isDefaultEnabled);
0502                 QVERIFY(!isEffectiveEnabled);
0503             } else if (name == QLatin1String("bluetooth")) {
0504                 QVERIFY(!isDefaultEnabled);
0505                 QVERIFY(isEffectiveEnabled);
0506             } else if (name == QLatin1String("xdg-download")) {
0507                 QVERIFY(isDefaultEnabled);
0508                 QVERIFY(isEffectiveEnabled);
0509             } else {
0510                 continue;
0511             }
0512 
0513             found.append(name);
0514         }
0515 
0516         QVERIFY(found.contains(QLatin1String("ipc")));
0517         QVERIFY(found.contains(QLatin1String("pulseaudio")));
0518         QVERIFY(found.contains(QLatin1String("dri")));
0519         QVERIFY(found.contains(QLatin1String("bluetooth")));
0520         QVERIFY(found.contains(QLatin1String("xdg-download")));
0521     }
0522 
0523     void testDefaultFilesystemsGoFirst()
0524     {
0525         // If there are no custom filesystems specified in defaults, then all custom ones should go below it.
0526         // The Discord test above can't be reused, because it has custom filesystems in base metadata, so
0527         // the well-known "host-etc" filesystem would not be the last one. But we want to test for the last
0528         // default index too.
0529         FlatpakPermissionModel model;
0530         FlatpakReferencesModel referencesModel;
0531         FlatpakReference reference(&referencesModel, //
0532                                    "org.gnome.dfeet",
0533                                    "x86_64",
0534                                    "stable",
0535                                    "0.3.16",
0536                                    "D-Feet",
0537                                    QUrl(),
0538                                    mockMetadataAndOverridesFiles("org.gnome.dfeet"));
0539         model.setReference(&reference);
0540         model.load();
0541         QStringList filesystems;
0542         for (auto i = 0; i < model.rowCount(); ++i) {
0543             const QString name = model.data(model.index(i, 0), FlatpakPermissionModel::Name).toString();
0544             // collect all filesystems
0545             const auto section = model.data(model.index(i, 0), FlatpakPermissionModel::Section).value<FlatpakPermissionsSectionType::Type>();
0546             if (section == FlatpakPermissionsSectionType::Filesystems) {
0547                 filesystems.append(name);
0548             }
0549         }
0550 
0551         // Note: update this name when more standard filesystems are added.
0552         const QString hostEtc = QLatin1String("host-etc");
0553         const auto indexOfHostEtc = filesystems.indexOf(hostEtc);
0554         QCOMPARE(indexOfHostEtc, 3);
0555         // But custom overrides should come after standard ones anyway.
0556         const auto custom = QLatin1String("/custom/path");
0557         const auto indexOfCustom = filesystems.indexOf(custom);
0558         QVERIFY(indexOfCustom != -1);
0559         QVERIFY(indexOfHostEtc < indexOfCustom);
0560     }
0561 
0562     void testDBusNonePolicy()
0563     {
0564         // This test verifies that "none" policy for D-Bus services works just
0565         // like other regular policies. It can be loaded, changed to, changed
0566         // from, and saved.
0567         const auto metadataAndOverridesFiles = mockMetadataAndOverridesFiles("org.gnome.dfeet");
0568         FlatpakPermissionModel model;
0569         FlatpakReferencesModel referencesModel;
0570         FlatpakReference reference(&referencesModel, //
0571                                    "org.gnome.dfeet",
0572                                    "x86_64",
0573                                    "stable",
0574                                    "0.3.16",
0575                                    "D-Feet",
0576                                    QUrl(),
0577                                    metadataAndOverridesFiles);
0578         model.setReference(&reference);
0579         model.load();
0580         model.setShowAdvanced(true);
0581 
0582         // This service is set to "none" by default (in metadata).
0583         const auto service0 = QLatin1String("com.example.service0");
0584         int indexOfService0 = -1;
0585         // This service is set to "none" in override file.
0586         const auto service1 = QLatin1String("com.example.service1");
0587         int indexOfService1 = -1;
0588         // This service is set to "see" in override, but will be changed to "none".
0589         const auto service2 = QLatin1String("com.example.service2");
0590         int indexOfService2 = -1;
0591 
0592         for (auto i = 0; i < model.rowCount(); ++i) {
0593             const QString name = model.data(model.index(i, 0), FlatpakPermissionModel::Name).toString();
0594             const auto section = model.data(model.index(i, 0), FlatpakPermissionModel::Section).value<FlatpakPermissionsSectionType::Type>();
0595             if (section == FlatpakPermissionsSectionType::SessionBus) {
0596                 if (name == service0) {
0597                     indexOfService0 = i;
0598                 } else if (name == service1) {
0599                     indexOfService1 = i;
0600                 } else if (name == service2) {
0601                     indexOfService2 = i;
0602                 }
0603             }
0604         }
0605         QVERIFY(indexOfService0 != -1);
0606         QVERIFY(indexOfService1 != -1);
0607         QVERIFY(indexOfService2 != -1);
0608         const auto name0 = model.data(model.index(indexOfService0, 0), FlatpakPermissionModel::EffectiveValue).value<FlatpakPolicy>();
0609         const auto name1 = model.data(model.index(indexOfService1, 0), FlatpakPermissionModel::EffectiveValue).value<FlatpakPolicy>();
0610         const auto name2 = model.data(model.index(indexOfService2, 0), FlatpakPermissionModel::EffectiveValue).value<FlatpakPolicy>();
0611         QCOMPARE(name0, FlatpakPolicy::FLATPAK_POLICY_NONE);
0612         QCOMPARE(name1, FlatpakPolicy::FLATPAK_POLICY_NONE);
0613         QCOMPARE(name2, FlatpakPolicy::FLATPAK_POLICY_SEE);
0614         QVERIFY(model.data(model.index(indexOfService0, 0), FlatpakPermissionModel::IsDefaultEnabled).toBool());
0615         QVERIFY(!model.data(model.index(indexOfService1, 0), FlatpakPermissionModel::IsDefaultEnabled).toBool());
0616         QVERIFY(!model.data(model.index(indexOfService2, 0), FlatpakPermissionModel::IsDefaultEnabled).toBool());
0617         QVERIFY(!model.data(model.index(indexOfService0, 0), FlatpakPermissionModel::CanBeDisabled).toBool());
0618         QVERIFY(model.data(model.index(indexOfService1, 0), FlatpakPermissionModel::CanBeDisabled).toBool());
0619         QVERIFY(model.data(model.index(indexOfService2, 0), FlatpakPermissionModel::CanBeDisabled).toBool());
0620 
0621         const auto setAndCheckBus = [&](const QString &value, FlatpakPolicy newPolicy) {
0622             model.setPermissionValueAtRow(indexOfService2, newPolicy);
0623             const auto name2i18nValue = model.data(model.index(indexOfService2, 0), FlatpakPermissionModel::EffectiveValue).value<FlatpakPolicy>();
0624             QCOMPARE(name2i18nValue, newPolicy);
0625 
0626             QVERIFY(model.isSaveNeeded());
0627             model.save();
0628 
0629             const KConfig expectedDesktopFile(metadataAndOverridesFiles.last());
0630             const auto group = expectedDesktopFile.group(QLatin1String(FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY));
0631             const auto name2value = group.readEntry(service2);
0632             QCOMPARE(name2value, value);
0633         };
0634 
0635         setAndCheckBus(QLatin1String("none"), FlatpakPolicy::FLATPAK_POLICY_NONE);
0636         setAndCheckBus(QLatin1String("see"), FlatpakPolicy::FLATPAK_POLICY_SEE);
0637         setAndCheckBus(QLatin1String("talk"), FlatpakPolicy::FLATPAK_POLICY_TALK);
0638         setAndCheckBus(QLatin1String("own"), FlatpakPolicy::FLATPAK_POLICY_OWN);
0639 
0640         const auto checkPossibleValues = [&](int row) {
0641             const auto values = model.data(model.index(row, 0), FlatpakPermissionModel::ValuesModel).value<QAbstractListModel *>();
0642             QVERIFY(modelContains(values, i18n("None")));
0643             QVERIFY(modelContains(values, i18n("see")));
0644             QVERIFY(modelContains(values, i18n("talk")));
0645             QVERIFY(modelContains(values, i18n("own")));
0646         };
0647         checkPossibleValues(indexOfService0);
0648         checkPossibleValues(indexOfService1);
0649         checkPossibleValues(indexOfService2);
0650 
0651         const auto isEffectiveEnabled = [&](int row) -> bool {
0652             return model.data(model.index(row, 0), FlatpakPermissionModel::IsEffectiveEnabled).toBool();
0653         };
0654         QVERIFY(isEffectiveEnabled(indexOfService0));
0655         QVERIFY(isEffectiveEnabled(indexOfService1));
0656         QVERIFY(isEffectiveEnabled(indexOfService2));
0657 
0658         // Toggling non-default bus entry should disable it (i.e. mark for deletion)
0659         model.togglePermissionAtRow(indexOfService2);
0660         QVERIFY(!isEffectiveEnabled(indexOfService2));
0661         // Reloading data should re-enable it again.
0662         model.load();
0663         QVERIFY(isEffectiveEnabled(indexOfService2));
0664         // Disabling non-default bus entry and saving it should remove it from override file.
0665         model.togglePermissionAtRow(indexOfService2);
0666         QVERIFY(!isEffectiveEnabled(indexOfService2));
0667         model.save();
0668         {
0669             const KConfig expectedDesktopFile(metadataAndOverridesFiles.last());
0670             const auto group = expectedDesktopFile.group(QLatin1String(FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY));
0671             QVERIFY(!group.hasKey(service2));
0672         }
0673         // Re-enabling non-default bus entry and saving it should add it back to override file.
0674         model.togglePermissionAtRow(indexOfService2);
0675         QVERIFY(isEffectiveEnabled(indexOfService2));
0676         model.save();
0677         {
0678             const KConfig expectedDesktopFile(metadataAndOverridesFiles.last());
0679             const auto group = expectedDesktopFile.group(QLatin1String(FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY));
0680             QVERIFY(group.hasKey(service2));
0681         }
0682     }
0683 
0684     void testDBusBrokenPolicies()
0685     {
0686         FlatpakPermissionModel model;
0687         FlatpakReferencesModel referencesModel;
0688         FlatpakReference reference(&referencesModel, //
0689                                    "org.gnome.Boxes",
0690                                    "x86_64",
0691                                    "stable",
0692                                    "43.1",
0693                                    "Boxes",
0694                                    QUrl(),
0695                                    mockMetadataAndOverridesFiles("org.gnome.Boxes"));
0696         model.setReference(&reference);
0697         model.load();
0698         model.setShowAdvanced(true);
0699 
0700         // This service is set to "hello" by default (in metadata).
0701         const auto session0 = QLatin1String("org.freedesktop.secrets");
0702         int indexOfSession0 = -1;
0703         // This service is set to "talk" by default (in metadata) but overriden with an invalid policy string.
0704         const auto session1 = QLatin1String("ca.desrt.dconf");
0705         int indexOfSession1 = -1;
0706         // This service is set to an invalid policy string by default (in metadata) but overridden with "see".
0707         const auto system = QLatin1String("org.freedesktop.timedate1");
0708         int indexOfSystem = -1;
0709 
0710         for (auto i = 0; i < model.rowCount(); ++i) {
0711             const auto name = model.data(model.index(i, 0), FlatpakPermissionModel::Name).toString();
0712             const auto section = model.data(model.index(i, 0), FlatpakPermissionModel::Section).value<FlatpakPermissionsSectionType::Type>();
0713             if (section == FlatpakPermissionsSectionType::SessionBus) {
0714                 if (name == session0) {
0715                     indexOfSession0 = i;
0716                 } else if (name == session1) {
0717                     indexOfSession1 = i;
0718                 }
0719             } else if (section == FlatpakPermissionsSectionType::SystemBus) {
0720                 if (name == system) {
0721                     indexOfSystem = i;
0722                 }
0723             }
0724         }
0725         QVERIFY(indexOfSession0 != -1);
0726         QVERIFY(indexOfSession1 != -1);
0727         QVERIFY(indexOfSystem != -1);
0728 
0729         QVERIFY(model.data(model.index(indexOfSession0, 0), FlatpakPermissionModel::IsDefaultEnabled).toBool());
0730         QCOMPARE(model.data(model.index(indexOfSession0, 0), FlatpakPermissionModel::DefaultValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_NONE);
0731         QCOMPARE(model.data(model.index(indexOfSession0, 0), FlatpakPermissionModel::EffectiveValue).value<FlatpakPolicy>(),
0732                  FlatpakPolicy::FLATPAK_POLICY_NONE);
0733 
0734         QVERIFY(model.data(model.index(indexOfSession1, 0), FlatpakPermissionModel::IsDefaultEnabled).toBool());
0735         QVERIFY(model.data(model.index(indexOfSession1, 0), FlatpakPermissionModel::IsEffectiveEnabled).toBool());
0736         QCOMPARE(model.data(model.index(indexOfSession1, 0), FlatpakPermissionModel::DefaultValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_TALK);
0737         QCOMPARE(model.data(model.index(indexOfSession1, 0), FlatpakPermissionModel::EffectiveValue).value<FlatpakPolicy>(),
0738                  FlatpakPolicy::FLATPAK_POLICY_NONE);
0739 
0740         QVERIFY(model.data(model.index(indexOfSystem, 0), FlatpakPermissionModel::IsDefaultEnabled).toBool());
0741         QVERIFY(model.data(model.index(indexOfSystem, 0), FlatpakPermissionModel::IsEffectiveEnabled).toBool());
0742         QCOMPARE(model.data(model.index(indexOfSystem, 0), FlatpakPermissionModel::DefaultValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_NONE);
0743         QCOMPARE(model.data(model.index(indexOfSystem, 0), FlatpakPermissionModel::EffectiveValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_SEE);
0744     }
0745 
0746     void testMutable()
0747     {
0748         // Ensure override files mutate properly
0749         const auto metadataAndOverridesFiles = mockMetadataAndOverridesFiles("com.discordapp.Discord");
0750         FlatpakPermissionModel model;
0751         FlatpakReferencesModel referencesModel;
0752         FlatpakReference reference(&referencesModel, //
0753                                    "com.discordapp.Discord",
0754                                    "x86_64",
0755                                    "stable",
0756                                    "0.0.24",
0757                                    "Discord",
0758                                    QUrl(),
0759                                    metadataAndOverridesFiles);
0760         model.setReference(&reference);
0761         model.load();
0762         model.setShowAdvanced(true);
0763 
0764         const auto filesystem = QLatin1String("~/path");
0765         const auto session = QLatin1String("com.example.session");
0766         const auto system = QLatin1String("com.example.system");
0767         const auto envName = QLatin1String("SOME_ENV");
0768         const auto envValue = QLatin1String("abc123");
0769 
0770         model.addUserEnteredPermission(FlatpakPermissionsSectionType::Filesystems,
0771                                        filesystem,
0772                                        QVariant::fromValue(FlatpakFilesystemsEntry::AccessMode::Create));
0773         model.addUserEnteredPermission(FlatpakPermissionsSectionType::SessionBus, session, FlatpakPolicy::FLATPAK_POLICY_TALK);
0774         // Try int cast to make sure QML/JS works fine too.
0775         model.addUserEnteredPermission(FlatpakPermissionsSectionType::SystemBus, system, FlatpakPolicy::FLATPAK_POLICY_SEE);
0776         model.addUserEnteredPermission(FlatpakPermissionsSectionType::Environment, envName, envValue);
0777 
0778         for (auto i = 0; i < model.rowCount(); ++i) {
0779             const QString name = model.data(model.index(i, 0), FlatpakPermissionModel::Name).toString();
0780             if (name == "host") {
0781                 const auto metaEnum = QMetaEnum::fromType<FlatpakPermissionModel::Roles>();
0782                 for (auto j = 0; j < metaEnum.keyCount(); ++j) { // purely for debugging purposes
0783                     qDebug() << metaEnum.key(j) << model.data(model.index(i, 0), metaEnum.value(j));
0784                 }
0785 
0786                 QCOMPARE(model.data(model.index(i, 0), FlatpakPermissionModel::IsEffectiveEnabled), false);
0787                 QVERIFY(model.data(model.index(i, 0), FlatpakPermissionModel::CanBeDisabled).toBool());
0788                 model.togglePermissionAtRow(i);
0789                 QCOMPARE(model.data(model.index(i, 0), FlatpakPermissionModel::IsEffectiveEnabled), true);
0790             }
0791 
0792             if (name == "org.kde.StatusNotifierWatcher") {
0793                 model.setPermissionValueAtRow(i, FlatpakPolicy::FLATPAK_POLICY_OWN);
0794             }
0795 
0796             if (name == "host-os") {
0797                 // Make sure the config manipulation works across multiple changes
0798                 model.setPermissionValueAtRow(i, QVariant::fromValue(FlatpakFilesystemsEntry::AccessMode::ReadOnly));
0799                 model.setPermissionValueAtRow(i, QVariant::fromValue(FlatpakFilesystemsEntry::AccessMode::ReadWrite));
0800                 model.setPermissionValueAtRow(i, QVariant::fromValue(FlatpakFilesystemsEntry::AccessMode::Create));
0801                 model.setPermissionValueAtRow(i, QVariant::fromValue(FlatpakFilesystemsEntry::AccessMode::ReadOnly));
0802             }
0803 
0804             if (name == filesystem) {
0805                 QVERIFY(model.data(model.index(i, 0), FlatpakPermissionModel::CanBeDisabled).toBool());
0806                 QVERIFY(!model.data(model.index(i, 0), FlatpakPermissionModel::IsDefaultEnabled).toBool());
0807                 QVERIFY(model.data(model.index(i, 0), FlatpakPermissionModel::IsEffectiveEnabled).toBool());
0808                 QCOMPARE(model.data(model.index(i, 0), FlatpakPermissionModel::DefaultValue).value<FlatpakFilesystemsEntry::AccessMode>(),
0809                          FlatpakFilesystemsEntry::AccessMode::Create);
0810                 QCOMPARE(model.data(model.index(i, 0), FlatpakPermissionModel::EffectiveValue).value<FlatpakFilesystemsEntry::AccessMode>(),
0811                          FlatpakFilesystemsEntry::AccessMode::Create);
0812             }
0813 
0814             if (name == session) {
0815                 QVERIFY(model.data(model.index(i, 0), FlatpakPermissionModel::CanBeDisabled).toBool());
0816                 QVERIFY(!model.data(model.index(i, 0), FlatpakPermissionModel::IsDefaultEnabled).toBool());
0817                 QVERIFY(model.data(model.index(i, 0), FlatpakPermissionModel::IsEffectiveEnabled).toBool());
0818                 QCOMPARE(model.data(model.index(i, 0), FlatpakPermissionModel::DefaultValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_TALK);
0819                 QCOMPARE(model.data(model.index(i, 0), FlatpakPermissionModel::EffectiveValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_TALK);
0820             }
0821 
0822             if (name == system) {
0823                 QVERIFY(model.data(model.index(i, 0), FlatpakPermissionModel::CanBeDisabled).toBool());
0824                 QVERIFY(!model.data(model.index(i, 0), FlatpakPermissionModel::IsDefaultEnabled).toBool());
0825                 QVERIFY(model.data(model.index(i, 0), FlatpakPermissionModel::IsEffectiveEnabled).toBool());
0826                 QCOMPARE(model.data(model.index(i, 0), FlatpakPermissionModel::DefaultValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_SEE);
0827                 QCOMPARE(model.data(model.index(i, 0), FlatpakPermissionModel::EffectiveValue).value<FlatpakPolicy>(), FlatpakPolicy::FLATPAK_POLICY_SEE);
0828             }
0829 
0830             if (name == envName) {
0831                 QVERIFY(model.data(model.index(i, 0), FlatpakPermissionModel::CanBeDisabled).toBool());
0832                 QVERIFY(!model.data(model.index(i, 0), FlatpakPermissionModel::IsDefaultEnabled).toBool());
0833                 QVERIFY(model.data(model.index(i, 0), FlatpakPermissionModel::IsEffectiveEnabled).toBool());
0834                 QCOMPARE(model.data(model.index(i, 0), FlatpakPermissionModel::DefaultValue).toString(), envValue);
0835                 QCOMPARE(model.data(model.index(i, 0), FlatpakPermissionModel::EffectiveValue).toString(), envValue);
0836             }
0837         }
0838         model.save();
0839         const KConfig actual(metadataAndOverridesFiles.last());
0840         const KConfig expected(QFINDTESTDATA("fixtures/overrides.out/com.discordapp.Discord"));
0841         QVERIFY(operatorFlatpakConfigEquals(actual, expected));
0842     }
0843 
0844     void testValuesModelForSectionsWithoutModels()
0845     {
0846         const auto values = FlatpakPermissionModel::valuesModelForSectionType(FlatpakPermissionsSectionType::Features);
0847         QCOMPARE(values, nullptr);
0848     }
0849 
0850     void testValuesModelForFilesystemsSection()
0851     {
0852         const auto values = FlatpakPermissionModel::valuesModelForFilesystemsSection();
0853         QCOMPARE(values->rowCount(QModelIndex()), 4);
0854         QVERIFY(modelContains(values, i18n("OFF"))); // Filesystems logic is not ready to use this value yet.
0855         QVERIFY(modelContains(values, i18n("read/write")));
0856         QVERIFY(modelContains(values, i18n("read-only")));
0857         QVERIFY(modelContains(values, i18n("create")));
0858     }
0859 
0860     void testValuesModelForBusSections()
0861     {
0862         const auto values = FlatpakPermissionModel::valuesModelForBusSections();
0863         QCOMPARE(values->rowCount(QModelIndex()), 4);
0864         QVERIFY(modelContains(values, i18n("None")));
0865         QVERIFY(modelContains(values, i18n("talk")));
0866         QVERIFY(modelContains(values, i18n("own")));
0867         QVERIFY(modelContains(values, i18n("see")));
0868     }
0869 
0870     void testValuesModelMapping()
0871     {
0872         auto expected = FlatpakPermissionModel::valuesModelForFilesystemsSection();
0873         auto actual = FlatpakPermissionModel::valuesModelForSectionType(FlatpakPermissionsSectionType::Filesystems);
0874         QCOMPARE(actual, expected);
0875 
0876         expected = FlatpakPermissionModel::valuesModelForBusSections();
0877         actual = FlatpakPermissionModel::valuesModelForSectionType(FlatpakPermissionsSectionType::SessionBus);
0878         QCOMPARE(actual, expected);
0879 
0880         actual = FlatpakPermissionModel::valuesModelForSectionType(FlatpakPermissionsSectionType::SystemBus);
0881         QCOMPARE(actual, expected);
0882     }
0883 
0884     void testUnparsableFilesystems()
0885     {
0886         const auto metadataAndOverridesFiles = mockMetadataAndOverridesFiles("com.example.unparsable.filesystems");
0887         FlatpakPermissionModel model;
0888         FlatpakReferencesModel referencesModel;
0889         FlatpakReference reference(&referencesModel,
0890                                    "com.example.unparsable.filesystems",
0891                                    "x86_64",
0892                                    "stable",
0893                                    "0.0.24",
0894                                    "Unparsable Filesystems",
0895                                    QUrl(),
0896                                    metadataAndOverridesFiles);
0897         model.setReference(&reference);
0898         model.load();
0899         model.setShowAdvanced(true);
0900 
0901         const auto filesystem = QLatin1String("xdg-data/path");
0902         model.addUserEnteredPermission(FlatpakPermissionsSectionType::Filesystems,
0903                                        filesystem,
0904                                        QVariant::fromValue(FlatpakFilesystemsEntry::AccessMode::ReadOnly));
0905         model.save();
0906 
0907         const KConfig actual(metadataAndOverridesFiles.last());
0908         const KConfig expected(QFINDTESTDATA("fixtures/overrides.out/com.example.unparsable.filesystems"));
0909         QVERIFY(operatorFlatpakConfigEquals(actual, expected));
0910     }
0911 
0912     void testDefaultHomeFilesystem()
0913     {
0914         FlatpakReferencesModel referencesModel;
0915         FlatpakReference reference(&referencesModel,
0916                                    "com.example.home.filesystems",
0917                                    "x86_64",
0918                                    "stable",
0919                                    "0.0.24",
0920                                    "Default Home Filesystems",
0921                                    QUrl(),
0922                                    mockMetadataAndOverridesFiles("com.example.home.filesystems"));
0923         FlatpakPermissionModel model;
0924         model.setReference(&reference);
0925         model.load();
0926 
0927         int dConfIndex = -1;
0928         int homeIndex = -1;
0929 
0930         for (int i = 0; i < model.rowCount(); i++) {
0931             const auto section = model.data(model.index(i), FlatpakPermissionModel::Section).value<FlatpakPermissionsSectionType::Type>();
0932             const auto name = model.data(model.index(i), FlatpakPermissionModel::Name).toString();
0933 
0934             if (section == FlatpakPermissionsSectionType::Filesystems) {
0935                 const auto value = model.data(model.index(i), FlatpakPermissionModel::DefaultValue).value<FlatpakFilesystemsEntry::AccessMode>();
0936 
0937                 if (name.contains(QLatin1String("dconf"))) {
0938                     dConfIndex = i;
0939                     QCOMPARE(value, FlatpakFilesystemsEntry::AccessMode::ReadWrite);
0940                 } else if (name == QLatin1String("home")) {
0941                     homeIndex = i;
0942                     QCOMPARE(value, FlatpakFilesystemsEntry::AccessMode::ReadOnly);
0943                 }
0944             }
0945         }
0946 
0947         QVERIFY(dConfIndex != -1);
0948         QVERIFY(homeIndex != -1);
0949     }
0950 
0951     void testValidNames()
0952     {
0953         FlatpakPermissionModel model;
0954         FlatpakReferencesModel referencesModel;
0955         FlatpakReference reference(&referencesModel,
0956                                    "com.example.valid.names",
0957                                    "x86_64",
0958                                    "stable",
0959                                    "1.0",
0960                                    "Valid Names",
0961                                    QUrl(),
0962                                    mockMetadataAndOverridesFiles("com.example.valid.names"));
0963         model.setReference(&reference);
0964         model.load();
0965         model.setShowAdvanced(true);
0966 
0967         QVERIFY(!model.isFilesystemNameValid(QString()));
0968         QVERIFY(model.permissionExists(FlatpakPermissionsSectionType::Filesystems, QLatin1String("~/path")));
0969         QVERIFY(model.isFilesystemNameValid(QLatin1String("~/valid/path")));
0970         QVERIFY(model.isFilesystemNameValid(QLatin1String("~/other")));
0971 
0972         QVERIFY(!model.isDBusServiceNameValid(QString()));
0973         QVERIFY(!model.isDBusServiceNameValid(QLatin1String("com")));
0974         QVERIFY(!model.isDBusServiceNameValid(QLatin1String("#$%")));
0975         QVERIFY(model.permissionExists(FlatpakPermissionsSectionType::SessionBus, QLatin1String("com.example.session")));
0976         QVERIFY(model.permissionExists(FlatpakPermissionsSectionType::SystemBus, QLatin1String("com.example.system")));
0977         QVERIFY(model.isDBusServiceNameValid(QLatin1String("com.example.session")));
0978         QVERIFY(model.isDBusServiceNameValid(QLatin1String("com.example.system")));
0979         QVERIFY(model.isDBusServiceNameValid(QLatin1String("com.example.session2")));
0980         QVERIFY(model.isDBusServiceNameValid(QLatin1String("com.example.*")));
0981         QVERIFY(!model.isDBusServiceNameValid(QLatin1String("com.example.")));
0982 
0983         QVERIFY(!model.isEnvironmentVariableNameValid(QString()));
0984         QVERIFY(!model.isEnvironmentVariableNameValid(QLatin1String("ABC=DEF")));
0985         QVERIFY(model.permissionExists(FlatpakPermissionsSectionType::Environment, QLatin1String("EXAMPLE_NAME")));
0986         QVERIFY(model.isEnvironmentVariableNameValid(QLatin1String("EXAMPLE_NAME")));
0987         QVERIFY(model.isEnvironmentVariableNameValid(QLatin1String("whatever")));
0988     }
0989 };
0990 
0991 QTEST_MAIN(FlatpakPermissionModelTest)
0992 
0993 #include "flatpakpermissiontest.moc"