diff --git a/setoolsgui/widgets/details/__init__.py b/setoolsgui/widgets/details/__init__.py index 93d4cf0..0e9308e 100644 --- a/setoolsgui/widgets/details/__init__.py +++ b/setoolsgui/widgets/details/__init__.py @@ -3,6 +3,6 @@ from .boolean import boolean_detail, boolean_detail_action, boolean_tooltip from .objclass import objclass_detail, objclass_detail_action, objclass_tooltip from .role import role_detail, role_detail_action, role_tooltip -from .typeattr import typeattr_detail, typeattr_tooltip +from .typeattr import (typeattr_detail, typeattr_detail_action, typeattr_tooltip) from .type import (type_detail, type_or_attr_detail, type_detail_action, type_or_attr_detail_action, type_tooltip, type_or_attr_tooltip) diff --git a/setoolsgui/widgets/details/role.py b/setoolsgui/widgets/details/role.py index c9f3630..63bc575 100644 --- a/setoolsgui/widgets/details/role.py +++ b/setoolsgui/widgets/details/role.py @@ -49,4 +49,4 @@ def role_tooltip(role: "Role") -> str: return f"{role} is a role associated with {n_types} types." else: return f"{role} is a role associated with types: " \ - f"{', '.join(t.name for t in role.expand())}" + f"{', '.join(t.name for t in role.types())}" diff --git a/setoolsgui/widgets/details/typeattr.py b/setoolsgui/widgets/details/typeattr.py index 20882ff..821f553 100644 --- a/setoolsgui/widgets/details/typeattr.py +++ b/setoolsgui/widgets/details/typeattr.py @@ -30,6 +30,16 @@ def typeattr_detail(attr: "TypeAttribute", parent: "Optional[QtWidgets.QWidget]" parent) +def typeattr_detail_action(attr: "TypeAttribute", + parent: QtWidgets.QWidget | None = None) -> QtWidgets.QAction: + + """Return a QAction that, when triggered, opens an detail popup for the attr.""" + + a = QtWidgets.QAction(f"Properties of {attr}") + a.triggered.connect(lambda _: typeattr_detail(attr, parent)) + return a + + def typeattr_tooltip(attr: "TypeAttribute") -> str: """Return tooltip text for this type attribute.""" n_types = len(attr) diff --git a/setoolsgui/widgets/mlsrulequery.py b/setoolsgui/widgets/mlsrulequery.py index 0617309..d503124 100644 --- a/setoolsgui/widgets/mlsrulequery.py +++ b/setoolsgui/widgets/mlsrulequery.py @@ -5,8 +5,7 @@ from typing import TYPE_CHECKING from PyQt5 import QtCore, QtGui, QtWidgets import setools -from . import criteria, tab -from .models.mlsrule import MLSRuleTableModel +from . import criteria, models, tab if TYPE_CHECKING: from typing import Optional @@ -130,7 +129,7 @@ class MLSRuleQueryTab(tab.TableResultTabWidget): self.criteria = (rt, src, dst, tclass, dflt) # Set result table's model - self.table_results_model = MLSRuleTableModel(self.table_results) + self.table_results_model = models.MLSRuleTable(self.table_results) if __name__ == '__main__': diff --git a/setoolsgui/widgets/models/__init__.py b/setoolsgui/widgets/models/__init__.py new file mode 100644 index 0000000..4118598 --- /dev/null +++ b/setoolsgui/widgets/models/__init__.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: LGPL-2.1-only + +from .boolean import BooleanList +from .bounds import BoundsTable +from .common import CommonTable +from .constraint import ConstraintTable +from .default import DefaultTable +from .fsuse import FSUseTable +from .genfscon import GenfsconTable +from .ibendportcon import IbendportconTable +from .ibpkeycon import IbpkeyconTable +from .initsid import InitialSIDTable +from .mls import MLSComponentTable +from .mlsrule import MLSRuleTable +from .netifcon import NetifconTable +from .nodecon import NodeconTable +from .objclass import ObjClassTable +from .portcon import PortconTable +from .rbacrule import RBACRuleTable +from .role import RoleTable +from .terule import TERuleTable +from .type import TypeTable +from .typeattr import TypeAttributeTable +from .user import UserTable diff --git a/setoolsgui/widgets/models/boolean.py b/setoolsgui/widgets/models/boolean.py index 5c93fbb..26271e9 100644 --- a/setoolsgui/widgets/models/boolean.py +++ b/setoolsgui/widgets/models/boolean.py @@ -3,20 +3,16 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from typing import TYPE_CHECKING - -from PyQt5 import QtCore, QtWidgets +from PyQt5 import QtCore +import setools from . import modelroles from .list import SEToolsListModel from .table import SEToolsTableModel from .. import details -if TYPE_CHECKING: - from setools import Boolean - -class BooleanList(SEToolsListModel["Boolean"]): +class BooleanList(SEToolsListModel[setools.Boolean]): """List-based model for Booleans.""" @@ -27,32 +23,35 @@ class BooleanList(SEToolsListModel["Boolean"]): row = index.row() item = self.item_list[row] - if role == modelroles.ContextMenuRole: - return (details.boolean_detail_action(item), ) - elif role == QtCore.Qt.ItemDataRole.ToolTipRole: - return details.boolean_tooltip(item) + match role: + case modelroles.ContextMenuRole: + return (details.boolean_detail_action(item), ) + case QtCore.Qt.ItemDataRole.ToolTipRole: + return details.boolean_tooltip(item) return super().data(index, role) -class BooleanTableModel(SEToolsTableModel): +class BooleanTable(SEToolsTableModel): """Table-based model for booleans.""" headers = ["Name", "Default State"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - boolean = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == QtCore.Qt.ItemDataRole.DisplayRole: - if col == 0: - return boolean.name - elif col == 1: - return str(boolean.state) + row = index.row() + col = index.column() + boolean = self.item_list[row] - elif role == QtCore.Qt.ItemDataRole.UserRole: - # get the whole rule for boolean boolean - return boolean + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return boolean.name + case 1: + return str(boolean.state) + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/bounds.py b/setoolsgui/widgets/models/bounds.py index 35df51c..428b912 100644 --- a/setoolsgui/widgets/models/bounds.py +++ b/setoolsgui/widgets/models/bounds.py @@ -3,30 +3,34 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt +from PyQt5 import QtCore +import setools from .table import SEToolsTableModel -class BoundsTableModel(SEToolsTableModel): +class BoundsTable(SEToolsTableModel[setools.Bounds]): """Table-based model for *bounds.""" headers = ["Rule Type", "Parent", "Child"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - item = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return item.ruletype.name - elif col == 1: - return item.parent.name - elif col == 2: - return item.child.name + row = index.row() + col = index.column() + item = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return item + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return item.ruletype.name + case 1: + return item.parent.name + case 2: + return item.child.name + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/common.py b/setoolsgui/widgets/models/common.py index c9b7e32..06ab811 100644 --- a/setoolsgui/widgets/models/common.py +++ b/setoolsgui/widgets/models/common.py @@ -3,50 +3,32 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt -from PyQt5.QtGui import QPalette, QTextCursor +from PyQt5 import QtCore +import setools -from setools.exception import NoCommon - -from .details import DetailsPopup from .table import SEToolsTableModel -def common_detail(parent, common): - """ - Create a dialog box for common perm set details. - - Parameters: - parent The parent Qt Widget - class_ The type - """ - - detail = DetailsPopup(parent, "Common detail: {0}".format(common)) - - detail.append_header("Permissions ({0}):".format(len(common.perms))) - for p in sorted(common.perms): - detail.append(" {0}".format(p)) - - detail.show() - - -class CommonTableModel(SEToolsTableModel): +class CommonTable(SEToolsTableModel[setools.Common]): """Table-based model for common permission sets.""" headers = ["Name", "Permissions"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - item = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return item.name - elif col == 1: - return ", ".join(sorted(item.perms)) + row = index.row() + col = index.column() + item = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return item + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return item.name + case 1: + return ", ".join(sorted(item.perms)) + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/constraint.py b/setoolsgui/widgets/models/constraint.py index 037fc79..f8b7e99 100644 --- a/setoolsgui/widgets/models/constraint.py +++ b/setoolsgui/widgets/models/constraint.py @@ -3,36 +3,40 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt -from setools.exception import ConstraintUseError +from PyQt5 import QtCore +import setools from .table import SEToolsTableModel -class ConstraintTableModel(SEToolsTableModel): +class ConstraintTable(SEToolsTableModel[setools.Constraint]): """A table-based model for constraints.""" headers = ["Rule Type", "Class", "Permissions", "Expression"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - rule = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return rule.ruletype.name - elif col == 1: - return rule.tclass.name - elif col == 2: - try: - return ", ".join(sorted(rule.perms)) - except ConstraintUseError: - return None - elif col == 3: - return str(rule.expression) + row = index.row() + col = index.column() + rule = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return rule + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return rule.ruletype.name + case 1: + return rule.tclass.name + case 2: + if rule.ruletype in (setools.ConstraintRuletype.constrain, + setools.ConstraintRuletype.mlsconstrain): + return ", ".join(sorted(rule.perms)) + else: + return None + case 3: + return str(rule.expression) + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/default.py b/setoolsgui/widgets/models/default.py index ddbadcd..180749f 100644 --- a/setoolsgui/widgets/models/default.py +++ b/setoolsgui/widgets/models/default.py @@ -5,33 +5,38 @@ # from contextlib import suppress -from PyQt5.QtCore import Qt +from PyQt5 import QtCore +import setools from .table import SEToolsTableModel -class DefaultTableModel(SEToolsTableModel): +class DefaultTable(SEToolsTableModel[setools.Default]): """Table-based model for default_*.""" headers = ["Rule Type", "Class", "Default", "Default Range"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - item = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return item.ruletype.name - elif col == 1: - return item.tclass.name - elif col == 2: - return item.default.name - elif col == 3: - with suppress(AttributeError): - return item.default_range.name + row = index.row() + col = index.column() + item = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return item + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return item.ruletype.name + case 1: + return item.tclass.name + case 2: + return item.default.name + case 3: + with suppress(AttributeError): + return item.default_range.name # type: ignore + return None + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/fsuse.py b/setoolsgui/widgets/models/fsuse.py index b5dbcc7..3e37a0b 100644 --- a/setoolsgui/widgets/models/fsuse.py +++ b/setoolsgui/widgets/models/fsuse.py @@ -3,30 +3,34 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt +from PyQt5 import QtCore +import setools from .table import SEToolsTableModel -class FSUseTableModel(SEToolsTableModel): +class FSUseTable(SEToolsTableModel[setools.FSUse]): """Table-based model for fs_use_*.""" headers = ["Ruletype", "FS Type", "Context"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - rule = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return rule.ruletype.name - elif col == 1: - return rule.fs - elif col == 2: - return str(rule.context) + row = index.row() + col = index.column() + rule = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return rule + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return rule.ruletype.name + case 1: + return rule.fs + case 2: + return str(rule.context) + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/genfscon.py b/setoolsgui/widgets/models/genfscon.py index 7c9120d..ed3a2d9 100644 --- a/setoolsgui/widgets/models/genfscon.py +++ b/setoolsgui/widgets/models/genfscon.py @@ -5,12 +5,13 @@ # import stat -from PyQt5.QtCore import Qt +from PyQt5 import QtCore +import setools from .table import SEToolsTableModel -class GenfsconTableModel(SEToolsTableModel): +class GenfsconTable(SEToolsTableModel[setools.Genfscon]): """Table-based model for genfscons.""" @@ -26,21 +27,24 @@ class GenfsconTableModel(SEToolsTableModel): stat.S_IFLNK: "Symbolic Link", stat.S_IFSOCK: "Socket"} - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - rule = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return rule.fs - elif col == 1: - return rule.path - elif col == 2: - return self._filetype_to_text[rule.filetype] - elif col == 3: - return str(rule.context) + row = index.row() + col = index.column() + rule = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return rule + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return rule.fs + case 1: + return rule.path + case 2: + return self._filetype_to_text[rule.filetype] + case 3: + return str(rule.context) + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/ibendportcon.py b/setoolsgui/widgets/models/ibendportcon.py index c7f6527..be88b58 100644 --- a/setoolsgui/widgets/models/ibendportcon.py +++ b/setoolsgui/widgets/models/ibendportcon.py @@ -3,30 +3,34 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt +from PyQt5 import QtCore +import setools from .table import SEToolsTableModel -class IbendportconTableModel(SEToolsTableModel): +class IbendportconTable(SEToolsTableModel[setools.Ibendportcon]): """Table-based model for ibendportcons.""" headers = ["Device", "Endport", "Context"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - rule = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return rule.name - elif col == 1: - return str(rule.port) - elif col == 2: - return str(rule.context) + row = index.row() + col = index.column() + rule = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return rule + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return rule.name + case 1: + return str(rule.port) + case 2: + return str(rule.context) + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/ibpkeycon.py b/setoolsgui/widgets/models/ibpkeycon.py index f93ff0e..0954cb7 100644 --- a/setoolsgui/widgets/models/ibpkeycon.py +++ b/setoolsgui/widgets/models/ibpkeycon.py @@ -3,34 +3,37 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt +from PyQt5 import QtCore +import setools from .table import SEToolsTableModel -class IbpkeyconTableModel(SEToolsTableModel): +class IbpkeyconTable(SEToolsTableModel[setools.Ibpkeycon]): """Table-based model for ibpkeycons.""" headers = ["Subnet Prefix", "Partition Keys", "Context"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - rule = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return str(rule.subnet_prefix) - elif col == 1: - low, high = rule.pkeys - if low == high: - return "{0:#x}".format(low) - else: - return "{0:#x}-{1:#x}".format(low, high) - elif col == 2: - return str(rule.context) + row = index.row() + col = index.column() + rule = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return rule + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return str(rule.subnet_prefix) + case 1: + low, high = rule.pkeys + if low == high: + return f"{low:#x}" + return f"{low:#x}-{high:#x}" + case 2: + return str(rule.context) + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/initsid.py b/setoolsgui/widgets/models/initsid.py index e0fc7c3..1b8b9dd 100644 --- a/setoolsgui/widgets/models/initsid.py +++ b/setoolsgui/widgets/models/initsid.py @@ -3,28 +3,32 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt +from PyQt5 import QtCore +import setools from .table import SEToolsTableModel -class InitialSIDTableModel(SEToolsTableModel): +class InitialSIDTable(SEToolsTableModel[setools.InitialSID]): """Table-based model for initial SIDs.""" headers = ["SID", "Context"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - rule = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return rule.name - elif col == 1: - return str(rule.context) + row = index.row() + col = index.column() + rule = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return rule + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return rule.name + case 1: + return str(rule.context) + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/list.py b/setoolsgui/widgets/models/list.py index 8c71a30..e47c9e0 100644 --- a/setoolsgui/widgets/models/list.py +++ b/setoolsgui/widgets/models/list.py @@ -14,6 +14,7 @@ from .typing import MetaclassFix T = typing.TypeVar("T") +# pylint: disable=invalid-metaclass class SEToolsListModel(QtCore.QAbstractListModel, typing.Generic[T], metaclass=MetaclassFix): """ diff --git a/setoolsgui/widgets/models/mls.py b/setoolsgui/widgets/models/mls.py index c3e2867..2ea2a4e 100644 --- a/setoolsgui/widgets/models/mls.py +++ b/setoolsgui/widgets/models/mls.py @@ -3,71 +3,31 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt -from PyQt5.QtGui import QPalette, QTextCursor +from PyQt5 import QtCore -from .details import DetailsPopup from .table import SEToolsTableModel -def _mls_detail(parent, obj, objtype): - """ - Create a dialog box for category or sensitivity details. - - Parameters: - parent The parent Qt Widget - type_ The type - """ - - detail = DetailsPopup(parent, "{0} detail: {1}".format(objtype, obj)) - - aliases = sorted(obj.aliases()) - detail.append_header("Aliases ({0}):".format(len(aliases))) - for a in aliases: - detail.append(" {0}".format(a)) - - detail.show() - - -def category_detail(parent, obj): - """ - Create a dialog box for category details. - - Parameters: - parent The parent Qt Widget - type_ The type - """ - _mls_detail(parent, obj, "Category") - - -def sensitivity_detail(parent, obj): - """ - Create a dialog box for sensitivity details. - - Parameters: - parent The parent Qt Widget - type_ The type - """ - _mls_detail(parent, obj, "Sensitivity") - - -class MLSComponentTableModel(SEToolsTableModel): +class MLSComponentTable(SEToolsTableModel): """Table-based model for sensitivities and categories.""" headers = ["Name", "Aliases"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - item = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return item.name - elif col == 1: - return ", ".join(sorted(a for a in item.aliases())) + row = index.row() + col = index.column() + item = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return item + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return item.name + case 1: + return ", ".join(sorted(a for a in item.aliases())) + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/mlsrule.py b/setoolsgui/widgets/models/mlsrule.py index f577b20..fcd1187 100644 --- a/setoolsgui/widgets/models/mlsrule.py +++ b/setoolsgui/widgets/models/mlsrule.py @@ -3,15 +3,15 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5 import QtCore, QtWidgets -from setools import MLSRuletype +from PyQt5 import QtCore +import setools from . import modelroles from .table import SEToolsTableModel from .. import details -class MLSRuleTableModel(SEToolsTableModel): +class MLSRuleTable(SEToolsTableModel[setools.MLSRule]): """A table-based model for MLS rules.""" @@ -25,66 +25,75 @@ class MLSRuleTableModel(SEToolsTableModel): col = index.column() rule = self.item_list[row] - if role == QtCore.Qt.ItemDataRole.DisplayRole: - if col == 0: - return rule.ruletype.name - elif col == 1: - return rule.source.name - elif col == 2: - return rule.target.name - elif col == 3: - return rule.tclass.name - elif col == 4: - return str(rule.default) + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return rule.ruletype.name + case 1: + return rule.source.name + case 2: + return rule.target.name + case 3: + return rule.tclass.name + case 4: + return str(rule.default) - elif role == modelroles.ContextMenuRole: - if col == 1: - return (details.type_or_attr_detail_action(rule.source), ) - elif col == 2: - return (details.type_or_attr_detail_action(rule.target), ) - elif col == 3: - return (details.objclass_detail_action(rule.tclass), ) + case modelroles.ContextMenuRole: + match col: + case 1: + return (details.type_or_attr_detail_action(rule.source), ) + case 2: + return (details.type_or_attr_detail_action(rule.target), ) + case 3: + return (details.objclass_detail_action(rule.tclass), ) - return () + case QtCore.Qt.ItemDataRole.ToolTipRole: + match col: + case 1: + return details.type_or_attr_tooltip(rule.source) + case 2: + return details.type_or_attr_tooltip(rule.target) + case 3: + return details.objclass_tooltip(rule.tclass) - elif role == QtCore.Qt.ItemDataRole.ToolTipRole: - if col in (1, 2): - if col == 1: - return details.type_or_attr_tooltip(rule.source) - else: - return details.type_or_attr_tooltip(rule.target) - elif col == 3: - return details.objclass_tooltip(rule.tclass) + case QtCore.Qt.ItemDataRole.WhatsThisRole: + match col: + case 0: + column_whatsthis = \ + f""" +

