#!/usr/share/ucs-test/runner bash
# shellcheck shell=bash
## desc: Check LDAP-replication of binary data
## bugs: [25858, 30165, 32025]
## versions:
##  3.0-0: skip
##  3.0-2: found
##  3.1-0-55: fixed
## tags:
##  - basic
##  - replication
## roles:
##  - domaincontroller_master
##  - domaincontroller_backup
##  - domaincontroller_slave
## packages:
##  - univention-config
##  - univention-directory-manager-tools
##  - ldap-utils
##  - openssl
##  - imagemagick
## exposure: careful

# 3.0-1: UDM is broken and double-bas64-encodes binary data

ucount=10 loop=10

# shellcheck source=../../lib/base.sh
. "$TESTLIBPATH/base.sh" || exit 137
# shellcheck source=../../lib/user.sh
. "$TESTLIBPATH/user.sh" || exit 137
# shellcheck source=../../lib/random.sh
. "$TESTLIBPATH/random.sh" || exit 137
# shellcheck source=../../lib/maildomain.sh
. "$TESTLIBPATH/maildomain.sh" || exit 137
# shellcheck source=../../lib/ucr.sh
. "$TESTLIBPATH/ucr.sh" || exit 137
# shellcheck source=../../lib/undo.sh
. "$TESTLIBPATH/undo.sh" || exit 137

set -o errexit # script bail out when it detects an error (a non-zero exit code).
set -o nounset # If expansion is attempted on an unset variable or parameter --> prints error

tmpdir=$(mktemp -d)
undo rm -rf "$tmpdir"

declare -r unique="${0##*/}_${$}_${RANDOM}"
section "Creating environment '$unique' for $ucount users"

undo wait_for_replication # wait at end

if [ "${listener_debug_level:-0}" -le 2 ]
then
	undo systemctl restart univention-directory-listener # Reversed order
	ucr set listener/debug/level=2
	undo ucr_restore
	systemctl restart univention-directory-listener
fi
log_lines=$(wc -l /var/log/univention/listener.log)

create_mail_domain "$domainname" && undo delete_mail_domain "$domainname"

openssl x509 \
	-inform pem -in "/etc/univention/ssl/$hostname/cert.pem" \
	-outform der -out "$tmpdir/userCertificate.bin"

users=()
for ((u=0; u<ucount; u++))
do
	username="$(user_randomname)"
	# shellcheck disable=SC2015
	MAIL=true PERSON=true POSIX=true SAMBA=true PKI=true \
		user_create "$username" \
			--set description="$unique" \
			--set displayName='Muster, Max' \
			--set birthday='2013-07-19' \
			--set employeeNumber='42' \
			--set employeeType='boss' \
			--set phone='+1-555-123' --set phone='+1-555-321' \
			--set roomNumber=1 \
			--set departmentNumber=1 \
			--set street="Mary Somerville Str. 1" \
			--set postcode="28359" \
			--set city="Bremen" \
			--set homeTelephoneNumber='+1-555-345' --set homeTelephoneNumber='+1-555-543' \
			--set mobileTelephoneNumber='+1-555-456' --set mobileTelephoneNumber='+1-555-654' \
			--set pagerTelephoneNumber='+1-555-567' --set pagerTelephoneNumber='+1-555-765' \
			--set homePostalAddress='"Mary Somerville Str. 1" "28359" "Bremen"' \
			--set homePostalAddress='"Mary-Somerville-Str. 1" "28359" "Bremen"' \
			--set mailHomeServer="${hostname}.${domainname}" \
			--set userCertificate="$(base64 <"$tmpdir/userCertificate.bin")" &&
		undo user_remove "$username" ||
		fail_fast 1 "Failed to create user $username"
	users+=("uid=$username,cn=users,$ldap_base")
done

section "Now testing replication..."
wait_for_replication

validate () {
	local file="$1" attr="$2" value
	sed -rne "s/^${attr}(;binary)?:: //p" "$tmpdir/${file}.ldif" >"$tmpdir/tmp"
	while read -r value
	do
		if [ "$attr" == "jpegPhoto" ]; then
			original_base64=$(base64 < "$tmpdir/$attr.bin")
			validate_reencoded_jpeg "$original_base64" "$value" ||
				fail_test 1 "Difference in reencoded '$attr' on $file"
		else
			base64 -d <<<"$value" | cmp "$tmpdir/$attr.bin" ||
			fail_test 1 "Difference in '$attr' on $file"
    fi
	done <"$tmpdir/tmp"
}

validate_reencoded_jpeg () {
	local original_base64="$1"
	local ldif_base64="$2"
	python3 - <<EOF
import base64
from univention.admin.syntax import jpegPhoto

original_base64 = """$original_base64"""
ldif_base64 = """$ldif_base64"""

reencoded_original = jpegPhoto.parse(original_base64)

if reencoded_original == ldif_base64:
	exit(0)
else:
	print(f'got: {reencoded_original}, expected: {ldif_base64}')
	exit(1)
EOF
}

for ((; loop>0; loop--))
do
	width=$((200 + RANDOM % 100)) height=$((200 + RANDOM % 100))
	convert -size "${width}x${height}" radial-gradient: jpeg:"$tmpdir/jpegPhoto.bin"
	info "loop=${loop} width=${width} height=${height} size=$(stat -c "%s" "$tmpdir/jpegPhoto.bin")"

	for userdn in "${users[@]}"
	do
		udm-test users/user modify --dn "$userdn" \
			--set jpegPhoto="$(base64 <"$tmpdir/jpegPhoto.bin")" ||
			fail_test 1 "Failed to modify user '$userdn'"
	done

	wait_for_replication
	# Delay the next batch of changes a bit for the S4-Connector to catch up
	# Otherwise sometimes it seems to happen that the S4-C overwrites the new value with an old one.
	sleep 20

	ldapsearch -x -LLL -o ldif-wrap=no \
		-H "ldap://$ldap_master:$ldap_master_port" \
		-D "$tests_domainadmin_account" \
		-y "$tests_domainadmin_pwdfile" \
		"(description=$unique)" >"$tmpdir/master.ldif" \
		>"$tmpdir/master.ldif" ||
		fail_fast 1 "Failure to dump master"
	[ "$(grep -c '^dn:' "$tmpdir/master.ldif")" -ne $ucount ] &&
		fail_test 1 "Incomplete LDIF of master: $(grep '^dn:' "$tmpdir/master.ldif")"
	validate master userCertificate
	validate master jpegPhoto

	ldapsearch -x -LLL -o ldif-wrap=no \
		-H "ldap://$ldap_server_name:$ldap_server_port" \
		-D "$tests_domainadmin_account" \
		-y "$tests_domainadmin_pwdfile" \
		"(description=$unique)" >"$tmpdir/server.ldif" \
		>"$tmpdir/server.ldif" ||
		fail_fast 1 "Failure to dump server"
	[ "$(grep -c '^dn:' "$tmpdir/server.ldif")" -ne $ucount ] &&
		fail_fast 1 "Incomplete LDIF of server: $(grep '^dn:' "$tmpdir/server.ldif")"
	validate server userCertificate
	validate server jpegPhoto

	ldiff --exclude sambaSID "$tmpdir/master.ldif" "$tmpdir/server.ldif" ||
		fail_fast 1 "Difference between master and server"

	if tail -n "+$log_lines" /var/log/univention/listener.log |
		grep -Em 5 'listener does not have value for key (jpegPhoto|userCertificate)'
	then
		fail_fast 1 "Problem replicating binary data"
	fi

	"$ALREADY_FAILED" && break
done

exit "$RETVAL"
