#!/usr/share/ucs-test/runner bash
# shellcheck shell=bash
## desc: "Test password synchronisation"
## exposure: dangerous
## packages:
## - univention-ad-connector
## tags:
##  - skip_admember

# shellcheck source=../../lib/base.sh
. "$TESTLIBPATH/base.sh" || exit 137
# shellcheck source=../../lib/udm.sh
. "$TESTLIBPATH/udm.sh" || exit 137
# shellcheck source=../../lib/random.sh
. "$TESTLIBPATH/random.sh" || exit 137

. "adconnector.sh" || exit 137
test -n "$connector_ad_ldap_host" || exit 137

. /usr/share/univention-lib/ucr.sh

if is_ucr_true 'connector/ad/mapping/user/password/disabled'; then
	echo "Password sync is disabled, skipping password sync check"
	exit 131
fi
SYNCMODE="$(ad_get_sync_mode)"
ad_set_sync_mode "sync"
invoke-rc.d univention-ad-connector restart

username="$(random_chars)"
lastname="$(random_chars)"
password1="U$(random_chars)123"
password2="V$(random_chars)234"
password3="W$(random_chars)987"
password4="X$(random_chars)345"
password5="Y$(random_chars)347"
password6="Z$(random_chars)555"
password7="A$(random_chars)782"

old_minPwdAge="$(univention-adsearch objectClass=domain minPwdAge | sed -ne 's|^minPwdAge: ||p')"

ldapmodify -x -D $connector_ad_ldap_binddn -y $connector_ad_ldap_bindpw -H "ldap://$connector_ad_ldap_host" <<__EOT
DN: $connector_ad_ldap_base
changetype: modify
replace: minPwdAge
minPwdAge: 0
__EOT

ad_domain=$(univention-adsearch --cross-ncs nETBIOSName=* nETBIOSName | sed -n 's/^nETBIOSName: //p')

echo "## "
echo "## Create user $username with password $password1"
echo "## "
udm-test users/user create --position cn=users,$ldap_base --set username="$username" --set lastname="$lastname" --set password="$password1" || fail_test 110
ad_wait_for_synchronization; fail_bool 0 110

samba_version=$(dpkg-query -f '${Version}' -W samba 2>/dev/null)
if dpkg --compare-versions "$samba_version" gt 2:4.13.4; then
	etypes="aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96"
else
	etypes="aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des-cbc-crc des-cbc-md5"
fi
test_auth_ucs ()
{
	local u="$1"
	local p="$2"
	echo "## "
	echo "## Test UCS auth for $username with password $p"
	echo "## "
	univention-ldapsearch -D "uid=$u,cn=users,$ldap_base" -w "$p" -LLL -s base dn
}

test_auth_ucs_kinit ()
{
	local u="$1"
	local p="$2"
	echo "## "
	echo "## Test UCS kerberos auth for $username with password $p "
	echo "## "
	## test kerberos hashes
	for type in $etypes;do
		echo "$p" | kinit --password-file=STDIN -e "$type" "$u"
		RET1=$?
		kdestroy
	done

	## test nt-hash
	echo "$p" | kinit --password-file=STDIN -e "arcfour-hmac-md5" "$u"
	RET2=$?
	kdestroy
	if [ "$RET1" != "$RET2" ];then
		echo "##FAIL Kerberos hashes and nt-hash differ!"
		fail_test 110
	else
		return $RET1
	fi

}

test_auth_ad ()
{
	local u="$1"
	local p="$2"
	echo "## "
	echo "## Test AD auth for $username with password $p"
	echo "## "
	smbclient --use-kerberos=no ${ad_domain:+-W "$ad_domain"} -U "$u%$p" //$(ucr get connector/ad/ldap/host)/sysvol -c ls
	return $?
}

change_password_in_ucs ()
{
	local u="$1"
	local p="$2"
	echo "## "
	echo "## Change password for $username via UDM to $p"
	echo "## "
	udm-test users/user modify --dn "uid=$u,cn=users,$ldap_base" --set password="$p"
	ad_wait_for_synchronization; fail_bool 0 110
}

reset_password_in_ad ()
{
	local u="$1"
	local old_passord="$2"
	local new_password="$3"
	echo "## "
	echo "## Change password for $username in Active Directory to $new_password"
	echo "## "
	ad_reset_password "$u" "$new_password"
	ad_wait_for_synchronization; fail_bool 0 110
	univention-ldapsearch uid="$u" | grep krb5Key
}


test_auth_ucs "$username" "$password1" || fail_test 110
test_auth_ucs "$username" "$password2" && fail_test 110
test_auth_ucs "$username" "$password3" && fail_test 110