The Rule Type column is the type of the rule; it is one of:

+ + """ + case 1: + column_whatsthis = \ + """ +

This is the source type or type attribute (subject) in the rule.

+ """ + case 2: + column_whatsthis = \ + """ +

This is the target type or type attribute (object) in the rule.

+ """ + case 3: + column_whatsthis = "

This is the object class of the rule.

" + case 4: + column_whatsthis = \ + """ +

Default Range: This the the default range specified in the rule.

+ """ + case _: + column_whatsthis = "" - return None - - elif role == QtCore.Qt.ItemDataRole.WhatsThisRole: - if col == 0: - column_whatsthis = \ + return \ f""" -

The Rule Type column is the type of the rule; it is one of:

- +

Table Representation of Multi-Level Security Rules

+ +

Each part of the rule is represented as a column in the table.

+ + {column_whatsthis} """ - elif col == 1: - column_whatsthis = \ - "

This is the source type or type attribute (subject) in the rule.

" - elif col == 2: - column_whatsthis = \ - "

This is the target type or type attribute (object) in the rule.

" - elif col == 3: - column_whatsthis = "

This is the object class of the rule.

" - elif col == 4: - column_whatsthis = \ - """

Default Range: This the the default range specified in the rule.

""" - return \ - f""" -

Table Representation of Multi-Level Security Rules

