Clean up item model classes.

* Remove "Model" from names of model classes.
* Remove remaining *_detail() functions.
* Add typing.
* Make models dir a package.
* Update for superclass data() method as fallback.
* Switch to match/case statements.

Signed-off-by: Chris PeBenito <pebenito@ieee.org>
This commit is contained in:
Chris PeBenito 2023-08-03 14:40:51 -04:00 committed by Chris PeBenito
parent f9d0a7f1f3
commit 2d755c46ca
31 changed files with 819 additions and 713 deletions

View File

@ -3,6 +3,6 @@
from .boolean import boolean_detail, boolean_detail_action, boolean_tooltip from .boolean import boolean_detail, boolean_detail_action, boolean_tooltip
from .objclass import objclass_detail, objclass_detail_action, objclass_tooltip from .objclass import objclass_detail, objclass_detail_action, objclass_tooltip
from .role import role_detail, role_detail_action, role_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, from .type import (type_detail, type_or_attr_detail, type_detail_action,
type_or_attr_detail_action, type_tooltip, type_or_attr_tooltip) type_or_attr_detail_action, type_tooltip, type_or_attr_tooltip)

View File

@ -49,4 +49,4 @@ def role_tooltip(role: "Role") -> str:
return f"{role} is a role associated with {n_types} types." return f"{role} is a role associated with {n_types} types."
else: else:
return f"{role} is a role associated with types: " \ 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())}"

View File

@ -30,6 +30,16 @@ def typeattr_detail(attr: "TypeAttribute", parent: "Optional[QtWidgets.QWidget]"
parent) 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: def typeattr_tooltip(attr: "TypeAttribute") -> str:
"""Return tooltip text for this type attribute.""" """Return tooltip text for this type attribute."""
n_types = len(attr) n_types = len(attr)

View File

@ -5,8 +5,7 @@ from typing import TYPE_CHECKING
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
import setools import setools
from . import criteria, tab from . import criteria, models, tab
from .models.mlsrule import MLSRuleTableModel
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Optional from typing import Optional
@ -130,7 +129,7 @@ class MLSRuleQueryTab(tab.TableResultTabWidget):
self.criteria = (rt, src, dst, tclass, dflt) self.criteria = (rt, src, dst, tclass, dflt)
# Set result table's model # 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__': if __name__ == '__main__':

View File

@ -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

View File

