diff options
author | Martin Basti <mbasti@redhat.com> | 2015-10-05 14:37:05 +0200 |
---|---|---|
committer | Martin Basti <mbasti@redhat.com> | 2015-10-15 18:37:52 +0200 |
commit | 63638ac9a32b528677694b438b276812e75917c4 (patch) | |
tree | 1d2d3434f67527d31ae36f2ce5744048480091f8 /ipaserver/install/installutils.py | |
parent | 9e007edbd902a5395797ca0ca9a698033540d755 (diff) | |
download | freeipa-63638ac9a32b528677694b438b276812e75917c4.tar.gz freeipa-63638ac9a32b528677694b438b276812e75917c4.tar.xz freeipa-63638ac9a32b528677694b438b276812e75917c4.zip |
Make offline LDIF modify more robust
* move code to installutils
* add replace_value method
* use lists instead of single values for add_value, remove_value methods
https://fedorahosted.org/freeipa/ticket/4949
Also fixes:
https://fedorahosted.org/freeipa/ticket/4048
https://fedorahosted.org/freeipa/ticket/1930
Reviewed-By: Martin Babinsky <mbabinsk@redhat.com>
Diffstat (limited to 'ipaserver/install/installutils.py')
-rw-r--r-- | ipaserver/install/installutils.py | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py index 52a110bb9..8e9e43d15 100644 --- a/ipaserver/install/installutils.py +++ b/ipaserver/install/installutils.py @@ -23,6 +23,7 @@ from __future__ import print_function import socket import getpass import gssapi +import ldif import os import re import fileinput @@ -1215,3 +1216,100 @@ def check_creds(options, realm_name): raise ScriptError("Invalid credentials: %s" % e) os.environ['KRB5CCNAME'] = ccache_name + + +class ModifyLDIF(ldif.LDIFParser): + """ + Allows to modify LDIF file. + + Operations keep the order in which were specified per DN. + Warning: only modifications of existing DNs are supported + """ + def __init__(self, input_file, output_file): + """ + :param input_file: an LDIF + :param output_file: an LDIF file + """ + ldif.LDIFParser.__init__(self, input_file) + self.writer = ldif.LDIFWriter(output_file) + self.dn_updated = set() + + self.modifications = {} # keep modify operations in original order + + def add_value(self, dn, attr, values): + """ + Add value to LDIF. + :param dn: DN of entry (must exists) + :param attr: attribute name + :param value: value to be added + """ + assert isinstance(values, list) + self.modifications.setdefault(dn, []).append( + dict( + op="add", + attr=attr, + values=values, + ) + ) + + def remove_value(self, dn, attr, values=None): + """ + Remove value from LDIF. + :param dn: DN of entry + :param attr: attribute name + :param value: value to be removed, if value is None, attribute will + be removed + """ + assert values is None or isinstance(values, list) + self.modifications.setdefault(dn, []).append( + dict( + op="del", + attr=attr, + values=values, + ) + ) + + def replace_value(self, dn, attr, values): + """ + Replace values in LDIF with new value. + :param dn: DN of entry + :param attr: attribute name + :param value: new value for atribute + """ + assert isinstance(values, list) + self.remove_value(dn, attr) + self.add_value(dn, attr, values) + + def handle(self, dn, entry): + if dn in self.modifications: + self.dn_updated.add(dn) + for mod in self.modifications.get(dn, []): + attr_name = mod["attr"] + values = mod["values"] + + if mod["op"] == "del": + # delete + attribute = entry.setdefault(attr_name, []) + if values is None: + attribute = [] + else: + attribute = [v for v in attribute if v not in values] + if not attribute: # empty + del entry[attr_name] + elif mod["op"] == "add": + # add + attribute = entry.setdefault(attr_name, []) + attribute.extend([v for v in values if v not in attribute]) + else: + assert False, "Unknown operation: %r" % mod["op"] + + self.writer.unparse(dn, entry) + + def parse(self): + ldif.LDIFParser.parse(self) + + # check if there are any remaining modifications + remaining_changes = set(self.modifications.keys()) - self.dn_updated + for dn in remaining_changes: + root_logger.error( + "DN: %s does not exists or haven't been updated", dn) |