- -

Each part of the rule is represented as a column in the table.

- - {column_whatsthis} - """ return super().data(index, role) diff --git a/setoolsgui/widgets/models/netifcon.py b/setoolsgui/widgets/models/netifcon.py index 9eb6690..55ae422 100644 --- a/setoolsgui/widgets/models/netifcon.py +++ b/setoolsgui/widgets/models/netifcon.py @@ -3,30 +3,34 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt +from PyQt5 import QtCore +import setools from .table import SEToolsTableModel -class NetifconTableModel(SEToolsTableModel): +class NetifconTable(SEToolsTableModel[setools.Netifcon]): """Table-based model for netifcons.""" headers = ["Device", "Device Context", "Packet Context"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - rule = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return rule.netif - elif col == 1: - return str(rule.context) - elif col == 2: - return str(rule.packet) + row = index.row() + col = index.column() + rule = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return rule + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return rule.netif + case 1: + return str(rule.context) + case 2: + return str(rule.packet) + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/nodecon.py b/setoolsgui/widgets/models/nodecon.py index 166476c..2c4ca9f 100644 --- a/setoolsgui/widgets/models/nodecon.py +++ b/setoolsgui/widgets/models/nodecon.py @@ -3,28 +3,32 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt +from PyQt5 import QtCore +import setools from .table import SEToolsTableModel -class NodeconTableModel(SEToolsTableModel): +class NodeconTable(SEToolsTableModel[setools.Nodecon]): """Table-based model for nodecons.""" headers = ["Network", "Context"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - rule = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return str(rule.network.with_netmask) - elif col == 1: - return str(rule.context) + row = index.row() + col = index.column() + rule = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return rule + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return str(rule.network.with_netmask) + case 1: + return str(rule.context) + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/objclass.py b/setoolsgui/widgets/models/objclass.py index eef6051..18df4b5 100644 --- a/setoolsgui/widgets/models/objclass.py +++ b/setoolsgui/widgets/models/objclass.py @@ -4,21 +4,17 @@ # # from itertools import chain -from typing import TYPE_CHECKING -from PyQt5 import QtCore, QtWidgets -from setools.exception import NoCommon +from PyQt5 import QtCore +import setools from . import modelroles from .list import SEToolsListModel from .table import SEToolsTableModel from .. import details -if TYPE_CHECKING: - from setools import ObjClass - -class ObjClassList(SEToolsListModel["ObjClass"]): +class ObjClassList(SEToolsListModel[setools.ObjClass]): """List-based model for object classes.""" @@ -29,36 +25,39 @@ class ObjClassList(SEToolsListModel["ObjClass"]): row = index.row() item = self.item_list[row] - if role == modelroles.ContextMenuRole: - return (details.objclass_detail_action(item), ) - elif role == QtCore.Qt.ItemDataRole.ToolTipRole: - return details.objclass_tooltip(item) + match role: + case modelroles.ContextMenuRole: + return (details.objclass_detail_action(item), ) + + case QtCore.Qt.ItemDataRole.ToolTipRole: + return details.objclass_tooltip(item) return super().data(index, role) -class ObjClassTableModel(SEToolsTableModel["ObjClass"]): +class ObjClassTable(SEToolsTableModel[setools.ObjClass]): """Table-based model for object classes.""" headers = ["Name", "Permissions"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - item = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == QtCore.Qt.ItemDataRole.DisplayRole: - if col == 0: - return item.name - elif col == 1: - try: - com_perms = item.common.perms - except NoCommon: - com_perms = [] + row = index.row() + col = index.column() + item = self.item_list[row] - return ", ".join(sorted(chain(com_perms, item.perms))) + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return item.name + case 1: + try: + return ", ".join(sorted(chain(item.common.perms, item.perms))) + except setools.exception.NoCommon: + return ", ".join(sorted(item.perms)) - elif role == QtCore.Qt.ItemDataRole.UserRole: - return item + return super().data(index, role) diff --git a/setoolsgui/widgets/models/portcon.py b/setoolsgui/widgets/models/portcon.py index 4753fe8..9e5b4a9 100644 --- a/setoolsgui/widgets/models/portcon.py +++ b/setoolsgui/widgets/models/portcon.py @@ -3,34 +3,37 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt +from PyQt5 import QtCore +import setools from .table import SEToolsTableModel -class PortconTableModel(SEToolsTableModel): +class PortconTable(SEToolsTableModel[setools.Portcon]): """Table-based model for portcons.""" headers = ["Port/Port Range", "Protocol", "Context"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - rule = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - low, high = rule.ports - if low == high: - return str(low) - else: - return "{0}-{1}".format(low, high) - elif col == 1: - return rule.protocol.name - elif col == 2: - return str(rule.context) + row = index.row() + col = index.column() + rule = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return rule + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + low, high = rule.ports + if low == high: + return str(low) + return f"{low}-{high}" + case 1: + return rule.protocol.name + case 2: + return str(rule.context) + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/rbacrule.py b/setoolsgui/widgets/models/rbacrule.py index 26b9530..09e2d17 100644 --- a/setoolsgui/widgets/models/rbacrule.py +++ b/setoolsgui/widgets/models/rbacrule.py @@ -3,16 +3,15 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5 import QtCore, QtWidgets -from setools import AnyRBACRule, Role, Type -from setools.exception import RuleUseError +from PyQt5 import QtCore +import setools from . import modelroles from .table import SEToolsTableModel from .. import details -class RBACRuleTableModel(SEToolsTableModel[AnyRBACRule]): +class RBACRuleTable(SEToolsTableModel[setools.AnyRBACRule]): """A table-based model for RBAC rules.""" @@ -26,105 +25,97 @@ class RBACRuleTableModel(SEToolsTableModel[AnyRBACRule]): col = index.column() rule = self.item_list[row] - if role == QtCore.Qt.ItemDataRole.DisplayRole: - if col == 0: - return rule.ruletype.name - elif col == 1: - return rule.source.name - elif col == 2: - return rule.target.name - elif col == 3: - try: - return rule.tclass.name - except RuleUseError: - # role allow - return None - elif col == 4: - try: - return rule.default.name - except RuleUseError: - # role allow - return None + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return rule.ruletype.name + case 1: + return rule.source.name + case 2: + return rule.target.name + case 3: + if rule.ruletype == setools.RBACRuletype.role_transition: + return rule.tclass.name + case 4: + if rule.ruletype == setools.RBACRuletype.role_transition: + return rule.default.name - elif role == modelroles.ContextMenuRole: - if col in (1, 2, 4): - if col == 1: - obj = rule.source - elif col == 2: - obj = rule.target - else: - try: - obj = rule.default - except RuleUseError: - return () + return None - if isinstance(obj, Role): - return (details.role_detail_action(obj), ) - else: - return (details.type_or_attr_detail_action(obj), ) + case modelroles.ContextMenuRole: + match col: + case 1: + return (details.role_detail_action(rule.source), ) + case 2: + if rule.ruletype == setools.RBACRuletype.role_transition: + return (details.type_or_attr_detail_action(rule.target), ) + return (details.role_detail_action(rule.target), ) + case 3: + if rule.ruletype == setools.RBACRuletype.role_transition: + return (details.objclass_detail_action(rule.tclass), ) + case 4: + if rule.ruletype == setools.RBACRuletype.role_transition: + return (details.role_detail_action(rule.default), ) - elif col == 3: - try: - return (details.objclass_detail_action(rule.tclass), ) - except RuleUseError: - pass + case QtCore.Qt.ItemDataRole.ToolTipRole: + match col: + case 1: + return details.role_tooltip(rule.source) + case 2: + if rule.ruletype == setools.RBACRuletype.role_transition: + return details.type_or_attr_tooltip(rule.target) + return details.role_tooltip(rule.target) + case 3: + return details.objclass_tooltip(rule.tclass) + case 4: + if rule.ruletype == setools.RBACRuletype.role_transition: + return details.role_tooltip(rule.default) - return () + case QtCore.Qt.ItemDataRole.WhatsThisRole: + match col: + case 0: + column_whatsthis = f"

{rule.ruletype} is the type of the rule.

" + case 1: + column_whatsthis = \ + f"

{rule.source} is the source role (subject) in the rule.

" + case 2: + if rule.ruletype == setools.RBACRuletype.role_transition: + column_whatsthis = \ + f""" +

