Home | History | Annotate | Download | only in setools
      1 # Copyright 2015, Tresys Technology, LLC
      2 #
      3 # This file is part of SETools.
      4 #
      5 # SETools is free software: you can redistribute it and/or modify
      6 # it under the terms of the GNU Lesser General Public License as
      7 # published by the Free Software Foundation, either version 2.1 of
      8 # the License, or (at your option) any later version.
      9 #
     10 # SETools is distributed in the hope that it will be useful,
     11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 # GNU Lesser General Public License for more details.
     14 #
     15 # You should have received a copy of the GNU Lesser General Public
     16 # License along with SETools.  If not, see
     17 # <http://www.gnu.org/licenses/>.
     18 #
     19 """
     20 SETools descriptors.
     21 
     22 These classes override how a class's attributes are get/set/deleted.
     23 This is how the @property decorator works.
     24 
     25 See https://docs.python.org/3/howto/descriptor.html
     26 for more details.
     27 """
     28 
     29 import re
     30 from collections import defaultdict
     31 from weakref import WeakKeyDictionary
     32 
     33 #
     34 # Query criteria descriptors
     35 #
     36 # Implementation note: if the name_regex attribute value
     37 # is changed the criteria must be reset.
     38 #
     39 
     40 
     41 class CriteriaDescriptor(object):
     42 
     43     """
     44     Single item criteria descriptor.
     45 
     46     Parameters:
     47     name_regex      The name of instance's regex setting attribute;
     48                     used as name_regex below.  If unset,
     49                     regular expressions will never be used.
     50     lookup_function The name of the SELinuxPolicy lookup function,
     51                     e.g. lookup_type or lookup_boolean.
     52     default_value   The default value of the criteria.  The default
     53                     is None.
     54 
     55     Read-only instance attribute use (obj parameter):
     56     policy          The instance of SELinuxPolicy
     57     name_regex      This attribute is read to determine if
     58                     the criteria should be looked up or
     59                     compiled into a regex.  If the attribute
     60                     does not exist, False is assumed.
     61     """
     62 
     63     def __init__(self, name_regex=None, lookup_function=None, default_value=None):
     64         assert name_regex or lookup_function, "A simple attribute should be used if there is " \
     65             "no regex nor lookup function."
     66         self.regex = name_regex
     67         self.default_value = default_value
     68         self.lookup_function = lookup_function
     69 
     70         # use weak references so instances can be
     71         # garbage collected, rather than unnecessarily
     72         # kept around due to this descriptor.
     73         self.instances = WeakKeyDictionary()
     74 
     75     def __get__(self, obj, objtype=None):
     76         if obj is None:
     77             return self
     78 
     79         return self.instances.setdefault(obj, self.default_value)
     80 
     81     def __set__(self, obj, value):
     82         if not value:
     83             self.instances[obj] = None
     84         elif self.regex and getattr(obj, self.regex, False):
     85             self.instances[obj] = re.compile(value)
     86         elif self.lookup_function:
     87             lookup = getattr(obj.policy, self.lookup_function)
     88             self.instances[obj] = lookup(value)
     89         else:
     90             self.instances[obj] = value
     91 
     92 
     93 class CriteriaSetDescriptor(CriteriaDescriptor):
     94 
     95     """Descriptor for a set of criteria."""
     96 
     97     def __set__(self, obj, value):
     98         if not value:
     99             self.instances[obj] = None
    100         elif self.regex and getattr(obj, self.regex, False):
    101             self.instances[obj] = re.compile(value)
    102         elif self.lookup_function:
    103             lookup = getattr(obj.policy, self.lookup_function)
    104             self.instances[obj] = set(lookup(v) for v in value)
    105         else:
    106             self.instances[obj] = set(value)
    107 
    108 
    109 #
    110 # NetworkX Graph Descriptors
    111 #
    112 # These descriptors are used to simplify all
    113 # of the dictionary use in the NetworkX graph.
    114 #
    115 
    116 
    117 class NetworkXGraphEdgeDescriptor(object):
    118 
    119     """
    120     Descriptor base class for NetworkX graph edge attributes.
    121 
    122     Parameter:
    123     name        The edge property name
    124 
    125     Instance class attribute use (obj parameter):
    126     G           The NetworkX graph
    127     source      The edge's source node
    128     target      The edge's target node
    129     """
    130 
    131     def __init__(self, propname):
    132         self.name = propname
    133 
    134     def __get__(self, obj, objtype=None):
    135         if obj is None:
    136             return self
    137 
    138         return obj.G[obj.source][obj.target][self.name]
    139 
    140     def __set__(self, obj, value):
    141         raise NotImplementedError
    142 
    143     def __delete__(self, obj):
    144         raise NotImplementedError
    145 
    146 
    147 class EdgeAttrDict(NetworkXGraphEdgeDescriptor):
    148 
    149     """A descriptor for edge attributes that are dictionaries."""
    150 
    151     def __set__(self, obj, value):
    152         # None is a special value to initialize the attribute
    153         if value is None:
    154             obj.G[obj.source][obj.target][self.name] = defaultdict(list)
    155         else:
    156             raise ValueError("{0} dictionaries should not be assigned directly".format(self.name))
    157 
    158     def __delete__(self, obj):
    159         obj.G[obj.source][obj.target][self.name].clear()
    160 
    161 
    162 class EdgeAttrIntMax(NetworkXGraphEdgeDescriptor):
    163 
    164     """
    165     A descriptor for edge attributes that are non-negative integers that always
    166     keep the max assigned value until re-initialized.
    167     """
    168 
    169     def __set__(self, obj, value):
    170         # None is a special value to initialize
    171         if value is None:
    172             obj.G[obj.source][obj.target][self.name] = 0
    173         else:
    174             current_value = obj.G[obj.source][obj.target][self.name]
    175             obj.G[obj.source][obj.target][self.name] = max(current_value, value)
    176 
    177 
    178 class EdgeAttrList(NetworkXGraphEdgeDescriptor):
    179 
    180     """A descriptor for edge attributes that are lists."""
    181 
    182     def __set__(self, obj, value):
    183         # None is a special value to initialize
    184         if value is None:
    185             obj.G[obj.source][obj.target][self.name] = []
    186         else:
    187             raise ValueError("{0} lists should not be assigned directly".format(self.name))
    188 
    189     def __delete__(self, obj):
    190         # in Python3 a .clear() function was added for lists
    191         # keep this implementation for Python 2 compat
    192         del obj.G[obj.source][obj.target][self.name][:]
    193