@ -3,20 +3,16 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from typing import TYPE_CHECKING from PyQt5 import QtCore
import setools
from PyQt5 import QtCore, QtWidgets
from . import modelroles from . import modelroles
from .list import SEToolsListModel from .list import SEToolsListModel
from .table import SEToolsTableModel from .table import SEToolsTableModel
from .. import details from .. import details
if TYPE_CHECKING:
from setools import Boolean
class BooleanList(SEToolsListModel[setools.Boolean]):
class BooleanList(SEToolsListModel["Boolean"]):
"""List-based model for Booleans.""" """List-based model for Booleans."""
@ -27,32 +23,35 @@ class BooleanList(SEToolsListModel["Boolean"]):
row = index.row() row = index.row()
item = self.item_list[row] item = self.item_list[row]
if role == modelroles.ContextMenuRole: match role:
return (details.boolean_detail_action(item), ) case modelroles.ContextMenuRole:
elif role == QtCore.Qt.ItemDataRole.ToolTipRole: return (details.boolean_detail_action(item), )
return details.boolean_tooltip(item) case QtCore.Qt.ItemDataRole.ToolTipRole:
return details.boolean_tooltip(item)
return super().data(index, role) return super().data(index, role)
class BooleanTableModel(SEToolsTableModel): class BooleanTable(SEToolsTableModel):
"""Table-based model for booleans.""" """Table-based model for booleans."""
headers = ["Name", "Default State"] headers = ["Name", "Default State"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
boolean = self.item_list[row]
if role == QtCore.Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return boolean.name boolean = self.item_list[row]
elif col == 1:
return str(boolean.state)
elif role == QtCore.Qt.ItemDataRole.UserRole: match role:
# get the whole rule for boolean boolean case QtCore.Qt.ItemDataRole.DisplayRole:
return boolean match col:
case 0:
return boolean.name
case 1:
return str(boolean.state)
return super().data(index, role)

View File

@ -3,30 +3,34 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
import setools
from .table import SEToolsTableModel from .table import SEToolsTableModel
class BoundsTableModel(SEToolsTableModel): class BoundsTable(SEToolsTableModel[setools.Bounds]):
"""Table-based model for *bounds.""" """Table-based model for *bounds."""
headers = ["Rule Type", "Parent", "Child"] headers = ["Rule Type", "Parent", "Child"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
item = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return item.ruletype.name item = self.item_list[row]
elif col == 1:
return item.parent.name
elif col == 2:
return item.child.name
elif role == Qt.ItemDataRole.UserRole: match role:
return item 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)

View File

@ -3,50 +3,32 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
from PyQt5.QtGui import QPalette, QTextCursor import setools
from setools.exception import NoCommon
from .details import DetailsPopup
from .table import SEToolsTableModel from .table import SEToolsTableModel
def common_detail(parent, common): class CommonTable(SEToolsTableModel[setools.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):
"""Table-based model for common permission sets.""" """Table-based model for common permission sets."""
headers = ["Name", "Permissions"] headers = ["Name", "Permissions"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
item = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return item.name item = self.item_list[row]
elif col == 1:
return ", ".join(sorted(item.perms))
elif role == Qt.ItemDataRole.UserRole: match role:
return item case QtCore.Qt.ItemDataRole.DisplayRole:
match col:
case 0:
return item.name
case 1:
return ", ".join(sorted(item.perms))
return super().data(index, role)

View File

@ -3,36 +3,40 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
from setools.exception import ConstraintUseError import setools
from .table import SEToolsTableModel from .table import SEToolsTableModel
class ConstraintTableModel(SEToolsTableModel): class ConstraintTable(SEToolsTableModel[setools.Constraint]):
"""A table-based model for constraints.""" """A table-based model for constraints."""
headers = ["Rule Type", "Class", "Permissions", "Expression"] headers = ["Rule Type", "Class", "Permissions", "Expression"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
rule = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return rule.ruletype.name rule = self.item_list[row]
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)
elif role == Qt.ItemDataRole.UserRole: match role:
return rule 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)

View File

@ -5,33 +5,38 @@
# #
from contextlib import suppress from contextlib import suppress
from PyQt5.QtCore import Qt from PyQt5 import QtCore
import setools
from .table import SEToolsTableModel from .table import SEToolsTableModel
class DefaultTableModel(SEToolsTableModel): class DefaultTable(SEToolsTableModel[setools.Default]):
"""Table-based model for default_*.""" """Table-based model for default_*."""
headers = ["Rule Type", "Class", "Default", "Default Range"] headers = ["Rule Type", "Class", "Default", "Default Range"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
item = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return item.ruletype.name item = self.item_list[row]
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
elif role == Qt.ItemDataRole.UserRole: match role:
return item 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)

View File

@ -3,30 +3,34 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
import setools
from .table import SEToolsTableModel from .table import SEToolsTableModel
class FSUseTableModel(SEToolsTableModel): class FSUseTable(SEToolsTableModel[setools.FSUse]):
"""Table-based model for fs_use_*.""" """Table-based model for fs_use_*."""
headers = ["Ruletype", "FS Type", "Context"] headers = ["Ruletype", "FS Type", "Context"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
rule = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return rule.ruletype.name rule = self.item_list[row]
elif col == 1:
return rule.fs
elif col == 2:
return str(rule.context)
elif role == Qt.ItemDataRole.UserRole: match role:
return rule 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)

View File

@ -5,12 +5,13 @@
# #
import stat import stat
from PyQt5.QtCore import Qt from PyQt5 import QtCore
import setools
from .table import SEToolsTableModel from .table import SEToolsTableModel
class GenfsconTableModel(SEToolsTableModel): class GenfsconTable(SEToolsTableModel[setools.Genfscon]):
"""Table-based model for genfscons.""" """Table-based model for genfscons."""
@ -26,21 +27,24 @@ class GenfsconTableModel(SEToolsTableModel):
stat.S_IFLNK: "Symbolic Link", stat.S_IFLNK: "Symbolic Link",
stat.S_IFSOCK: "Socket"} stat.S_IFSOCK: "Socket"}
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
rule = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return rule.fs rule = self.item_list[row]
elif col == 1:
return rule.path
elif col == 2:
return self._filetype_to_text[rule.filetype]
elif col == 3:
return str(rule.context)
elif role == Qt.ItemDataRole.UserRole: match role:
return rule 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)

View File

@ -3,30 +3,34 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
import setools
from .table import SEToolsTableModel from .table import SEToolsTableModel
class IbendportconTableModel(SEToolsTableModel): class IbendportconTable(SEToolsTableModel[setools.Ibendportcon]):
"""Table-based model for ibendportcons.""" """Table-based model for ibendportcons."""
headers = ["Device", "Endport", "Context"] headers = ["Device", "Endport", "Context"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
rule = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return rule.name rule = self.item_list[row]
elif col == 1:
return str(rule.port)
elif col == 2:
return str(rule.context)
elif role == Qt.ItemDataRole.UserRole: match role:
return rule 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)

View File