{rule.target} is the target type/attribute (object) in the rule. +

""" + else: + column_whatsthis = \ + f"

{rule.target} is the target role (object) in the rule.

" + case 3: + if rule.ruletype == setools.RBACRuletype.role_transition: + column_whatsthis = \ + f"

{rule.tclass} is the object class of the rule.

" + else: + column_whatsthis = \ + f""" +

The object class column does not apply to {rule.ruletype} rules. +

""" + case 4: + if rule.ruletype == setools.RBACRuletype.role_transition: + column_whatsthis = \ + f"

{rule.default} is the default role in the rule.

" + else: + column_whatsthis = \ + f""" +

The default role column does not apply to {rule.ruletype} rules. +

""" + case _: + column_whatsthis = "" - elif role == QtCore.Qt.ItemDataRole.ToolTipRole: - if col in (1, 2): - if col == 1: - obj = rule.source - elif col == 2: - obj = rule.target - else: - try: - obj = rule.default - except RuleUseError: - return None + return \ + f""" +

Table Representation of Role-based Access Control (RBAC) Rules

- if isinstance(obj, Role): - return details.role_tooltip(obj) - else: - return details.type_or_attr_tooltip(obj) - elif col == 3: - return details.objclass_tooltip(rule.tclass) +

Each part of the rule is represented as a column in the table.

