File indexing completed on 2024-09-08 07:56:40
0001 /** 0002 * SPDX-FileCopyrightText: 2022 Suhaas Joshi <joshiesuhaas0@gmail.com> 0003 * SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk> 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include "flatpakcommon.h" 0010 #include "flatpakreference.h" 0011 0012 #include <KConfigGroup> 0013 #include <QAbstractItemModel> 0014 #include <QAbstractListModel> 0015 #include <QPointer> 0016 #include <QString> 0017 0018 #include <optional> 0019 #include <variant> 0020 0021 class FlatpakReference; 0022 0023 /** For exporting enum to QML */ 0024 class FlatpakPermissionsSectionType : public QObject 0025 { 0026 Q_OBJECT 0027 0028 public: 0029 enum Type { 0030 Basic, 0031 Filesystems, 0032 Advanced, 0033 SubsystemsShared, 0034 Sockets, 0035 Devices, 0036 Features, 0037 SessionBus, 0038 SystemBus, 0039 Environment, 0040 }; 0041 Q_ENUM(Type) 0042 }; 0043 0044 /** 0045 * Boolean entry of one of the `shared=`, `sockets=`, `devices=` or `features=` 0046 * lists of the `[Context]` group. 0047 * 0048 * Such entries are either ON or OFF. They are turned OFF if and only if their 0049 * string representation is prefixed with a `!` bang character. 0050 */ 0051 class FlatpakSimpleEntry 0052 { 0053 Q_GADGET 0054 public: 0055 // Default constructor is required for meta-type registration. 0056 /** Default constructor. Creates an invalid entry. */ 0057 FlatpakSimpleEntry() = default; 0058 0059 /** 0060 * Construct new entry directly from raw unvalidated data. 0061 */ 0062 explicit FlatpakSimpleEntry(const QString &name, bool enabled = true); 0063 0064 /** 0065 * Parse list entry into data structure. Might fail for various reasons, 0066 * such as ill-formed access mode, so returns an optional value. 0067 */ 0068 static std::optional<FlatpakSimpleEntry> parse(QStringView entry); 0069 0070 /** 0071 * Parse list of entries. Returns list of successfully parsed valid entries, 0072 * as well as a list of the rest unparsable strings. 0073 */ 0074 static std::pair<QStringList, QList<FlatpakSimpleEntry>> getCategory(const KConfigGroup &group, const QString &category); 0075 0076 /** 0077 * Parse list of entries. Returns only list of successfully parsed valid entries. 0078 */ 0079 static QList<FlatpakSimpleEntry> getCategorySkippingInvalidEntries(const KConfigGroup &group, const QString &category); 0080 0081 static std::optional<bool> isEnabled(const QList<FlatpakSimpleEntry> &entries, const QString &name); 0082 0083 /** 0084 * Format this entry into string. Opposite of parse(). 0085 */ 0086 QString format() const; 0087 0088 bool isEnabled() const; 0089 void setEnabled(bool enabled); 0090 0091 const QString &name() const; 0092 0093 // TODO C++20: use `= default` implementation. 0094 bool operator==(const FlatpakSimpleEntry &other) const; 0095 bool operator!=(const FlatpakSimpleEntry &other) const; 0096 0097 private: 0098 QString m_name; 0099 bool m_enabled = true; 0100 }; 0101 0102 /** 0103 * Entry of the `filesystems=` list. 0104 * 0105 * "Deny" access mode represents an entry prefixes with a `!` bang character. 0106 * Other modes correspond to `:ro`, `:rw` and `:create` suffixes. 0107 * 0108 * Path should be without trailing `/` slash character. 0109 * See flatpak-metadata(5) for possible values. 0110 */ 0111 class FlatpakFilesystemsEntry 0112 { 0113 Q_GADGET 0114 public: 0115 enum class AccessMode { 0116 /** Make the given directory available read-only. */ 0117 ReadOnly, 0118 /** Make the given directory available read/write. This is the default. */ 0119 ReadWrite, 0120 /** Make the given directory available read/write, and create it if it does not already exist. */ 0121 Create, 0122 /** Don't expose filesystem to app. */ 0123 Deny, 0124 }; 0125 Q_ENUM(AccessMode) 0126 0127 enum class PathMode { 0128 Required, 0129 Optional, 0130 NoPath, 0131 }; 0132 0133 enum class FilesystemPrefix { 0134 /** Path is required. */ 0135 Absolute, 0136 /** Path is optional. */ 0137 Home, 0138 /** Path is invalid. */ 0139 Host, 0140 HostOs, 0141 HostEtc, 0142 /** Path is optional. */ 0143 XdgDesktop, 0144 XdgDocuments, 0145 XdgDownload, 0146 XdgMusic, 0147 XdgPictures, 0148 XdgPublicShare, 0149 XdgVideos, 0150 XdgTemplates, 0151 /** Path is optional. */ 0152 XdgCache, 0153 XdgConfig, 0154 XdgData, 0155 /** Path is required. */ 0156 XdgRun, 0157 // Future-proof fallback variant. Prefix is empty, so path mode is 0158 // required, which also ensures that is will be non-empty. 0159 Unknown, 0160 }; 0161 0162 // For Required path mode, fixed string is empty because it is meaningless. 0163 // For Invalid path mode, second, prefix string is empty. 0164 struct TableEntry { 0165 FilesystemPrefix prefix; 0166 PathMode mode; 0167 // Fixed magic filesystem name 0168 QLatin1String fixedString; 0169 // Same as fixed, but with '/' appended. 0170 QLatin1String prefixString; 0171 }; 0172 0173 // Default constructor is required for meta-type registration. 0174 /** Default constructor. Creates an invalid entry. */ 0175 FlatpakFilesystemsEntry() = default; 0176 0177 /** 0178 * Construct new entry directly from raw unvalidated data. 0179 */ 0180 explicit FlatpakFilesystemsEntry(FilesystemPrefix prefix, AccessMode mode, const QString &path = QString()); 0181 0182 /** 0183 * Parse list entry into data structure. Might fail for various reasons, 0184 * such as ill-formed access mode, so returns an optional value. 0185 */ 0186 static std::optional<FlatpakFilesystemsEntry> parse(QStringView entry); 0187 0188 /** 0189 * Parse name of a list entry into data structure. Access mode is passed 0190 * separately and not expected to be a part of the name. Might fail for 0191 * various reasons, so returns an optional value. 0192 * 0193 * See also: name() 0194 */ 0195 static std::optional<FlatpakFilesystemsEntry> parse(QStringView name, AccessMode accessMode); 0196 0197 /** 0198 * Formatted prefix and path only, without access mode prefixes or suffixes. Suitable for comparison. 0199 * 0200 * See also: parse(QStringView, AccessMode) 0201 */ 0202 QString name() const; 0203 0204 /** 0205 * Format this entry into string. Opposite of parse(). Omits default `:rw` suffix. 0206 */ 0207 QString format() const; 0208 0209 FilesystemPrefix prefix() const; 0210 QString path() const; 0211 AccessMode mode() const; 0212 0213 // TODO C++20: use `= default` implementation. 0214 bool operator==(const FlatpakFilesystemsEntry &other) const; 0215 bool operator!=(const FlatpakFilesystemsEntry &other) const; 0216 0217 private: 0218 FilesystemPrefix m_prefix = FilesystemPrefix::Absolute; 0219 AccessMode m_mode = AccessMode::ReadWrite; 0220 // Depending on prefix type, path can be optional, required or illegal. 0221 QString m_path; 0222 }; 0223 0224 class PolicyChoicesModel : public QAbstractListModel 0225 { 0226 Q_OBJECT 0227 public: 0228 enum Roles { 0229 // Uses standard Qt::DisplayRole for text 0230 ValueRole = Qt::UserRole + 1, 0231 }; 0232 0233 QHash<int, QByteArray> roleNames() const override; 0234 int rowCount(const QModelIndex &parent) const override; 0235 QVariant data(const QModelIndex &index, int role) const override; 0236 0237 protected: 0238 struct Entry { 0239 int value; 0240 QString display; 0241 }; 0242 0243 explicit PolicyChoicesModel(QList<Entry> &&policies, QObject *parent = nullptr); 0244 0245 private: 0246 const QList<Entry> m_policies; 0247 }; 0248 0249 class FilesystemChoicesModel : public PolicyChoicesModel 0250 { 0251 Q_OBJECT 0252 public: 0253 explicit FilesystemChoicesModel(QObject *parent = nullptr); 0254 }; 0255 0256 class DBusPolicyChoicesModel : public PolicyChoicesModel 0257 { 0258 Q_OBJECT 0259 public: 0260 explicit DBusPolicyChoicesModel(QObject *parent = nullptr); 0261 }; 0262 0263 /** 0264 * @class FlatpakPermission describes a single configurable entry in the list model of permissions. 0265 * 0266 * The content of instances of this class can be interpretted in different ways depending on their 0267 * value type(), permission pType() and section sType(). 0268 * 0269 * See flatpak-metadata(5) for more. 0270 */ 0271 class FlatpakPermission 0272 { 0273 public: 0274 enum class ValueType { 0275 /** 0276 * This type is for permission entries representing simple boolean 0277 * toggles. 0278 * 0279 * Name of such entry is one of the predefined resource names, e.g.: 0280 * "bluetooth" from "features" category, "kvm" from "devices" category 0281 * etc, "pulseaudio" from "sockets" category etc. 0282 */ 0283 Simple, 0284 /** 0285 * Filesystem permissions fall into the "Basic" section type, i.e. 0286 * always shown. 0287 * 0288 * Name of such entry is an actual filesystem path, and the value is 0289 * one of the suffixes: ":ro", ":rw" (default), ":create". 0290 */ 0291 Filesystems, 0292 /** 0293 * Name of such permission entry is a D-Bus bus name or prefix thereof, 0294 * for example org.gnome.SessionManager or org.freedesktop.portal.* 0295 * 0296 * The possible values for entry are: "none", "see", "talk" or "own". 0297 */ 0298 Bus, 0299 /** 0300 * Name and value of such permission entry are name and value of an 0301 * environment variable. 0302 */ 0303 Environment 0304 }; 0305 0306 static ValueType valueTypeFromSectionType(FlatpakPermissionsSectionType::Type section); 0307 0308 enum class OriginType { 0309 /** 0310 * Built-in type is for all pre-defined system resources (permissions) 0311 * as found in flatpak-metadata(5) man page, and any other additional 0312 * resources declared in app metadata. 0313 * 0314 * They shall not be removed from the list of permissions when 0315 * unchecked. 0316 * 0317 * Predefined resources come with translated description. 0318 */ 0319 // TODO: Instead of unchecking there should be more obvious UI. For Bus 0320 // type, there's a "none" policy. For environment we should implement 0321 // "unset-environment" category. 0322 BuiltIn, 0323 /** 0324 * User-defined permissions are resources that user has manually added 0325 * in their overrides. In other words, they are not present in app 0326 * metadata manifest, and can be removed completely when unchecked. 0327 */ 0328 // TODO: Same as in BuiltIn, consider "Remove" button instead of unchecking. 0329 UserDefined, 0330 /** 0331 * Empty permissions, just for showing section headers for categories 0332 * that don't have permissions. 0333 */ 0334 Dummy 0335 }; 0336 0337 using Variant = std::variant<QString, FlatpakPolicy, FlatpakFilesystemsEntry::AccessMode>; 0338 0339 // Default constructor is required for meta-type registration. 0340 /** Default constructor. Creates an invalid entry. */ 0341 FlatpakPermission() = default; 0342 0343 /** 0344 * Create a Dummy entry for the Advanced and user-editable sections, just so 0345 * that ListView shows a section header even if there are no permission row 0346 * entries in it. 0347 */ 0348 explicit FlatpakPermission(FlatpakPermissionsSectionType::Type section); 0349 0350 explicit FlatpakPermission(FlatpakPermissionsSectionType::Type section, 0351 const QString &name, 0352 const QString &category, 0353 const QString &description, 0354 bool isDefaultEnabled, 0355 const Variant &defaultValue = QString()); 0356 0357 /** Section type for QtQuick/ListView. */ 0358 FlatpakPermissionsSectionType::Type section() const; 0359 0360 /** 0361 * Technical untranslated name of the resource managed by this permission entry. 0362 * 0363 * See ValueType enum for more. 0364 */ 0365 const QString &name() const; 0366 0367 /** 0368 * Technical untranslated category name of the resource managed by this permission entry. 0369 * 0370 * See ValueType enum for more. 0371 */ 0372 const QString &category() const; 0373 0374 /** 0375 * Untranslate section heading back into category identifier. It's a hack 0376 * until the model is refactored to only operate on identifiers, and all 0377 * i18n stuff is moved elsewhere. 0378 */ 0379 static QString categoryHeadingToRawCategory(const QString §ion); 0380 0381 /** 0382 * User-facing translated description of the resource managed by this permission entry. 0383 * 0384 * See ValueType enum for more. 0385 */ 0386 const QString &description() const; 0387 0388 /** 0389 * Return type of value of this entry, inferred from its SectionType. 0390 */ 0391 ValueType valueType() const; 0392 0393 /** 0394 * Type of permission this entry represents. 0395 */ 0396 OriginType originType() const; 0397 0398 /** 0399 * Set which type of permissions this entry represents. 0400 */ 0401 // TODO: This method should be replaced with constructor argument. 0402 void setOriginType(OriginType type); 0403 0404 /** 0405 * System default "enabled" status of this permission. It can not be modified. 0406 */ 0407 bool isDefaultEnabled() const; 0408 0409 /** Set user override */ 0410 void setOverrideEnabled(bool enabled); 0411 0412 /** 0413 * This property reports the current effective "enabled" status of this 0414 * permission in KCM. 0415 * 0416 * For ValueType::Simple permissions, if current enabled status matches 0417 * system defaults, it will be removed from user overrides. 0418 * 0419 * For user-defined permissions of other ValueType types, disabling them 0420 * would mark them for removal but only if they are disabled (not present) 0421 * in defaults. 0422 */ 0423 bool canBeDisabled() const; 0424 bool isEffectiveEnabled() const; 0425 void setEffectiveEnabled(bool enabled); 0426 0427 /** 0428 * System default value for this permission. It can only be modified for 0429 * entries which are disabled (not present) by default. 0430 * 0431 * Applicable for any permissions other than ValueType::Simple. 0432 */ 0433 const Variant defaultValue() const; 0434 0435 void setDefaultValue(const Variant &value); 0436 0437 /** Set user override. Affects isSaveNeeded() state. */ 0438 void setOverrideValue(const Variant &value); 0439 0440 /** 0441 * This property holds the current effective value of this permission in 0442 * KCM. 0443 * 0444 * See ValueType enum for more. 0445 */ 0446 const Variant effectiveValue() const; 0447 void setEffectiveValue(const Variant &value); 0448 0449 /** Integration with KCM. */ 0450 bool isSaveNeeded() const; 0451 bool isDefaults() const; 0452 0453 private: 0454 /** Section type for QtQuick/ListView. */ 0455 FlatpakPermissionsSectionType::Type m_section; 0456 0457 /** 0458 * Untranslatable identifier of permission. 0459 * 0460 * For ValueType::Simple permissions, it's the name of the entry in the list of togglable options in that category. 0461 * For ValueType::Filesystems it's either one of the pre-defined symbolic names or the absolute filepath. 0462 * For ValueType::Bus permissions it's the the name or glob pattern of D-Bus service(s). 0463 * For ValueType::Environment permissions it's the name of environment variable. 0464 */ 0465 QString m_name; 0466 /** Untranslatable name of [Category] as seen in metadata and override ini-style files. */ 0467 QString m_category; 0468 /** Human-readable description of the permission, or whatever to be displayed in UI. */ 0469 QString m_description; 0470 0471 /* Attempts to classify permissions into various types and groups. */ 0472 0473 OriginType m_originType; 0474 0475 /* Applicable for all ValueType permissions. */ 0476 0477 /** System defaults */ 0478 bool m_defaultEnable; 0479 /** User overrides */ 0480 bool m_overrideEnable; 0481 /** Current value in KCM */ 0482 bool m_effectiveEnable; 0483 0484 /* Applicable for any permissions other than ValueType::Simple. */ 0485 0486 /** System defaults */ 0487 Variant m_defaultValue; 0488 /** User overrides */ 0489 Variant m_overrideValue; 0490 /** Current value in KCM */ 0491 Variant m_effectiveValue; 0492 }; 0493 0494 namespace FlatpakOverrides 0495 { 0496 0497 using KConfigPtr = std::unique_ptr<KConfig>; 0498 0499 KConfigPtr loadAndMerge(const QStringList &filenames); 0500 void merge(KConfig &target, const QString &filename); 0501 void merge(KConfig &target, const KConfig &source); 0502 }; 0503 0504 class FlatpakPermissionModel : public QAbstractListModel 0505 { 0506 friend class FlatpakPermissionModelTest; 0507 Q_OBJECT 0508 Q_PROPERTY(FlatpakReference *reference READ reference WRITE setReference NOTIFY referenceChanged) 0509 Q_PROPERTY(bool showAdvanced READ showAdvanced WRITE setShowAdvanced NOTIFY showAdvancedChanged) 0510 public: 0511 FlatpakPermissionModel(QObject *parent = nullptr); 0512 0513 enum Roles { 0514 Section = Qt::UserRole + 1, 0515 Name, 0516 Description, 0517 // 0518 IsNotDummy, 0519 // 0520 CanBeDisabled, 0521 IsDefaultEnabled, 0522 IsEffectiveEnabled, 0523 DefaultValue, 0524 EffectiveValue, 0525 // 0526 ValuesModel, 0527 }; 0528 Q_ENUM(Roles) 0529 0530 int rowCount(const QModelIndex &parent = QModelIndex()) const override; 0531 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 0532 QHash<int, QByteArray> roleNames() const override; 0533 0534 void loadDefaultValues(); 0535 void loadCurrentValues(); 0536 0537 FlatpakReference *reference() const; 0538 void setReference(FlatpakReference *ref); 0539 0540 bool showAdvanced() const; 0541 void setShowAdvanced(bool); 0542 /** Helper function to count actual rows regardless of current "showAdvanced" setting. */ 0543 int rowCount(bool showAdvanced) const; 0544 0545 void load(); 0546 void save(); 0547 void defaults(); 0548 bool isDefaults() const; 0549 bool isSaveNeeded() const; 0550 0551 /** 0552 * Static list of user-facing translated policy names for this kind of 0553 * permission. 0554 * 0555 * Applicable for ValueType::Filesystems and ValueType::Bus only. 0556 */ 0557 Q_INVOKABLE static PolicyChoicesModel *valuesModelForSectionType(int /*FlatpakPermissionsSectionType::Type*/ rawSection); 0558 Q_INVOKABLE static PolicyChoicesModel *valuesModelForFilesystemsSection(); 0559 Q_INVOKABLE static PolicyChoicesModel *valuesModelForBusSections(); 0560 0561 Q_INVOKABLE static QString sectionHeaderForSectionType(int /*FlatpakPermissionsSectionType::Type*/ rawSection); 0562 Q_INVOKABLE static QString sectionAddButtonToolTipTextForSectionType(int /*FlatpakPermissionsSectionType::Type*/ rawSection); 0563 0564 Q_INVOKABLE bool permissionExists(int /*FlatpakPermissionsSectionType::Type*/ rawSection, const QString &name) const; 0565 bool permissionExists(FlatpakPermissionsSectionType::Type section, const QString &name) const; 0566 0567 std::optional<int> findPermissionRow(FlatpakPermissionsSectionType::Type section, const QString &name) const; 0568 QModelIndex findPermissionIndex(FlatpakPermissionsSectionType::Type section, const QString &name) const; 0569 0570 /** 0571 * Validators to check that names comply with section-specific rules. 0572 */ 0573 Q_INVOKABLE static bool isFilesystemNameValid(const QString &name); 0574 Q_INVOKABLE static bool isDBusServiceNameValid(const QString &name); 0575 Q_INVOKABLE static bool isEnvironmentVariableNameValid(const QString &name); 0576 0577 public Q_SLOTS: 0578 void togglePermissionAtRow(int row); 0579 void setPermissionValueAtRow(int row, const QVariant &newValue); 0580 void addUserEnteredPermission(int /*FlatpakPermissionsSectionType::Type*/ rawSection, const QString &name, const QVariant &value); 0581 0582 Q_SIGNALS: 0583 void referenceChanged(); 0584 void showAdvancedChanged(); 0585 0586 private: 0587 int findIndexToInsertRowAndRemoveDummyRowIfNeeded(FlatpakPermissionsSectionType::Type section, bool emitModelSignals); 0588 0589 void writeToFile() const; 0590 void writeToKConfig(KConfig &config) const; 0591 0592 QList<FlatpakPermission> m_permissions; 0593 QHash<QString, QStringList> m_unparsableEntriesByCategory; 0594 QPointer<FlatpakReference> m_reference; 0595 bool m_showAdvanced; 0596 0597 friend class FlatpakPermissionModelTest; 0598 };