# 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/>.
#
from collections import namedtuple
from ..policyrep.exception import MLSDisabled
from .descriptors import DiffResultDescriptor
from .difference import Difference, SymbolWrapper
from .mls import LevelWrapper, RangeWrapper
modified_users_record = namedtuple("modified_user", ["added_roles",
"removed_roles",
"matched_roles",
"added_level",
"removed_level",
"added_range",
"removed_range"])
class UsersDifference(Difference):
"""Determine the difference in users between two policies."""
added_users = DiffResultDescriptor("diff_users")
removed_users = DiffResultDescriptor("diff_users")
modified_users = DiffResultDescriptor("diff_users")
def diff_users(self):
"""Generate the difference in users between the policies."""
self.log.info(
"Generating user differences from {0.left_policy} to {0.right_policy}".format(self))
self.added_users, self.removed_users, matched_users = self._set_diff(
(SymbolWrapper(r) for r in self.left_policy.users()),
(SymbolWrapper(r) for r in self.right_policy.users()))
self.modified_users = dict()
for left_user, right_user in matched_users:
# Criteria for modified users
# 1. change to role set, or
# 2. change to default level, or
# 3. change to range
added_roles, removed_roles, matched_roles = self._set_diff(
(SymbolWrapper(r) for r in left_user.roles),
(SymbolWrapper(r) for r in right_user.roles))
# keep wrapped and unwrapped MLS objects here so there
# are not several nested try blocks
try:
left_level_wrap = LevelWrapper(left_user.mls_level)
left_range_wrap = RangeWrapper(left_user.mls_range)
left_level = left_user.mls_level
left_range = left_user.mls_range
except MLSDisabled:
left_level_wrap = None
left_range_wrap = None
left_level = "None (MLS Disabled)"
left_range = "None (MLS Disabled)"
try:
right_level_wrap = LevelWrapper(right_user.mls_level)
right_range_wrap = RangeWrapper(right_user.mls_range)
right_level = right_user.mls_level
right_range = right_user.mls_range
except MLSDisabled:
right_level_wrap = None
right_range_wrap = None
right_level = "None (MLS Disabled)"
right_range = "None (MLS Disabled)"
if left_level_wrap != right_level_wrap:
added_level = right_level
removed_level = left_level
else:
added_level = None
removed_level = None
if left_range_wrap != right_range_wrap:
added_range = right_range
removed_range = left_range
else:
added_range = None
removed_range = None
if added_roles or removed_roles or removed_level or removed_range:
self.modified_users[left_user] = modified_users_record(added_roles,
removed_roles,
matched_roles,
added_level,
removed_level,
added_range,
removed_range)
#
# Internal functions
#
def _reset_diff(self):
"""Reset diff results on policy changes."""
self.log.debug("Resetting user differences")
self.added_users = None
self.removed_users = None
self.modified_users = None