- return None - - elif role == QtCore.Qt.ItemDataRole.WhatsThisRole: - if col == 0: - column_whatsthis = f"

{rule.ruletype} is the type of the rule.

" - elif col == 1: - column_whatsthis = \ - f"

{rule.source} is the source role (subject) in the rule.

" - elif col == 2: - if isinstance(rule.target, Role): - column_whatsthis = \ - f"

{rule.target} is the target role (object) in the rule.

" - else: - column_whatsthis = \ - f"

{rule.target} is the target type/attribute (object) in the rule.

" - elif col == 3: - try: - column_whatsthis = f"

{rule.tclass} is the object class of the rule.

" - except RuleUseError: - column_whatsthis = \ - f"

The object class column does not apply to {rule.ruletype} rules.

" - elif col == 4: - try: - column_whatsthis = f"

{rule.default} is the default role in the rule.

" - except RuleUseError: - column_whatsthis = \ - f"

The default role column does not apply to {rule.ruletype} rules.

" - - return \ - f""" -

Table Representation of Role-based Access Control (RBAC) Rules

- -

Each part of the rule is represented as a column in the table.

- - {column_whatsthis} - """ + {column_whatsthis} + """ return super().data(index, role) diff --git a/setoolsgui/widgets/models/role.py b/setoolsgui/widgets/models/role.py index 6a567ee..afd5a18 100644 --- a/setoolsgui/widgets/models/role.py +++ b/setoolsgui/widgets/models/role.py @@ -3,34 +3,60 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt -from PyQt5.QtGui import QPalette, QTextCursor - -from setools.exception import MLSDisabled +from PyQt5 import QtCore +import setools +from .. import details +from . import modelroles from .table import SEToolsTableModel -class RoleTableModel(SEToolsTableModel): +class RoleTable(SEToolsTableModel[setools.Role]): """Table-based model for roles.""" headers = ["Name", "Types"] - def data(self, index, role): + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None + # There are two roles here. # The parameter, role, is the Qt role # The below item is a role in the list. - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - item = self.item_list[row] + row = index.row() + col = index.column() + item = self.item_list[row] - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return item.name - elif col == 1: - return ", ".join(sorted(t.name for t in item.types())) - elif role == Qt.ItemDataRole.UserRole: - # get the whole object - return item + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return item.name + case 1: + return ", ".join(sorted(t.name for t in item.types())) + + case modelroles.ContextMenuRole: + if col == 1: + return (details.type_detail_action(t) for t in sorted(item.types())) + + case QtCore.Qt.ItemDataRole.WhatsThisRole: + match col: + case 0: + column_whatsthis = "