test_auth_ad "$username" "$password1" || fail_test 110
test_auth_ad "$username" "$password2" && fail_test 110
test_auth_ad "$username" "$password3" && fail_test 110

change_password_in_ucs "$username" "$password2"
ad_wait_for_synchronization; fail_bool 0 110

test_auth_ucs "$username" "$password1" && fail_test 110
test_auth_ucs "$username" "$password2" || fail_test 110
test_auth_ucs "$username" "$password3" && fail_test 110

test_auth_ad "$username" "$password1" || fail_test 110
# in AD the old password also works
test_auth_ad "$username" "$password2" || fail_test 110
test_auth_ad "$username" "$password3" && fail_test 110

change_password_in_ucs "$username" "$password3"
ad_wait_for_synchronization; fail_bool 0 110

test_auth_ucs "$username" "$password1" && fail_test 110
test_auth_ucs "$username" "$password2" && fail_test 110
test_auth_ucs "$username" "$password3" || fail_test 110

test_auth_ad "$username" "$password1" && fail_test 110
test_auth_ad "$username" "$password2" || fail_test 110
test_auth_ad "$username" "$password3" || fail_test 110

reset_password_in_ad "$username" "$password3" "$password4"
ad_wait_for_synchronization; fail_bool 0 110

test_auth_ad "$username" "$password2" && fail_test 110
# It doesn't seem to be deterministic if the OLD password is valid or not. We are using
# a password reset, so the password should be valid. But sometimes it is.
#test_auth_ad "$username" "$password3" && fail_test 110
test_auth_ad "$username" "$password4" || fail_test 110

test_auth_ucs "$username" "$password2" && fail_test 110
test_auth_ucs "$username" "$password3" && fail_test 110
test_auth_ucs "$username" "$password4" || fail_test 110

reset_password_in_ad "$username" "$password4" "$password5"
ad_wait_for_synchronization; fail_bool 0 110

test_auth_ucs "$username" "$password3" && fail_test 110
test_auth_ucs "$username" "$password4" && fail_test 110
test_auth_ucs "$username" "$password5" || fail_test 110

test_auth_ad "$username" "$password3" && fail_test 110
# It doesn't seem to be deterministic if the OLD password is valid or not. We are using
# a password reset, so the password should be valid. But sometimes it is.
#test_auth_ad "$username" "$password4" && fail_test 110
test_auth_ad "$username" "$password5" || fail_test 110

change_password_in_ucs "$username" "$password6"
ad_wait_for_synchronization; fail_bool 0 110

test_auth_ucs "$username" "$password4" && fail_test 110
test_auth_ucs "$username" "$password5" && fail_test 110
test_auth_ucs "$username" "$password6" || fail_test 110

test_auth_ad "$username" "$password4" && fail_test 110
test_auth_ad "$username" "$password5" || fail_test 110
test_auth_ad "$username" "$password6" || fail_test 110

# test sync of kerberos hashes

kerberos_mapping="$(ucr get connector/ad/mapping/user/password/kerberos/enabled)"
ucr set 'connector/ad/mapping/user/password/kerberos/enabled=yes'
invoke-rc.d univention-ad-connector restart

reset_password_in_ad "$username" "$password6" "$password7"
ad_wait_for_synchronization; fail_bool 0 110

test_auth_ucs "$username" "$password5" && fail_test 110
test_auth_ucs "$username" "$password6" && fail_test 110
test_auth_ucs "$username" "$password7" || fail_test 110
test_auth_ucs_kinit "$username" "$password5" && fail_test 110
test_auth_ucs_kinit "$username" "$password6" && fail_test 110
test_auth_ucs_kinit "$username" "$password7" || fail_test 110

test_auth_ad "$username" "$password5" && fail_test 110
# It doesn't seem to be deterministic if the OLD password is valid or not. We are using
# a password reset, so the password should be valid. But sometimes it is.
#test_auth_ad "$username" "$password4" && fail_test 110
test_auth_ad "$username" "$password7" || fail_test 110
udm-test users/user remove --dn "uid=$username,cn=users,$ldap_base" || fail_test 110
ad_wait_for_synchronization; fail_bool 0 110

ldapmodify -x -D $connector_ad_ldap_binddn -y $connector_ad_ldap_bindpw -H "ldap://$connector_ad_ldap_host" <<__EOT
DN: $connector_ad_ldap_base
changetype: modify
replace: minPwdAge
minPwdAge: $old_minPwdAge
__EOT

ad_set_sync_mode "$SYNCMODE"
if $kerberos_mapping; then
	ucr set connector/ad/mapping/user/password/kerberos/enabled="$kerberos_mapping"
else
	ucr unset connector/ad/mapping/user/password/kerberos/enabled
fi
invoke-rc.d univention-ad-connector restart

exit "$RETVAL"
