Make available case insensitive mapping matching
authorSimo Sorce <simo@redhat.com>
Mon, 23 Feb 2015 20:25:09 +0000 (15:25 -0500)
committerPatrick Uiterwijk <puiterwijk@redhat.com>
Tue, 24 Feb 2015 15:58:25 +0000 (16:58 +0100)
If ignore_case is True then the incomping attributes are matched
case-insensitively in the policy engine.
The CAse of the incoming attribute is not changed on wildcard
matches. On ther matches attributes will be replaced according
to the mapping tables and the case used will be that of the
mapped attributes.

Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Patrick Uiterwijk <puiterwijk@redhat.com>
ipsilon/util/policy.py

index 17363aa..7fdac0c 100644 (file)
@@ -65,7 +65,7 @@ class Policy(Log):
                                  type(allowed))
             self.allowed = allowed
 
-    def map_attributes(self, attributes):
+    def map_attributes(self, attributes, ignore_case=False):
 
         if not isinstance(attributes, dict):
             raise ValueError("Attributes must be dictionary, not %s" %
@@ -74,6 +74,17 @@ class Policy(Log):
         not_mapped = copy.deepcopy(attributes)
         mapped = dict()
 
+        # If ignore_case is True,
+        # then PD translates case insensitively prefixes
+        PD = dict()
+        for k in attributes.keys():
+            if ignore_case:
+                # note duplicates that differ only by case
+                # will be lost here, beware!
+                PD[k.lower()] = k
+            else:
+                PD[k] = k
+
         for (key, value) in self.mappings:
             if not isinstance(key, list):
                 key = [key]
@@ -93,36 +104,57 @@ class Policy(Log):
                 mapprefix = None
                 mapname = value[0]
 
+            if ignore_case:
+                if prefix:
+                    prefix = prefix.lower()
+                name = name.lower()
+
             if prefix:
-                if prefix in attributes:
-                    attr = attributes[prefix]
+                if prefix in PD:
+                    attr = attributes[PD[prefix]]
                 else:
                     # '*' in a prefix matches nothing
                     continue
+
+                # If ignore_case is True,
+                # then ND translates case insensitively names
+                ND = dict()
+                if isinstance(attr, list):
+                    klist = attr
+                else:
+                    klist = attr.keys()
+                for k in klist:
+                    if ignore_case:
+                        # note duplicates that differ only by case
+                        # will be lost here, beware!
+                        ND[k.lower()] = k
+                    else:
+                        ND[k] = k
             else:
                 attr = attributes
+                ND = PD
 
-            if name in attr:
+            if name in ND and ND[name] in attr:
                 if isinstance(attr, list):
                     if mapprefix:
                         if mapprefix not in mapped:
                             mapped[mapprefix] = list()
                         mapped[mapprefix].append(mapname)
                         if not_mapped:
-                            if prefix in not_mapped:
-                                while name in not_mapped[prefix]:
-                                    not_mapped[prefix].remove(name)
+                            if PD[prefix] in not_mapped:
+                                while ND[name] in not_mapped[PD[prefix]]:
+                                    not_mapped[PD[prefix]].remove(ND[name])
                     else:
                         if mapname not in mapped:
                             mapped[mapname] = list()
-                        mapped[mapname].append(attr[name])
+                        mapped[mapname].append(attr[ND[name]])
                         if not_mapped:
-                            if prefix in not_mapped:
-                                del not_mapped[prefix]
+                            if PD[prefix] in not_mapped:
+                                del not_mapped[PD[prefix]]
                 else:
-                    mapin = copy.deepcopy(attr[name])
+                    mapin = copy.deepcopy(attr[ND[name]])
                     if mapname == '*':
-                        mapname = name
+                        mapname = ND[name]
                     if mapprefix:
                         if mapprefix not in mapped:
                             mapped[mapprefix] = dict()
@@ -131,11 +163,11 @@ class Policy(Log):
                         mapped.update({mapname: mapin})
                     if not_mapped:
                         if prefix:
-                            if prefix in not_mapped:
-                                if name in not_mapped[prefix]:
-                                    del not_mapped[prefix][name]
-                        elif name in not_mapped:
-                            del not_mapped[name]
+                            if PD[prefix] in not_mapped:
+                                if ND[name] in not_mapped[PD[prefix]]:
+                                    del not_mapped[PD[prefix]][ND[name]]
+                        elif ND[name] in not_mapped:
+                            del not_mapped[ND[name]]
             elif name == '*':
                 mapin = copy.deepcopy(attr)
                 # mapname is ignored if name == '*'
@@ -147,8 +179,8 @@ class Policy(Log):
                 else:
                     mapped.update(mapin)
                 if not_mapped:
-                    if prefix in not_mapped:
-                        del not_mapped[prefix]
+                    if prefix and PD[prefix] in not_mapped:
+                        del not_mapped[PD[prefix]]
                     else:
                         not_mapped = None
             else:
@@ -316,4 +348,49 @@ if __name__ == '__main__':
         ret += 1
         print 'Expected %s\nObtained %s' % (f2_result, f)
 
+    # Case Insensitive matching
+    tci_attributes = {'oneNameone': 'onevalueone',
+                      'onenamEtwo': 'onevaluetwo',
+                      'Two': {'twonameone': 'twovalueone',
+                              'twonameTwo': 'twovaluetwo'},
+                      'thrEE': {'threeNAMEone': 'threevalueone',
+                                'thrEEnametwo': 'threevaluetwo'},
+                      'foUr': {'fournameone': 'fourvalueone',
+                               'fournametwo': 'fourvaluetwo'},
+                      'FIVE': ['one', 'two', 'three'],
+                      'six': ['ONE', 'two', 'three']}
+
+    tci_mappings = [[['onenameone'], 'onemappedone'],
+                    [['onenametwo'], 'onemappedtwo'],
+                    [['two', '*'], '*'],
+                    [['three', 'threenameone'], 'threemappedone'],
+                    [['three', 'threenameone'], 'threemappedbis'],
+                    [['four', '*'], ['Four', '*']],
+                    [['five'], 'listfive'],
+                    [['six', 'one'], ['six', 'mapone']]]
+
+    mci_result = {'onemappedone': 'onevalueone',
+                  'onemappedtwo': 'onevaluetwo',
+                  'twonameone': 'twovalueone',
+                  'twonameTwo': 'twovaluetwo',
+                  'threemappedone': 'threevalueone',
+                  'threemappedbis': 'threevalueone',
+                  'Four': {'fournameone': 'fourvalueone',
+                           'fournametwo': 'fourvaluetwo'},
+                  'listfive': ['one', 'two', 'three'],
+                  'six': ['mapone']}
+
+    nci_result = {'thrEE': {'thrEEnametwo': 'threevaluetwo'},
+                  'six': ['two', 'three']}
+
+    p = Policy(tci_mappings)
+    print 'Case insensitive attribute mapping'
+    m, n = p.map_attributes(tci_attributes, ignore_case=True)
+    if m == mci_result and n == nci_result:
+        print 'SUCCESS'
+    else:
+        ret += 1
+        print 'FAIL: Expected %s // %s\nObtained %s // %s' % \
+            (mci_result, nci_result, m, n)
+
     sys.exit(ret)