This is the name of the role.

" + case 1: + column_whatsthis = \ + "

This is the list of types associated with this role.

" + case _: + column_whatsthis = "" + + return \ + f""" +

Table Representation of Roles

+ +

Each part of the declaration is represented as a column in the table.

+ + {column_whatsthis} + """ + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/table.py b/setoolsgui/widgets/models/table.py index 3ca0785..c58a1d0 100644 --- a/setoolsgui/widgets/models/table.py +++ b/setoolsgui/widgets/models/table.py @@ -14,6 +14,7 @@ from .typing import MetaclassFix T = typing.TypeVar("T") +# pylint: disable=invalid-metaclass class SEToolsTableModel(QtCore.QAbstractTableModel, typing.Generic[T], metaclass=MetaclassFix): """Base class for SETools table models, modeling a list in a tabular form.""" diff --git a/setoolsgui/widgets/models/terule.py b/setoolsgui/widgets/models/terule.py index 238f70e..0846bfd 100644 --- a/setoolsgui/widgets/models/terule.py +++ b/setoolsgui/widgets/models/terule.py @@ -5,8 +5,8 @@ # from contextlib import suppress -from PyQt5 import QtCore, QtWidgets -from setools import AnyTERule, TERuletype, TypeAttribute +from PyQt5 import QtCore +import setools from setools.exception import RuleNotConditional, RuleUseError from . import modelroles @@ -14,7 +14,7 @@ from .table import SEToolsTableModel from .. import details -class TERuleTableModel(SEToolsTableModel[AnyTERule]): +class TERuleTable(SEToolsTableModel[setools.AnyTERule]): """A table-based model for TE rules.""" @@ -29,107 +29,114 @@ class TERuleTableModel(SEToolsTableModel[AnyTERule]): col = index.column() rule = self.item_list[row] - if role == QtCore.Qt.ItemDataRole.DisplayRole: - if col == 0: - return rule.ruletype.name - elif col == 1: - return rule.source.name - elif col == 2: - return rule.target.name - elif col == 3: - return rule.tclass.name - elif col == 4: - try: - if rule.extended: - return f"{rule.xperm_type}: {rule.perms:,}" # type: ignore - else: - return ", ".join(sorted(rule.perms)) # type: ignore - except RuleUseError: - return rule.default.name # type: ignore - elif col == 5: - with suppress(RuleNotConditional): - return str(rule.conditional) - elif col == 6: - with suppress(RuleNotConditional): - return str(rule.conditional_block) + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return rule.ruletype.name + case 1: + return rule.source.name + case 2: + return rule.target.name + case 3: + return rule.tclass.name + case 4: + try: + if rule.extended: + return f"{rule.xperm_type}: {rule.perms:,}" # type: ignore + else: + return ", ".join(sorted(rule.perms)) # type: ignore + except RuleUseError: + return rule.default.name # type: ignore + case 5: + with suppress(RuleNotConditional): + return str(rule.conditional) + case 6: + with suppress(RuleNotConditional): + return str(rule.conditional_block) - return None + return None - elif role == modelroles.ContextMenuRole: - if col == 1: - return (details.type_or_attr_detail_action(rule.source), ) - elif col == 2: - return (details.type_or_attr_detail_action(rule.target), ) - elif col == 3: - return (details.objclass_detail_action(rule.tclass), ) - elif col == 4: - with suppress(RuleUseError): - return (details.type_detail_action(rule.default), ) + case modelroles.ContextMenuRole: + match col: + case 1: + return (details.type_or_attr_detail_action(rule.source), ) + case 2: + return (details.type_or_attr_detail_action(rule.target), ) + case 3: + return (details.objclass_detail_action(rule.tclass), ) + case 4: + with suppress(RuleUseError): + return (details.type_detail_action(rule.default), ) - return () + case QtCore.Qt.ItemDataRole.ToolTipRole: + match col: + case 1: + return details.type_or_attr_tooltip(rule.source) + case 2: + return details.type_or_attr_tooltip(rule.target) + case 3: + return details.objclass_tooltip(rule.tclass) - elif role == QtCore.Qt.ItemDataRole.ToolTipRole: - if col in (1, 2): - if col == 1: - return details.type_or_attr_tooltip(rule.source) - else: - return details.type_or_attr_tooltip(rule.target) - elif col == 3: - return details.objclass_tooltip(rule.tclass) + case QtCore.Qt.ItemDataRole.WhatsThisRole: + match col: + case 0: + column_whatsthis = \ + f""" +

