def process_outbound_entry(queue_entry, ldap_connection):
    """ Processes queue_entry and apply changes to the LDAP user/group.

    Args:
        queue_entry: (dict) A outbound_queue table entry. The mandatory keys in
            the dict are:
                {
                    "data": (dict containing current state of LDAP object)
                    "data_type": (str)
                }
        ldap_connection: (ldap Connection object) A bound ldap connection object.
    Returns:
        write_confirmation: (bool) Returns True if a change to LDAP occurred,
            returns False if no LDAP changes occurred
    """
    distinguished_name = get_distinguished_name(queue_entry)
    data_type = queue_entry["data_type"]
    if data_type == "group":
        sawtooth_entry_filtered = outbound_group_filter(queue_entry["data"], "ldap")
    elif data_type == "user":
        # Outbound AD user changes is currently not supported
        return False

    object_def = ObjectDef(data_type, ldap_connection)
    reader_cursor = Reader(ldap_connection, object_def, distinguished_name)
    reader_cursor.search()
    writer_cursor = Writer.from_cursor(reader_cursor)

    if reader_cursor.entries:
        LOGGER.debug("Updating AD %s: %s", data_type, distinguished_name)
        validated_entry = validate_update_entry(sawtooth_entry_filtered, data_type)

        # Grab current state of user/group in LDAP
        ldap_resource = writer_cursor[0]

        # Update AD user/group
        for ad_attribute in validated_entry:
            ldap_current_value = ldap_resource[ad_attribute].value

            if ad_attribute != "distinguishedName" and validated_entry[ad_attribute]:
                # Convert member list to list if value is a string
                if ad_attribute == "member" and isinstance(ldap_current_value, str):
                    ldap_current_value = [ldap_current_value]

                # Sort lists for comparison
                if isinstance(ldap_current_value, list):
                    ldap_current_value.sort()
                    validated_entry[ad_attribute].sort()
                if ldap_current_value != validated_entry[ad_attribute]:
                    ldap_resource[ad_attribute] = validated_entry[ad_attribute]
        return ldap_resource.entry_commit_changes()

    LOGGER.debug("AD %s %s was not found.", data_type, distinguished_name)
    return False
def update_entry_ldap(queue_entry, ldap_conn):
    """
        Routes the given queue entry to the proper handler to update the
        AD (user | group) in Active Directory.
    """
    data_type = queue_entry["data_type"]
    distinguished_name = get_distinguished_name(queue_entry)

    LOGGER.info("Updating information for %s", distinguished_name)
    if data_type == "user":
        sawtooth_entry_filtered = outbound_user_filter(queue_entry["data"], "ldap")
    elif data_type == "group":
        sawtooth_entry_filtered = outbound_group_filter(queue_entry["data"], "ldap")
    validated_entry = validate_update_entry(sawtooth_entry_filtered, data_type)
    modify_ad_attributes(distinguished_name, validated_entry, ldap_conn)
def test_validate_update_entry_errors(payload, data_type, err_msg):
    """Test that invalid payloads raise an error."""
    with pytest.raises(ValidationException) as err:
        validate_update_entry(payload, data_type)
        assert str(err.value) == err_msg
def test_validate_update_entry(payload, data_type, expected):
    """Test that valid payloads returns a valid payload."""
    result = validate_update_entry(payload, data_type)
    expected.pop("cn", None)
    expected.pop("groupType", None)
    assert result == expected