+ res = samdb.search(base=dn, controls=["search_options:1:2", "reveal:1"],
+ attrs=[att])
+
+ blacklist = {}
+ hash = {}
+ newlinklist = []
+ changed = False
+
+ for v in value:
+ newlinklist.append(str(v))
+
+ for e in value:
+ hash[e] = 1
+ # for w2k domain level the reveal won't reveal anything ...
+ # it means that we can readd links that were removed on purpose ...
+ # Also this function in fact just accept add not removal
+
+ for e in res[0][att]:
+ if not hash.has_key(e):
+ # We put in the blacklist all the element that are in the "revealed"
+ # result and not in the "standard" result
+ # This element are links that were removed before and so that
+ # we don't wan't to readd
+ blacklist[e] = 1
+
+ for e in ref_value:
+ if not blacklist.has_key(e) and not hash.has_key(e):
+ newlinklist.append(str(e))
+ changed = True
+ if changed:
+ delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
+ else:
+ delta.remove(att)
+
+ return delta
+
+
+msg_elt_flag_strs = {
+ ldb.FLAG_MOD_ADD: "MOD_ADD",
+ ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
+ ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
+
+def checkKeepAttributeOldMtd(delta, att, reference, current,
+ basedn, samdb):
+ """ Check if we should keep the attribute modification or not.
+ This function didn't use replicationMetadata to take a decision.
+
+ :param delta: A message diff object
+ :param att: An attribute
+ :param reference: A message object for the current entry comming from
+ the reference provision.
+ :param current: A message object for the current entry commin from
+ the current provision.
+ :param basedn: The DN of the partition
+ :param samdb: A ldb connection to the sam database of the current provision.
+
+ :return: The modified message diff.
+ """
+ # Old school way of handling things for pre alpha12 upgrade
+ global defSDmodified
+ isFirst = False
+ txt = ""
+ dn = current[0].dn
+
+ for att in list(delta):
+ msgElt = delta.get(att)
+
+ if att == "nTSecurityDescriptor":
+ defSDmodified = True
+ delta.remove(att)
+ continue
+
+ if att == "dn":
+ continue
+
+ if not hashOverwrittenAtt.has_key(att):
+ if msgElt.flags() != FLAG_MOD_ADD:
+ if not handle_special_case(att, delta, reference, current,
+ False, basedn, samdb):
+ if opts.debugchange or opts.debugall:
+ try:
+ dump_denied_change(dn, att,
+ msg_elt_flag_strs[msgElt.flags()],
+ current[0][att], reference[0][att])
+ except KeyError:
+ dump_denied_change(dn, att,
+ msg_elt_flag_strs[msgElt.flags()],
+ current[0][att], None)
+ delta.remove(att)
+ continue
+ else:
+ if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
+ continue
+ elif hashOverwrittenAtt.get(att)==never:
+ delta.remove(att)
+ continue
+
+ return delta
+
+def checkKeepAttributeWithMetadata(delta, att, message, reference, current,
+ hash_attr_usn, basedn, usns, samdb):
+ """ Check if we should keep the attribute modification or not
+
+ :param delta: A message diff object
+ :param att: An attribute
+ :param message: A function to print messages
+ :param reference: A message object for the current entry comming from
+ the reference provision.
+ :param current: A message object for the current entry commin from
+ the current provision.
+ :param hash_attr_usn: A dictionnary with attribute name as keys,
+ USN and invocation id as values.
+ :param basedn: The DN of the partition
+ :param usns: A dictionnary with invocation ID as keys and USN ranges
+ as values.
+ :param samdb: A ldb object pointing to the sam DB
+
+ :return: The modified message diff.
+ """
+ global defSDmodified
+ isFirst = True
+ txt = ""
+ dn = current[0].dn
+
+ for att in list(delta):
+ if att in ["dn", "objectSid"]:
+ delta.remove(att)
+ continue
+
+ # We have updated by provision usn information so let's exploit
+ # replMetadataProperties
+ if att in forwardlinked:
+ curval = current[0].get(att, ())
+ refval = reference[0].get(att, ())
+ delta = handle_links(samdb, att, basedn, current[0]["dn"],
+ curval, refval, delta)
+ continue
+
+
+ if isFirst and len(list(delta)) > 1:
+ isFirst = False
+ txt = "%s\n" % (str(dn))
+
+ if handle_special_case(att, delta, reference, current, True, None, None):
+ # This attribute is "complicated" to handle and handling
+ # was done in handle_special_case
+ continue
+
+ attrUSN = None
+ if hash_attr_usn.get(att):
+ [attrUSN, attInvId] = hash_attr_usn.get(att)
+
+ if attrUSN is None:
+ # If it's a replicated attribute and we don't have any USN
+ # information about it. It means that we never saw it before
+ # so let's add it !
+ # If it is a replicated attribute but we are not master on it
+ # (ie. not initially added in the provision we masterize).
+ # attrUSN will be -1
+ if isReplicated(att):
+ continue
+ else:
+ message(CHANGE, "Non replicated attribute %s changed" % att)
+ continue
+
+ if att == "nTSecurityDescriptor":
+ cursd = ndr_unpack(security.descriptor,
+ str(current[0]["nTSecurityDescriptor"]))
+ cursddl = cursd.as_sddl(names.domainsid)
+ refsd = ndr_unpack(security.descriptor,
+ str(reference[0]["nTSecurityDescriptor"]))
+ refsddl = refsd.as_sddl(names.domainsid)
+
+ diff = get_diff_sddls(refsddl, cursddl)
+ if diff == "":
+ # FIXME find a way to have it only with huge huge verbose mode
+ # message(CHANGE, "%ssd are identical" % txt)
+ # txt = ""
+ delta.remove(att)
+ continue
+ else:
+ delta.remove(att)
+ message(CHANGESD, "%ssd are not identical:\n%s" % (txt, diff))
+ txt = ""
+ if attrUSN == -1:
+ message(CHANGESD, "But the SD has been changed by someonelse "\
+ "so it's impossible to know if the difference"\
+ " cames from the modification or from a previous bug")
+ dnNotToRecalculate.append(str(dn))
+ else:
+ dnToRecalculate.append(str(dn))
+ continue
+
+ if attrUSN == -1:
+ # This attribute was last modified by another DC forget
+ # about it
+ message(CHANGE, "%sAttribute: %s has been "
+ "created/modified/deleted by another DC. "
+ "Doing nothing" % (txt, att))
+ txt = ""
+ delta.remove(att)
+ continue
+ elif not usn_in_range(int(attrUSN), usns.get(attInvId)):
+ message(CHANGE, "%sAttribute: %s was not "
+ "created/modified/deleted during a "
+ "provision or upgradeprovision. Current "
+ "usn: %d. Doing nothing" % (txt, att,
+ attrUSN))
+ txt = ""
+ delta.remove(att)
+ continue
+ else:
+ if att == "defaultSecurityDescriptor":
+ defSDmodified = True
+ if attrUSN:
+ message(CHANGE, "%sAttribute: %s will be modified"
+ "/deleted it was last modified "
+ "during a provision. Current usn: "
+ "%d" % (txt, att, attrUSN))
+ txt = ""
+ else:
+ message(CHANGE, "%sAttribute: %s will be added because "
+ "it did not exist before" % (txt, att))
+ txt = ""
+ continue
+
+ return delta
+
+def update_present(ref_samdb, samdb, basedn, listPresent, usns):
+ """ This function updates the object that are already present in the
+ provision
+
+ :param ref_samdb: An LDB object pointing to the reference provision
+ :param samdb: An LDB object pointing to the updated provision
+ :param basedn: A string with the value of the base DN for the provision
+ (ie. DC=foo, DC=bar)
+ :param listPresent: A list of object that is present in the provision
+ :param usns: A list of USN range modified by previous provision and
+ upgradeprovision grouped by invocation ID
+ """
+
+ # This hash is meant to speedup lookup of attribute name from an oid,
+ # it's for the replPropertyMetaData handling
+ hash_oid_name = {}
+ res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
+ controls=["search_options:1:2"], attrs=["attributeID",
+ "lDAPDisplayName"])
+ if len(res) > 0:
+ for e in res:
+ strDisplay = str(e.get("lDAPDisplayName"))
+ hash_oid_name[str(e.get("attributeID"))] = strDisplay
+ else:
+ msg = "Unable to insert missing elements: circular references"
+ raise ProvisioningError(msg)
+
+ changed = 0
+ controls = ["search_options:1:2", "sd_flags:1:0"]
+ if usns is not None:
+ message(CHANGE, "Using replPropertyMetadata for change selection")
+ for dn in listPresent:
+ reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
+ scope=SCOPE_SUBTREE,
+ controls=controls)
+ current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
+ scope=SCOPE_SUBTREE, controls=controls)
+
+ if (
+ (str(current[0].dn) != str(reference[0].dn)) and
+ (str(current[0].dn).upper() == str(reference[0].dn).upper())
+ ):
+ message(CHANGE, "Names are the same except for the case. "
+ "Renaming %s to %s" % (str(current[0].dn),
+ str(reference[0].dn)))
+ identic_rename(samdb, reference[0].dn)
+ current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
+ scope=SCOPE_SUBTREE,
+ controls=controls)
+
+ delta = samdb.msg_diff(current[0], reference[0])
+
+ for att in backlinked:
+ delta.remove(att)
+
+ for att in attrNotCopied:
+ delta.remove(att)
+
+ delta.remove("name")
+
+ nb_items = len(list(delta))
+
+ if nb_items == 1:
+ continue
+
+ if nb_items > 1 and usns is not None:
+ # Fetch the replPropertyMetaData
+ res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
+ scope=SCOPE_SUBTREE, controls=controls,
+ attrs=["replPropertyMetaData"])
+ ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(res[0]["replPropertyMetaData"])).ctr
+
+ hash_attr_usn = {}
+ for o in ctr.array:
+ # We put in this hash only modification
+ # made on the current host
+ att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
+ if str(o.originating_invocation_id) in usns.keys():
+ hash_attr_usn[att] = [o.originating_usn, str(o.originating_invocation_id)]
+ else:
+ hash_attr_usn[att] = [-1, None]
+
+ if usns is not None:
+ delta = checkKeepAttributeWithMetadata(delta, att, message, reference,
+ current, hash_attr_usn,
+ basedn, usns, samdb)
+ else:
+ delta = checkKeepAttributeOldMtd(delta, att, reference, current, basedn, samdb)
+
+ delta.dn = dn
+
+
+ if len(delta) >1:
+ # Skip dn as the value is not really changed ...
+ attributes=", ".join(delta.keys()[1:])
+ modcontrols = []
+ relaxedatt = ['iscriticalsystemobject', 'grouptype']
+ # Let's try to reduce as much as possible the use of relax control
+ for attr in delta.keys():
+ if attr.lower() in relaxedatt:
+ modcontrols = ["relax:0", "provision:0"]
+ message(CHANGE, "%s is different from the reference one, changed"
+ " attributes: %s\n" % (dn, attributes))
+ changed += 1
+ samdb.modify(delta, modcontrols)
+ return changed
+
+def reload_full_schema(samdb, names):
+ """Load the updated schema with all the new and existing classes
+ and attributes.
+
+ :param samdb: An LDB object connected to the sam.ldb of the update
+ provision
+ :param names: List of key provision parameters
+ """
+
+ schemadn = str(names.schemadn)
+ current = samdb.search(expression="objectClass=*", base=schemadn,
+ scope=SCOPE_SUBTREE)
+ schema_ldif = ""
+ prefixmap_data = ""
+
+ for ent in current:
+ schema_ldif += samdb.write_ldif(ent, ldb.CHANGETYPE_NONE)
+
+ prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()
+ prefixmap_data = b64encode(prefixmap_data)
+
+ # We don't actually add this ldif, just parse it
+ prefixmap_ldif = "dn: %s\nprefixMap:: %s\n\n" % (schemadn, prefixmap_data)
+
+ dsdb._dsdb_set_schema_from_ldif(samdb, prefixmap_ldif, schema_ldif, schemadn)
+
+
+def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, prereloadfunc):