The Rule Type column is the type of the rule; it is one of:

+ + """ + case 1: + column_whatsthis = \ + """ +

This is the source type or type attribute (subject) in the rule.

+ """ + case 2: + column_whatsthis = \ + """ +

This is the target type or type attribute (object) in the rule.

+ """ + case 3: + column_whatsthis = "

This is the object class of the rule.

" + case 4: + column_whatsthis = \ + """ +

Permissions/Default Type: The value of this depends on the rule + type:

+ + + """ + case 5: + column_whatsthis = \ + """ +

This is the conditional expression that enables/disables + this rule. If this is blank, the rule is unconditional.

+ """ + case 6: + column_whatsthis = \ + """ +

This contains the conditional branch that that rule resides in. + "True" means the rule is enabled when the conditional expression is + true; also known as the "if" block. "False" means the rule is enabled + when the conditional expression is false; also known as the "else" + block. If this is blank, the rule is unconditional.

+ """ + case _: + column_whatsthis = "" - return None - - elif role == QtCore.Qt.ItemDataRole.WhatsThisRole: - if col == 0: - column_whatsthis = \ + return \ f""" -

The Rule Type column is the type of the rule; it is one of:

- - """ - elif col == 1: - column_whatsthis = \ - "

This is the source type or type attribute (subject) in the rule.

" - elif col == 2: - column_whatsthis = \ - "

This is the target type or type attribute (object) in the rule.

" - elif col == 3: - column_whatsthis = "

This is the object class of the rule.

" - elif col == 4: - column_whatsthis = \ - """ -

Permissions/Default Type: The value of this depends on the rule type:

- - - """ - elif col == 5: - column_whatsthis = \ - """ -

This is the conditional expression that enables/disables - this rule. If this is blank, the rule is unconditional.

- """ - elif col == 6: - column_whatsthis = \ - """ -

This contains the conditional branch that that rule resides in. - "True" means the rule is enabled when the conditional expression is true; - also known as the "if" block. "False" means the rule is enabled when the - conditional expression is false; also known as the "else" block. If this - is blank, the rule is unconditional.

- """ +

Table Representation of Type Enforcement Rules

- return \ - f""" -

Table Representation of Type Enforcement Rules

+

Each part of the rule is represented as a column in the table.

-

Each part of the rule is represented as a column in the table.