@ -3,34 +3,37 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
import setools
from .table import SEToolsTableModel from .table import SEToolsTableModel
class IbpkeyconTableModel(SEToolsTableModel): class IbpkeyconTable(SEToolsTableModel[setools.Ibpkeycon]):
"""Table-based model for ibpkeycons.""" """Table-based model for ibpkeycons."""
headers = ["Subnet Prefix", "Partition Keys", "Context"] headers = ["Subnet Prefix", "Partition Keys", "Context"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
rule = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return str(rule.subnet_prefix) rule = self.item_list[row]
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)
elif role == Qt.ItemDataRole.UserRole: match role:
return rule 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)

View File

@ -3,28 +3,32 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
import setools
from .table import SEToolsTableModel from .table import SEToolsTableModel
class InitialSIDTableModel(SEToolsTableModel): class InitialSIDTable(SEToolsTableModel[setools.InitialSID]):
"""Table-based model for initial SIDs.""" """Table-based model for initial SIDs."""
headers = ["SID", "Context"] headers = ["SID", "Context"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
rule = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return rule.name rule = self.item_list[row]
elif col == 1:
return str(rule.context)
elif role == Qt.ItemDataRole.UserRole: match role:
return rule case QtCore.Qt.ItemDataRole.DisplayRole:
match col:
case 0:
return rule.name
case 1:
return str(rule.context)
return super().data(index, role)

View File

@ -14,6 +14,7 @@ from .typing import MetaclassFix
T = typing.TypeVar("T") T = typing.TypeVar("T")
# pylint: disable=invalid-metaclass
class SEToolsListModel(QtCore.QAbstractListModel, typing.Generic[T], metaclass=MetaclassFix): class SEToolsListModel(QtCore.QAbstractListModel, typing.Generic[T], metaclass=MetaclassFix):
""" """

View File

@ -3,71 +3,31 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
from PyQt5.QtGui import QPalette, QTextCursor
from .details import DetailsPopup
from .table import SEToolsTableModel from .table import SEToolsTableModel
def _mls_detail(parent, obj, objtype): class MLSComponentTable(SEToolsTableModel):
"""
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):
"""Table-based model for sensitivities and categories.""" """Table-based model for sensitivities and categories."""
headers = ["Name", "Aliases"] headers = ["Name", "Aliases"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
item = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return item.name item = self.item_list[row]
elif col == 1:
return ", ".join(sorted(a for a in item.aliases()))
elif role == Qt.ItemDataRole.UserRole: match role:
return item 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)

View File

@ -3,15 +3,15 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore
from setools import MLSRuletype import setools
from . import modelroles from . import modelroles
from .table import SEToolsTableModel from .table import SEToolsTableModel
from .. import details from .. import details
class MLSRuleTableModel(SEToolsTableModel): class MLSRuleTable(SEToolsTableModel[setools.MLSRule]):
"""A table-based model for MLS rules.""" """A table-based model for MLS rules."""
@ -25,66 +25,75 @@ class MLSRuleTableModel(SEToolsTableModel):
col = index.column() col = index.column()
rule = self.item_list[row] rule = self.item_list[row]
if role == QtCore.Qt.ItemDataRole.DisplayRole: match role:
if col == 0: case QtCore.Qt.ItemDataRole.DisplayRole:
return rule.ruletype.name match col:
elif col == 1: case 0:
return rule.source.name return rule.ruletype.name
elif col == 2: case 1:
return rule.target.name return rule.source.name
elif col == 3: case 2:
return rule.tclass.name return rule.target.name
elif col == 4: case 3:
return str(rule.default) return rule.tclass.name
case 4:
return str(rule.default)
elif role == modelroles.ContextMenuRole: case modelroles.ContextMenuRole:
if col == 1: match col:
return (details.type_or_attr_detail_action(rule.source), ) case 1:
elif col == 2: return (details.type_or_attr_detail_action(rule.source), )
return (details.type_or_attr_detail_action(rule.target), ) case 2:
elif col == 3: return (details.type_or_attr_detail_action(rule.target), )
return (details.objclass_detail_action(rule.tclass), ) 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: case QtCore.Qt.ItemDataRole.WhatsThisRole:
if col in (1, 2): match col:
if col == 1: case 0:
return details.type_or_attr_tooltip(rule.source) column_whatsthis = \
else: f"""
return details.type_or_attr_tooltip(rule.target) <p>The Rule Type column is the type of the rule; it is one of:</p>
elif col == 3: <ul>
return details.objclass_tooltip(rule.tclass) {"".join(f"<li>{t.name}</li>" for t in setools.MLSRuletype)}
</ul>
"""
case 1:
column_whatsthis = \
"""
<p>This is the source type or type attribute (subject) in the rule.</p>
"""
case 2:
column_whatsthis = \
"""
<p>This is the target type or type attribute (object) in the rule.</p>
"""
case 3:
column_whatsthis = "<p>This is the object class of the rule.</p>"
case 4:
column_whatsthis = \
"""
<p>Default Range: This the the default range specified in the rule.</p>
"""
case _:
column_whatsthis = ""
return None return \
elif role == QtCore.Qt.ItemDataRole.WhatsThisRole:
if col == 0:
column_whatsthis = \
f""" f"""
<p>The Rule Type column is the type of the rule; it is one of:</p> <b><p>Table Representation of Multi-Level Security Rules</p></b>
<ul>
{"".join(f"<li>{t.name}</li>" for t in MLSRuletype)} <p>Each part of the rule is represented as a column in the table.</p>
</ul>
{column_whatsthis}
""" """
elif col == 1:
column_whatsthis = \
"<p>This is the source type or type attribute (subject) in the rule.</p>"
elif col == 2:
column_whatsthis = \
"<p>This is the target type or type attribute (object) in the rule.</p>"
elif col == 3:
column_whatsthis = "<p>This is the object class of the rule.</p>"
elif col == 4:
column_whatsthis = \
"""<p>Default Range: This the the default range specified in the rule.</p>"""
return \
f"""
<b><p>Table Representation of Multi-Level Security Rules</p></b>
<p>Each part of the rule is represented as a column in the table.</p>
{column_whatsthis}
"""
return super().data(index, role) return super().data(index, role)

View File

@ -3,30 +3,34 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
import setools
from .table import SEToolsTableModel from .table import SEToolsTableModel
class NetifconTableModel(SEToolsTableModel): class NetifconTable(SEToolsTableModel[setools.Netifcon]):
"""Table-based model for netifcons.""" """Table-based model for netifcons."""
headers = ["Device", "Device Context", "Packet Context"] headers = ["Device", "Device Context", "Packet Context"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
rule = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return rule.netif rule = self.item_list[row]
elif col == 1:
return str(rule.context)
elif col == 2:
return str(rule.packet)
elif role == Qt.ItemDataRole.UserRole: match role:
return rule 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)

View File

@ -3,28 +3,32 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
import setools
from .table import SEToolsTableModel from .table import SEToolsTableModel
class NodeconTableModel(SEToolsTableModel): class NodeconTable(SEToolsTableModel[setools.Nodecon]):
"""Table-based model for nodecons.""" """Table-based model for nodecons."""
headers = ["Network", "Context"] headers = ["Network", "Context"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
rule = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return str(rule.network.with_netmask) rule = self.item_list[row]
elif col == 1:
return str(rule.context)
elif role == Qt.ItemDataRole.UserRole: match role:
return rule 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)

View File

@ -4,21 +4,17 @@
# #
# #
from itertools import chain from itertools import chain
from typing import TYPE_CHECKING
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore
from setools.exception import NoCommon import setools
from . import modelroles from . import modelroles
from .list import SEToolsListModel from .list import SEToolsListModel
from .table import SEToolsTableModel from .table import SEToolsTableModel
from .. import details from .. import details
if TYPE_CHECKING:
from setools import ObjClass
class ObjClassList(SEToolsListModel[setools.ObjClass]):
class ObjClassList(SEToolsListModel["ObjClass"]):
"""List-based model for object classes.""" """List-based model for object classes."""
@ -29,36 +25,39 @@ class ObjClassList(SEToolsListModel["ObjClass"]):
row = index.row() row = index.row()
item = self.item_list[row] item = self.item_list[row]
if role == modelroles.ContextMenuRole: match role:
return (details.objclass_detail_action(item), ) case modelroles.ContextMenuRole:
elif role == QtCore.Qt.ItemDataRole.ToolTipRole: return (details.objclass_detail_action(item), )
return details.objclass_tooltip(item)
case QtCore.Qt.ItemDataRole.ToolTipRole:
return details.objclass_tooltip(item)
return super().data(index, role) return super().data(index, role)
class ObjClassTableModel(SEToolsTableModel["ObjClass"]): class ObjClassTable(SEToolsTableModel[setools.ObjClass]):
"""Table-based model for object classes.""" """Table-based model for object classes."""
headers = ["Name", "Permissions"] headers = ["Name", "Permissions"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
item = self.item_list[row]
if role == QtCore.Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return item.name item = self.item_list[row]
elif col == 1:
try:
com_perms = item.common.perms
except NoCommon:
com_perms = []
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 super().data(index, role)
return item

View File

@ -3,34 +3,37 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
import setools
from .table import SEToolsTableModel from .table import SEToolsTableModel
class PortconTableModel(SEToolsTableModel): class PortconTable(SEToolsTableModel[setools.Portcon]):
"""Table-based model for portcons.""" """Table-based model for portcons."""
headers = ["Port/Port Range", "Protocol", "Context"] headers = ["Port/Port Range", "Protocol", "Context"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
rule = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
low, high = rule.ports rule = self.item_list[row]
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)
elif role == Qt.ItemDataRole.UserRole: match role:
return rule 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)

View File

@ -3,16 +3,15 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore
from setools import AnyRBACRule, Role, Type import setools
from setools.exception import RuleUseError
from . import modelroles from . import modelroles
from .table import SEToolsTableModel from .table import SEToolsTableModel
from .. import details from .. import details
class RBACRuleTableModel(SEToolsTableModel[AnyRBACRule]): class RBACRuleTable(SEToolsTableModel[setools.AnyRBACRule]):
"""A table-based model for RBAC rules.""" """A table-based model for RBAC rules."""
@ -26,105 +25,97 @@ class RBACRuleTableModel(SEToolsTableModel[AnyRBACRule]):
col = index.column() col = index.column()
rule = self.item_list[row] rule = self.item_list[row]
if role == QtCore.Qt.ItemDataRole.DisplayRole: match role:
if col == 0: case QtCore.Qt.ItemDataRole.DisplayRole:
return rule.ruletype.name match col:
elif col == 1: case 0:
return rule.source.name return rule.ruletype.name
elif col == 2: case 1:
return rule.target.name return rule.source.name
elif col == 3: case 2:
try: return rule.target.name
return rule.tclass.name case 3:
except RuleUseError: if rule.ruletype == setools.RBACRuletype.role_transition:
# role allow return rule.tclass.name
return None case 4:
elif col == 4: if rule.ruletype == setools.RBACRuletype.role_transition:
try: return rule.default.name
return rule.default.name
except RuleUseError:
# role allow
return None
elif role == modelroles.ContextMenuRole: return None
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 ()
if isinstance(obj, Role): case modelroles.ContextMenuRole:
return (details.role_detail_action(obj), ) match col:
else: case 1:
return (details.type_or_attr_detail_action(obj), ) 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: case QtCore.Qt.ItemDataRole.ToolTipRole:
try: match col:
return (details.objclass_detail_action(rule.tclass), ) case 1:
except RuleUseError: return details.role_tooltip(rule.source)
pass 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"<p>{rule.ruletype} is the type of the rule.</p>"
case 1:
column_whatsthis = \
f"<p>{rule.source} is the source role (subject) in the rule.</p>"
case 2:
if rule.ruletype == setools.RBACRuletype.role_transition:
column_whatsthis = \
f"""
<p>{rule.target} is the target type/attribute (object) in the rule.
</p>"""
else:
column_whatsthis = \
f"<p>{rule.target} is the target role (object) in the rule.</p>"
case 3:
if rule.ruletype == setools.RBACRuletype.role_transition:
column_whatsthis = \
f"<p>{rule.tclass} is the object class of the rule.</p>"
else:
column_whatsthis = \
f"""
<p>The object class column does not apply to {rule.ruletype} rules.
</p>"""
case 4:
if rule.ruletype == setools.RBACRuletype.role_transition:
column_whatsthis = \
f"<p>{rule.default} is the default role in the rule.<p>"
else:
column_whatsthis = \
f"""
<p>The default role column does not apply to {rule.ruletype} rules.
</p>"""
case _:
column_whatsthis = ""
elif role == QtCore.Qt.ItemDataRole.ToolTipRole: return \
if col in (1, 2): f"""
if col == 1: <b><p>Table Representation of Role-based Access Control (RBAC) Rules</p></b>
obj = rule.source
elif col == 2:
obj = rule.target
else:
try:
obj = rule.default
except RuleUseError:
return None
if isinstance(obj, Role): <p>Each part of the rule is represented as a column in the table.</p>
return details.role_tooltip(obj)
else:
return details.type_or_attr_tooltip(obj)
elif col == 3:
return details.objclass_tooltip(rule.tclass)
return None {column_whatsthis}
"""
elif role == QtCore.Qt.ItemDataRole.WhatsThisRole:
if col == 0:
column_whatsthis = f"<p>{rule.ruletype} is the type of the rule.</p>"
elif col == 1:
column_whatsthis = \
f"<p>{rule.source} is the source role (subject) in the rule.</p>"
elif col == 2:
if isinstance(rule.target, Role):
column_whatsthis = \
f"<p>{rule.target} is the target role (object) in the rule.</p>"
else:
column_whatsthis = \
f"<p>{rule.target} is the target type/attribute (object) in the rule.</p>"
elif col == 3:
try:
column_whatsthis = f"<p>{rule.tclass} is the object class of the rule.</p>"
except RuleUseError:
column_whatsthis = \
f"<p>The object class column does not apply to {rule.ruletype} rules.</p>"
elif col == 4:
try:
column_whatsthis = f"<p>{rule.default} is the default role in the rule.<p>"
except RuleUseError:
column_whatsthis = \
f"<p>The default role column does not apply to {rule.ruletype} rules.</p>"
return \
f"""
<b><p>Table Representation of Role-based Access Control (RBAC) Rules</p></b>
<p>Each part of the rule is represented as a column in the table.</p>
{column_whatsthis}
"""
return super().data(index, role) return super().data(index, role)

View File

@ -3,34 +3,60 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
from PyQt5.QtGui import QPalette, QTextCursor import setools
from setools.exception import MLSDisabled
from .. import details
from . import modelroles
from .table import SEToolsTableModel from .table import SEToolsTableModel
class RoleTableModel(SEToolsTableModel): class RoleTable(SEToolsTableModel[setools.Role]):
"""Table-based model for roles.""" """Table-based model for roles."""
headers = ["Name", "Types"] 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. # There are two roles here.
# The parameter, role, is the Qt role # The parameter, role, is the Qt role
# The below item is a role in the list. # The below item is a role in the list.
if self.item_list and index.isValid(): row = index.row()
row = index.row() col = index.column()
col = index.column() item = self.item_list[row]
item = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: match role:
if col == 0: case QtCore.Qt.ItemDataRole.DisplayRole:
return item.name match col:
elif col == 1: case 0:
return ", ".join(sorted(t.name for t in item.types())) return item.name
elif role == Qt.ItemDataRole.UserRole: case 1:
# get the whole object return ", ".join(sorted(t.name for t in item.types()))
return item
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 = "<p>This is the name of the role.</p>"
case 1:
column_whatsthis = \
"<p>This is the list of types associated with this role.</p>"
case _:
column_whatsthis = ""
return \
f"""
<b><p>Table Representation of Roles</p></b>
<p>Each part of the declaration is represented as a column in the table.</p>
{column_whatsthis}
"""
return super().data(index, role)

View File

@ -14,6 +14,7 @@ from .typing import MetaclassFix
T = typing.TypeVar("T") T = typing.TypeVar("T")
# pylint: disable=invalid-metaclass
class SEToolsTableModel(QtCore.QAbstractTableModel, typing.Generic[T], metaclass=MetaclassFix): class SEToolsTableModel(QtCore.QAbstractTableModel, typing.Generic[T], metaclass=MetaclassFix):
"""Base class for SETools table models, modeling a list in a tabular form.""" """Base class for SETools table models, modeling a list in a tabular form."""

View File

@ -5,8 +5,8 @@
# #
from contextlib import suppress from contextlib import suppress
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore
from setools import AnyTERule, TERuletype, TypeAttribute import setools
from setools.exception import RuleNotConditional, RuleUseError from setools.exception import RuleNotConditional, RuleUseError
from . import modelroles from . import modelroles
@ -14,7 +14,7 @@ from .table import SEToolsTableModel
from .. import details from .. import details
class TERuleTableModel(SEToolsTableModel[AnyTERule]): class TERuleTable(SEToolsTableModel[setools.AnyTERule]):
"""A table-based model for TE rules.""" """A table-based model for TE rules."""
@ -29,107 +29,114 @@ class TERuleTableModel(SEToolsTableModel[AnyTERule]):
col = index.column() col = index.column()
rule = self.item_list[row] rule = self.item_list[row]
if role == QtCore.Qt.ItemDataRole.DisplayRole: match role:
if col == 0: case QtCore.Qt.ItemDataRole.DisplayRole:
return rule.ruletype.name match col:
elif col == 1: case 0:
return rule.source.name return rule.ruletype.name
elif col == 2: case 1:
return rule.target.name return rule.source.name
elif col == 3: case 2:
return rule.tclass.name return rule.target.name
elif col == 4: case 3:
try: return rule.tclass.name
if rule.extended: case 4:
return f"{rule.xperm_type}: {rule.perms:,}" # type: ignore try:
else: if rule.extended:
return ", ".join(sorted(rule.perms)) # type: ignore return f"{rule.xperm_type}: {rule.perms:,}" # type: ignore
except RuleUseError: else:
return rule.default.name # type: ignore return ", ".join(sorted(rule.perms)) # type: ignore
elif col == 5: except RuleUseError:
with suppress(RuleNotConditional): return rule.default.name # type: ignore
return str(rule.conditional) case 5:
elif col == 6: with suppress(RuleNotConditional):
with suppress(RuleNotConditional): return str(rule.conditional)
return str(rule.conditional_block) case 6:
with suppress(RuleNotConditional):
return str(rule.conditional_block)
return None return None
elif role == modelroles.ContextMenuRole: case modelroles.ContextMenuRole:
if col == 1: match col:
return (details.type_or_attr_detail_action(rule.source), ) case 1:
elif col == 2: return (details.type_or_attr_detail_action(rule.source), )
return (details.type_or_attr_detail_action(rule.target), ) case 2:
elif col == 3: return (details.type_or_attr_detail_action(rule.target), )
return (details.objclass_detail_action(rule.tclass), ) case 3:
elif col == 4: return (details.objclass_detail_action(rule.tclass), )
with suppress(RuleUseError): case 4:
return (details.type_detail_action(rule.default), ) 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: case QtCore.Qt.ItemDataRole.WhatsThisRole:
if col in (1, 2): match col:
if col == 1: case 0:
return details.type_or_attr_tooltip(rule.source) column_whatsthis = \
else: f"""
return details.type_or_attr_tooltip(rule.target) <p>The Rule Type column is the type of the rule; it is one of:</p>
elif col == 3: <ul>
return details.objclass_tooltip(rule.tclass) {"".join(f"<li>{t.name}</li>" for t in setools.TERuletype)}
</ul>
"""
case 1:
column_whatsthis = \
"""
<p>This is the source type or type attribute (subject) in the rule.</p>
"""
case 2:
column_whatsthis = \
"""
<p>This is the target type or type attribute (object) in the rule.</p>
"""
case 3:
column_whatsthis = "<p>This is the object class of the rule.</p>"
case 4:
column_whatsthis = \
"""
<p>Permissions/Default Type: The value of this depends on the rule
type:</p>
<ul>
<li>Allow and allow-like rules: These are the permissions set in the
rule.</li>
<li>type_* rules: This the the default type specified in the rule.</li>
</ul>
</li>
"""
case 5:
column_whatsthis = \
"""
<p>This is the conditional expression that enables/disables
this rule. If this is blank, the rule is unconditional.</p>
"""
case 6:
column_whatsthis = \
"""
<p>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.</p>
"""
case _:
column_whatsthis = ""
return None return \
elif role == QtCore.Qt.ItemDataRole.WhatsThisRole:
if col == 0:
column_whatsthis = \
f""" f"""
<p>The Rule Type column is the type of the rule; it is one of:</p> <b><p>Table Representation of Type Enforcement Rules</p></b>
<ul>
{"".join(f"<li>{t.name}</li>" for t in TERuletype)}
</ul>
"""
elif col == 1:
column_whatsthis = \
"<p>This is the source type or type attribute (subject) in the rule.</p>"
elif col == 2:
column_whatsthis = \
"<p>This is the target type or type attribute (object) in the rule.</p>"
elif col == 3:
column_whatsthis = "<p>This is the object class of the rule.</p>"
elif col == 4:
column_whatsthis = \
"""
<p>Permissions/Default Type: The value of this depends on the rule type:</p>
<ul>
<li>Allow and allow-like rules: These are the permissions set in the rule.
</li>
<li>type_* rules: This the the default type specified in the rule.</li>
</ul>
</li>
"""
elif col == 5:
column_whatsthis = \
"""
<p>This is the conditional expression that enables/disables
this rule. If this is blank, the rule is unconditional.</p>
"""
elif col == 6:
column_whatsthis = \
"""
<p>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.</p>
"""
return \ <p>Each part of the rule is represented as a column in the table.</p>
f"""
<b><p>Table Representation of Type Enforcement Rules</p></b>
<p>Each part of the rule is represented as a column in the table.</p> {column_whatsthis}
"""
{column_whatsthis}
"""
return super().data(index, role) return super().data(index, role)

View File

@ -3,32 +3,67 @@
# SPDX-License-Identifier: LGPL-2.1-only # 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 from .table import SEToolsTableModel
class TypeTableModel(SEToolsTableModel): class TypeTable(SEToolsTableModel[setools.Type]):
"""Table-based model for types.""" """Table-based model for types."""
headers = ["Name", "Attributes", "Aliases", "Permissive"] headers = ["Name", "Attributes", "Aliases", "Permissive"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
item = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return item.name item = self.item_list[row]
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"
elif role == Qt.ItemDataRole.UserRole: match role:
return item 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 = "<p>This is the name of the type.</p>"
case 1:
column_whatsthis = \
"<p>This is the list of attributes this type belongs to.</p>"
case 2:
column_whatsthis = \
"<p>This is the list of alias names for this type.</p>"
case 3:
column_whatsthis = \
"<p>This indicates whether the type is permissive.</p>"
case _:
column_whatsthis = ""
return \
f"""
<b><p>Table Representation of SELinux Types</p></b>
<p>Each part of the declaration is represented as a column in the table.</p>
{column_whatsthis}
"""
return super().data(index, role)

View File

@ -3,31 +3,57 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt from PyQt5 import QtCore
from PyQt5.QtGui import QPalette, QTextCursor import setools
from setools.exception import MLSDisabled
from .. import details
from . import modelroles
from .table import SEToolsTableModel from .table import SEToolsTableModel
class TypeAttributeTableModel(SEToolsTableModel): class TypeAttributeTable(SEToolsTableModel[setools.TypeAttribute]):
"""Table-based model for roles.""" """Table-based model for roles."""
headers = ["Name", "Types"] headers = ["Name", "Types"]
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.item_list and index.isValid(): if not self.item_list or not index.isValid():
row = index.row() return None
col = index.column()
item = self.item_list[row]
if role == Qt.ItemDataRole.DisplayRole: row = index.row()
if col == 0: col = index.column()
return item.name attr = self.item_list[row]
elif col == 1:
return ", ".join(sorted(t.name for t in item.expand()))
elif role == Qt.ItemDataRole.UserRole: match role:
return item 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 = "<p>This is the name of the type attribute.</p>"
case 1:
column_whatsthis = \
"<p>This is the list of types associated with the attribute.</p>"
case _:
column_whatsthis = ""
return \
f"""
<b><p>Table Representation of SELinux users</p></b>
<p>Each part of the declaration is represented as a column in the table.</p>
{column_whatsthis}
"""
return super().data(index, role)

View File

@ -3,78 +3,74 @@
# SPDX-License-Identifier: LGPL-2.1-only # SPDX-License-Identifier: LGPL-2.1-only
# #
# #
from PyQt5.QtCore import Qt, QModelIndex import typing
from setools.exception import MLSDisabled
from .details import DetailsPopup from PyQt5 import QtCore
import setools
from .. import details
from . import modelroles
from .table import SEToolsTableModel from .table import SEToolsTableModel
def user_detail(parent, user): class UserTable(SEToolsTableModel[setools.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):
"""Table-based model for users.""" """Table-based model for users."""
headers = ["Name", "Roles", "Default Level", "Range"] headers = ["Name", "Roles", "Default Level", "Range"]
def __init__(self, parent, mls): def __init__(self, mls: bool = False, parent: QtCore.QObject | None = None):
super(UserTableModel, self).__init__(parent) super().__init__(parent)
self.col_count = 4 if mls else 2 self.mls: typing.Final[bool] = mls
def columnCount(self, parent=QModelIndex()): def columnCount(self, parent=QtCore.QModelIndex()) -> int:
return self.col_count return 4 if self.mls else 2
def data(self, index, role): def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole):
if self.resultlist and index.isValid(): if not self.item_list or not index.isValid():
if role == Qt.ItemDataRole.DisplayRole: return None
row = index.row()
col = index.column()
user = self.item_list[row]
if col == 0: row = index.row()
return user.name col = index.column()
elif col == 1: user = self.item_list[row]
return ", ".join(sorted(r.name for r in user.roles))
elif col == 2: match role:
try: 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) return str(user.mls_level)
except MLSDisabled: case 3:
return None
elif col == 3:
try:
return str(user.mls_range) return str(user.mls_range)
except MLSDisabled:
return None
elif role == Qt.ItemDataRole.UserRole: case modelroles.ContextMenuRole:
return user 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 = "<p>This is the name of the user.</p>"
case 1:
column_whatsthis = \
"<p>This is the list of roles associated with the user.</p>"
case 2:
column_whatsthis = "<p>This is the default MLS level of the user.</p>"
case 3:
column_whatsthis = "<p>This is allowed range for the user.</p>"
case _:
column_whatsthis = ""
return \
f"""
<b><p>Table Representation of SELinux users</p></b>
<p>Each part of the declaration is represented as a column in the table.</p>
{column_whatsthis}
"""
return super().data(index, role)

View File

@ -6,8 +6,7 @@ from typing import TYPE_CHECKING
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
import setools import setools
from . import criteria, tab from . import criteria, models, tab
from .models.rbacrule import RBACRuleTableModel
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Optional from typing import Optional
@ -118,7 +117,7 @@ class RBACRuleQueryTab(tab.TableResultTabWidget):
self.criteria = (rt, src, dst, tclass, dflt) self.criteria = (rt, src, dst, tclass, dflt)
# Set result table's model # 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__': if __name__ == '__main__':

View File

@ -6,8 +6,7 @@ from typing import TYPE_CHECKING
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
import setools import setools
from . import criteria, tab from . import criteria, models, tab
from .models.terule import TERuleTableModel
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Optional from typing import Optional
@ -165,7 +164,7 @@ class TERuleQueryTab(tab.TableResultTabWidget):
self.criteria = (rt, src, dst, tclass, perms, dflt, bools) self.criteria = (rt, src, dst, tclass, perms, dflt, bools)
# Set result table's model # Set result table's model
self.table_results_model = TERuleTableModel(self.table_results) self.table_results_model = models.TERuleTable(self.table_results)
# Connect signals # Connect signals
tclass.selectionChanged.connect(perms.set_classes) tclass.selectionChanged.connect(perms.set_classes)