| # Copyright 2015, 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/>. |
| # |
| """ |
| SETools descriptors. |
| |
| These classes override how a class's attributes are get/set/deleted. |
| This is how the @property decorator works. |
| |
| See https://docs.python.org/3/howto/descriptor.html |
| for more details. |
| """ |
| |
| import re |
| from collections import defaultdict |
| from weakref import WeakKeyDictionary |
| |
| # |
| # Query criteria descriptors |
| # |
| # Implementation note: if the name_regex attribute value |
| # is changed the criteria must be reset. |
| # |
| |
| |
| class CriteriaDescriptor(object): |
| |
| """ |
| Single item criteria descriptor. |
| |
| Parameters: |
| name_regex The name of instance's regex setting attribute; |
| used as name_regex below. If unset, |
| regular expressions will never be used. |
| lookup_function The name of the SELinuxPolicy lookup function, |
| e.g. lookup_type or lookup_boolean. |
| default_value The default value of the criteria. The default |
| is None. |
| |
| Read-only instance attribute use (obj parameter): |
| policy The instance of SELinuxPolicy |
| name_regex This attribute is read to determine if |
| the criteria should be looked up or |
| compiled into a regex. If the attribute |
| does not exist, False is assumed. |
| """ |
| |
| def __init__(self, name_regex=None, lookup_function=None, default_value=None): |
| assert name_regex or lookup_function, "A simple attribute should be used if there is " \ |
| "no regex nor lookup function." |
| self.regex = name_regex |
| self.default_value = default_value |
| self.lookup_function = lookup_function |
| |
| # use weak references so instances can be |
| # garbage collected, rather than unnecessarily |
| # kept around due to this descriptor. |
| self.instances = WeakKeyDictionary() |
| |
| def __get__(self, obj, objtype=None): |
| if obj is None: |
| return self |
| |
| return self.instances.setdefault(obj, self.default_value) |
| |
| def __set__(self, obj, value): |
| if not value: |
| self.instances[obj] = None |
| elif self.regex and getattr(obj, self.regex, False): |
| self.instances[obj] = re.compile(value) |
| elif self.lookup_function: |
| lookup = getattr(obj.policy, self.lookup_function) |
| self.instances[obj] = lookup(value) |
| else: |
| self.instances[obj] = value |
| |
| |
| class CriteriaSetDescriptor(CriteriaDescriptor): |
| |
| """Descriptor for a set of criteria.""" |
| |
| def __set__(self, obj, value): |
| if not value: |
| self.instances[obj] = None |
| elif self.regex and getattr(obj, self.regex, False): |
| self.instances[obj] = re.compile(value) |
| elif self.lookup_function: |
| lookup = getattr(obj.policy, self.lookup_function) |
| self.instances[obj] = set(lookup(v) for v in value) |
| else: |
| self.instances[obj] = set(value) |
| |
| |
| # |
| # NetworkX Graph Descriptors |
| # |
| # These descriptors are used to simplify all |
| # of the dictionary use in the NetworkX graph. |
| # |
| |
| |
| class NetworkXGraphEdgeDescriptor(object): |
| |
| """ |
| Descriptor base class for NetworkX graph edge attributes. |
| |
| Parameter: |
| name The edge property name |
| |
| Instance class attribute use (obj parameter): |
| G The NetworkX graph |
| source The edge's source node |
| target The edge's target node |
| """ |
| |
| def __init__(self, propname): |
| self.name = propname |
| |
| def __get__(self, obj, objtype=None): |
| if obj is None: |
| return self |
| |
| return obj.G[obj.source][obj.target][self.name] |
| |
| def __set__(self, obj, value): |
| raise NotImplementedError |
| |
| def __delete__(self, obj): |
| raise NotImplementedError |
| |
| |
| class EdgeAttrDict(NetworkXGraphEdgeDescriptor): |
| |
| """A descriptor for edge attributes that are dictionaries.""" |
| |
| def __set__(self, obj, value): |
| # None is a special value to initialize the attribute |
| if value is None: |
| obj.G[obj.source][obj.target][self.name] = defaultdict(list) |
| else: |
| raise ValueError("{0} dictionaries should not be assigned directly".format(self.name)) |
| |
| def __delete__(self, obj): |
| obj.G[obj.source][obj.target][self.name].clear() |
| |
| |
| class EdgeAttrIntMax(NetworkXGraphEdgeDescriptor): |
| |
| """ |
| A descriptor for edge attributes that are non-negative integers that always |
| keep the max assigned value until re-initialized. |
| """ |
| |
| def __set__(self, obj, value): |
| # None is a special value to initialize |
| if value is None: |
| obj.G[obj.source][obj.target][self.name] = 0 |
| else: |
| current_value = obj.G[obj.source][obj.target][self.name] |
| obj.G[obj.source][obj.target][self.name] = max(current_value, value) |
| |
| |
| class EdgeAttrList(NetworkXGraphEdgeDescriptor): |
| |
| """A descriptor for edge attributes that are lists.""" |
| |
| def __set__(self, obj, value): |
| # None is a special value to initialize |
| if value is None: |
| obj.G[obj.source][obj.target][self.name] = [] |
| else: |
| raise ValueError("{0} lists should not be assigned directly".format(self.name)) |
| |
| def __delete__(self, obj): |
| # in Python3 a .clear() function was added for lists |
| # keep this implementation for Python 2 compat |
| del obj.G[obj.source][obj.target][self.name][:] |
| |
| |
| # |
| # Permission map descriptors |
| # |
| class PermissionMapDescriptor(object): |
| |
| """ |
| Descriptor for Permission Map mappings. |
| |
| Parameter: |
| name The map setting name. |
| validator A callable for validating the setting. |
| |
| Instance class attribute use (obj parameter): |
| perm_map The full permission map. |
| class_ The mapping's object class |
| perm The mapping's permission |
| """ |
| |
| def __init__(self, propname, validator): |
| self.name = propname |
| self.validator = validator |
| |
| def __get__(self, obj, objtype=None): |
| if obj is None: |
| return self |
| |
| return obj.perm_map[obj.class_][obj.perm][self.name] |
| |
| def __set__(self, obj, value): |
| obj.perm_map[obj.class_][obj.perm][self.name] = self.validator(value) |
| |
| def __delete__(self, obj): |
| raise AttributeError |