- - {column_whatsthis} - """ + {column_whatsthis} + """ return super().data(index, role) diff --git a/setoolsgui/widgets/models/type.py b/setoolsgui/widgets/models/type.py index ebbc02a..af0bdea 100644 --- a/setoolsgui/widgets/models/type.py +++ b/setoolsgui/widgets/models/type.py @@ -3,32 +3,67 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt +from PyQt5 import QtCore +import setools +from .. import details +from . import modelroles from .table import SEToolsTableModel -class TypeTableModel(SEToolsTableModel): +class TypeTable(SEToolsTableModel[setools.Type]): """Table-based model for types.""" headers = ["Name", "Attributes", "Aliases", "Permissive"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - item = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return item.name - elif col == 1: - return ", ".join(sorted(a.name for a in item.attributes())) - elif col == 2: - return ", ".join(sorted(a for a in item.aliases())) - elif col == 3 and item.ispermissive: - return "Permissive" + row = index.row() + col = index.column() + item = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return item + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return item.name + case 1: + return ", ".join(sorted(a.name for a in item.attributes())) + case 2: + return ", ".join(sorted(a for a in item.aliases())) + case 3: + return "Permissive" if item.ispermissive else None + + case modelroles.ContextMenuRole: + if col == 1: + return (details.typeattr_detail_action(ta) for ta in sorted(item.attributes())) + + case QtCore.Qt.ItemDataRole.WhatsThisRole: + match col: + case 0: + column_whatsthis = "

This is the name of the type.

" + case 1: + column_whatsthis = \ + "

This is the list of attributes this type belongs to.

" + case 2: + column_whatsthis = \ + "

This is the list of alias names for this type.

" + case 3: + column_whatsthis = \ + "

This indicates whether the type is permissive.

" + case _: + column_whatsthis = "" + + return \ + f""" +

Table Representation of SELinux Types

+ +

Each part of the declaration is represented as a column in the table.

+ + {column_whatsthis} + """ + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/typeattr.py b/setoolsgui/widgets/models/typeattr.py index e28aff4..e5f5072 100644 --- a/setoolsgui/widgets/models/typeattr.py +++ b/setoolsgui/widgets/models/typeattr.py @@ -3,31 +3,57 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt -from PyQt5.QtGui import QPalette, QTextCursor - -from setools.exception import MLSDisabled +from PyQt5 import QtCore +import setools +from .. import details +from . import modelroles from .table import SEToolsTableModel -class TypeAttributeTableModel(SEToolsTableModel): +class TypeAttributeTable(SEToolsTableModel[setools.TypeAttribute]): """Table-based model for roles.""" headers = ["Name", "Types"] - def data(self, index, role): - if self.item_list and index.isValid(): - row = index.row() - col = index.column() - item = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if role == Qt.ItemDataRole.DisplayRole: - if col == 0: - return item.name - elif col == 1: - return ", ".join(sorted(t.name for t in item.expand())) + row = index.row() + col = index.column() + attr = self.item_list[row] - elif role == Qt.ItemDataRole.UserRole: - return item + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return attr.name + case 1: + return ", ".join(sorted(a.name for a in sorted(attr.expand()))) + + case modelroles.ContextMenuRole: + if col == 1: + return (details.type_detail_action(t) for t in sorted(attr.expand())) + + case QtCore.Qt.ItemDataRole.WhatsThisRole: + match col: + case 0: + column_whatsthis = "

This is the name of the type attribute.

" + case 1: + column_whatsthis = \ + "

This is the list of types associated with the attribute.

" + case _: + column_whatsthis = "" + + return \ + f""" +

Table Representation of SELinux users

+ +

Each part of the declaration is represented as a column in the table.

+ + {column_whatsthis} + """ + + return super().data(index, role) diff --git a/setoolsgui/widgets/models/user.py b/setoolsgui/widgets/models/user.py index 1e32181..5cae117 100644 --- a/setoolsgui/widgets/models/user.py +++ b/setoolsgui/widgets/models/user.py @@ -3,78 +3,74 @@ # SPDX-License-Identifier: LGPL-2.1-only # # -from PyQt5.QtCore import Qt, QModelIndex -from setools.exception import MLSDisabled +import typing -from .details import DetailsPopup +from PyQt5 import QtCore +import setools + +from .. import details +from . import modelroles from .table import SEToolsTableModel -def user_detail(parent, user): - """ - Create a dialog box for user details. - - Parameters: - parent The parent Qt Widget - user The user - """ - - detail = DetailsPopup(parent, "User detail: {0}".format(user)) - - roles = sorted(user.roles) - detail.append_header("Roles ({0}):".format(len(roles))) - - for role in roles: - detail.append(" {0}".format(role)) - - try: - level = user.mls_level - range_ = user.mls_range - except MLSDisabled: - pass - else: - detail.append_header("\nDefault MLS Level:") - detail.append(" {0}".format(level)) - detail.append_header("\nMLS Range:") - detail.append(" {0}".format(range_)) - - detail.show() - - -class UserTableModel(SEToolsTableModel): +class UserTable(SEToolsTableModel[setools.User]): """Table-based model for users.""" headers = ["Name", "Roles", "Default Level", "Range"] - def __init__(self, parent, mls): - super(UserTableModel, self).__init__(parent) - self.col_count = 4 if mls else 2 + def __init__(self, mls: bool = False, parent: QtCore.QObject | None = None): + super().__init__(parent) + self.mls: typing.Final[bool] = mls - def columnCount(self, parent=QModelIndex()): - return self.col_count + def columnCount(self, parent=QtCore.QModelIndex()) -> int: + return 4 if self.mls else 2 - def data(self, index, role): - if self.resultlist and index.isValid(): - if role == Qt.ItemDataRole.DisplayRole: - row = index.row() - col = index.column() - user = self.item_list[row] + def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole): + if not self.item_list or not index.isValid(): + return None - if col == 0: - return user.name - elif col == 1: - return ", ".join(sorted(r.name for r in user.roles)) - elif col == 2: - try: + row = index.row() + col = index.column() + user = self.item_list[row] + + match role: + case QtCore.Qt.ItemDataRole.DisplayRole: + match col: + case 0: + return user.name + case 1: + return ", ".join(sorted(r.name for r in user.roles)) + case 2: return str(user.mls_level) - except MLSDisabled: - return None - elif col == 3: - try: + case 3: return str(user.mls_range) - except MLSDisabled: - return None - elif role == Qt.ItemDataRole.UserRole: - return user + case modelroles.ContextMenuRole: + if col == 1: + return (details.role_detail_action(r) for r in sorted(user.roles)) + + case QtCore.Qt.ItemDataRole.WhatsThisRole: + match col: + case 0: + column_whatsthis = "

This is the name of the user.

" + case 1: + column_whatsthis = \ + "

This is the list of roles associated with the user.

" + case 2: + column_whatsthis = "

This is the default MLS level of the user.

" + case 3: + column_whatsthis = "

This is allowed range for the user.

" + case _: + column_whatsthis = "" + + return \ + f""" +

Table Representation of SELinux users

+ +

Each part of the declaration is represented as a column in the table.

+ + {column_whatsthis} + """ + + return super().data(index, role) diff --git a/setoolsgui/widgets/rbacrulequery.py b/setoolsgui/widgets/rbacrulequery.py index 05909a7..14e63fc 100644 --- a/setoolsgui/widgets/rbacrulequery.py +++ b/setoolsgui/widgets/rbacrulequery.py @@ -6,8 +6,7 @@ from typing import TYPE_CHECKING from PyQt5 import QtCore, QtGui, QtWidgets import setools -from . import criteria, tab -from .models.rbacrule import RBACRuleTableModel +from . import criteria, models, tab if TYPE_CHECKING: from typing import Optional @@ -118,7 +117,7 @@ class RBACRuleQueryTab(tab.TableResultTabWidget): self.criteria = (rt, src, dst, tclass, dflt) # Set result table's model - self.table_results_model = RBACRuleTableModel(self.table_results) + self.table_results_model = models.RBACRuleTable(self.table_results) if __name__ == '__main__': diff --git a/setoolsgui/widgets/terulequery.py b/setoolsgui/widgets/terulequery.py index a8312fc..c1fd780 100644 --- a/setoolsgui/widgets/terulequery.py +++ b/setoolsgui/widgets/terulequery.py @@ -6,8 +6,7 @@ from typing import TYPE_CHECKING from PyQt5 import QtCore, QtGui, QtWidgets import setools -from . import criteria, tab -from .models.terule import TERuleTableModel +from . import criteria, models, tab if TYPE_CHECKING: from typing import Optional @@ -165,7 +164,7 @@ class TERuleQueryTab(tab.TableResultTabWidget): self.criteria = (rt, src, dst, tclass, perms, dflt, bools) # Set result table's model - self.table_results_model = TERuleTableModel(self.table_results) + self.table_results_model = models.TERuleTable(self.table_results) # Connect signals tclass.selectionChanged.connect(perms.set_classes)