setools/setoolsgui/apol/permmapedit.py

236 lines
8.1 KiB
Python
Raw Normal View History

# Copyright 2016, Tresys Technology, LLC
#
# This file is part of SETools.
#
# SETools is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 2.1 of
# the License, or (at your option) any later version.
#
# SETools is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with SETools. If not, see
# <http://www.gnu.org/licenses/>.
#
import logging
import copy
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt
from PyQt5.QtGui import QPalette
from PyQt5.QtWidgets import QDialog, QFrame, QWidget
from ..models import SEToolsListModel
from ..widget import SEToolsWidget
class PermissionMapEditor(SEToolsWidget, QDialog):
"""
A permission map editor. This dialog has two versions,
one for editing the weight/direction and another for
including or excluding permissions in an analysis.
Parameters:
parent The parent Qt widget
edit (bool) If true, the dialog will take
the editor behavior. If False, the dialog
will take the enable/disable permission
behavior.
"""
class_toggle = pyqtSignal(bool)
def __init__(self, parent, edit):
super(PermissionMapEditor, self).__init__(parent)
self.log = logging.getLogger(__name__)
self.parent = parent
self.edit = edit
self.setupUi()
def setupUi(self):
self.load_ui("apol/permmap_editor.ui")
# set up class list
self.class_model = SEToolsListModel(self)
self.classes.setModel(self.class_model)
# permission widgets
self.widgets = []
# set up editor mode
self.enable_all.setHidden(self.edit)
self.disable_all.setHidden(self.edit)
# connect signals
self.classes.selectionModel().selectionChanged.connect(self.class_selected)
self.enable_all.clicked.connect(self.enable_all_perms)
self.disable_all.clicked.connect(self.disable_all_perms)
def show(self, perm_map):
# keep an internal copy because the map is mutable
# and this dialog may be canceled after some edits.
self.perm_map = copy.deepcopy(perm_map)
self.class_model.item_list = sorted(perm_map.classes())
# clear class selection and mappings
# since this widget will typically
# be reused.
self.classes.clearSelection()
self._clear_mappings()
self.enable_all.setToolTip(None)
self.disable_all.setToolTip(None)
if self.edit:
self.setWindowTitle("{0} - Permission Map Editor - apol".format(self.perm_map))
else:
self.setWindowTitle("{0} - Permission Map Viewer - apol".format(self.perm_map))
super(PermissionMapEditor, self).show()
def accept(self):
self.parent.apply_permmap(self.perm_map)
super(PermissionMapEditor, self).accept()
def class_selected(self):
# the .ui is set to 1 selection
for index in self.classes.selectionModel().selectedIndexes():
class_name = self.class_model.data(index, Qt.DisplayRole)
self.log.debug("Setting class to {0}".format(class_name))
self.enable_all.setToolTip("Include all permissions in the {0} class.".format(class_name))
self.disable_all.setToolTip("Exclude all permissions in the {0} class.".format(class_name))
self._clear_mappings()
# populate new mappings
for perm in sorted(self.perm_map.perms(class_name)):
# create permission mapping
mapping = PermissionMapping(self, perm, self.edit)
mapping.setAttribute(Qt.WA_DeleteOnClose)
self.class_toggle.connect(mapping.enabled.setChecked)
self.perm_mappings.addWidget(mapping)
self.widgets.append(mapping)
# add horizonal line
line = QFrame(self)
line.setFrameShape(QFrame.HLine)
line.setFrameShadow(QFrame.Sunken)
self.perm_mappings.addWidget(line)
self.widgets.append(line)
def enable_all_perms(self):
self.class_toggle.emit(True)
def disable_all_perms(self):
self.class_toggle.emit(False)
#
# Internal functions
#
def _clear_mappings(self):
# delete current mappings
for mapping in self.widgets:
mapping.close()
self.widgets.clear()
index_to_setting = ["r", "w", "b", "n"]
index_to_word = ["Read", "Write", "Both", "None"]
setting_to_index = {"r": 0, "w": 1, "b": 2, "n": 3}
class PermissionMapping(SEToolsWidget, QWidget):
"""
A widget representing mapping for a particular permission.
This dialog has two versions, one for editing the weight/direction
and another for including or excluding permissions in an analysis.
Parameters:
parent The parent Qt widget
edit (bool) If true, the widget will take
the editor behavior. If False, the dialog
will take the enable/disable permission
behavior.
"""
def __init__(self, parent, mapping, edit):
super(PermissionMapping, self).__init__(parent)
self.log = logging.getLogger(__name__)
self.parent = parent
self.mapping = mapping
self.edit = edit
self.setupUi()
def setupUi(self):
self.load_ui("apol/permmapping.ui")
self.permission.setText(str(self.mapping.perm))
self.weight.setValue(self.mapping.weight)
self.enabled.setChecked(self.mapping.enabled)
if self.edit:
self.weight.setToolTip("Set the information flow weight of {0}".format(
self.mapping.perm))
self.direction.setToolTip("Set the information flow direction of {0}".format(
self.mapping.perm))
else:
self.enabled.setToolTip("Include or exclude {0} from the analysis.".format(
self.mapping.perm))
self.weight.setEnabled(self.edit)
self.direction.setEnabled(self.edit)
self.enabled.setHidden(self.edit)
# setup color palettes for direction
self.orig_palette = self.direction.palette()
self.error_palette = self.direction.palette()
self.error_palette.setColor(QPalette.Button, Qt.red)
self.error_palette.setColor(QPalette.ButtonText, Qt.white)
# setup direction
self.direction.insertItems(0, index_to_word)
if self.mapping.direction == 'u':
# Temporarily add unmapped value to items
self.direction.insertItem(len(index_to_word), "Unmapped")
self.direction.setCurrentText("Unmapped")
self.direction.setPalette(self.error_palette)
self.unmapped = True
else:
self.direction.setCurrentIndex(setting_to_index[self.mapping.direction])
self.unmapped = False
# connect signals
self.direction.currentIndexChanged.connect(self.set_direction)
self.weight.valueChanged.connect(self.set_weight)
self.enabled.toggled.connect(self.set_enabled)
def set_direction(self, value):
if self.unmapped:
if value == "Unmapped":
return
# Remove unmapped item if setting the mapping.
self.direction.removeItem(len(index_to_word))
self.direction.setPalette(self.orig_palette)
self.unmapped = False
dir_ = index_to_setting[value]
self.log.debug("Setting {0.class_}:{0.perm} direction to {1}".format(self.mapping, dir_))
self.mapping.direction = dir_
def set_weight(self, value):
self.log.debug("Setting {0.class_}:{0.perm} weight to {1}".format(self.mapping, value))
self.mapping.weight = int(value)
def set_enabled(self, value):
self.log.debug("Setting {0.class_}:{0.perm} enabled to {1}".format(self.mapping, value))
self.mapping.enabled = value