mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2025-04-04 00:17:06 +00:00
Upstream is adding handling for utf8 domains by creating a domain alias @utf8 -> @idna. I'm deviating from this approach by setting multiple email address (idna and utf8) per user and alias where a domain contains non-ascii characters. The maildrop (mailbox) remains the same - all mail goes to the user's mailbox regardless of which email address was used. This is more in line with how other systems (eg. active directory), handle multiple email addresses for a single user. # Conflicts: # README.md # management/mailconfig.py # management/templates/index.html # setup/dns.sh # setup/mail-users.sh
364 lines
12 KiB
Bash
364 lines
12 KiB
Bash
# -*- indent-tabs-mode: t; tab-width: 4; -*-
|
|
#
|
|
# User management tests
|
|
|
|
_test_mixed_case() {
|
|
# helper function sends multiple email messages to test mixed case
|
|
# input scenarios
|
|
local alices=($1) # list of mixed-case email addresses for alice
|
|
local bobs=($2) # list of mixed-case email addresses for bob
|
|
local aliases=($3) # list of mixed-case email addresses for an alias
|
|
|
|
start_log_capture
|
|
|
|
local alice_pw="$(generate_password 16)"
|
|
local bob_pw="$(generate_password 16)"
|
|
# create local user alice and alias group
|
|
if mgmt_assert_create_user "${alices[0]}" "$alice_pw"; then
|
|
# test that alice cannot also exist at the same time
|
|
if mgmt_create_user "${alices[1]}" "$alice_pw" no; then
|
|
test_failure "Creation of a user with the same email address, but different case, succeeded."
|
|
test_failure "${REST_ERROR}"
|
|
fi
|
|
|
|
# create an alias group with alice in it
|
|
mgmt_assert_create_alias_group "${aliases[0]}" "${alices[1]}"
|
|
fi
|
|
|
|
# create local user bob
|
|
mgmt_assert_create_user "${bobs[0]}" "$bob_pw"
|
|
|
|
assert_check_logs
|
|
|
|
|
|
# send mail from bob to alice
|
|
#
|
|
if ! have_test_failures; then
|
|
record "[Mailing to alice from bob]"
|
|
start_log_capture
|
|
local output
|
|
output="$($PYMAIL -to ${alices[2]} "$alice_pw" $PRIVATE_IP ${bobs[1]} "$bob_pw" 2>&1)"
|
|
assert_python_success $? "$output"
|
|
assert_check_logs
|
|
|
|
# send mail from bob to the alias, ensure alice got it
|
|
#
|
|
record "[Mailing to alias from bob]"
|
|
start_log_capture
|
|
local subject="Mail-In-A-Box test $(generate_uuid)"
|
|
output="$($PYMAIL -subj "$subject" -no-delete -to ${aliases[1]} na $PRIVATE_IP ${bobs[2]} "$bob_pw" 2>&1)"
|
|
assert_python_success $? "$output"
|
|
output="$($PYMAIL -subj "$subject" -no-send $PRIVATE_IP ${alices[3]} "$alice_pw" 2>&1)"
|
|
assert_python_success $? "$output"
|
|
assert_check_logs
|
|
|
|
# send mail from alice as the alias to bob, ensure bob got it
|
|
#
|
|
record "[Mailing to bob as alias from alice]"
|
|
start_log_capture
|
|
local subject="Mail-In-A-Box test $(generate_uuid)"
|
|
output="$($PYMAIL -subj "$subject" -no-delete -f ${aliases[2]} -to ${bobs[2]} "$bob_pw" $PRIVATE_IP ${alices[4]} "$alice_pw" 2>&1)"
|
|
assert_python_success $? "$output"
|
|
output="$($PYMAIL -subj "$subject" -no-send $PRIVATE_IP ${bobs[3]} "$bob_pw" 2>&1)"
|
|
assert_python_success $? "$output"
|
|
assert_check_logs
|
|
fi
|
|
|
|
mgmt_assert_delete_user "${alices[1]}"
|
|
mgmt_assert_delete_user "${bobs[1]}"
|
|
mgmt_assert_delete_alias_group "${aliases[1]}"
|
|
}
|
|
|
|
|
|
test_mixed_case_users() {
|
|
# create mixed-case user name
|
|
# add user to alias using different cases
|
|
# send mail from another user to that user - validates smtp, imap, delivery
|
|
# send mail from another user to the alias
|
|
# send mail from that user as the alias to the other user
|
|
|
|
test_start "mixed-case-users"
|
|
|
|
local alices=(alice@mgmt.somedomain.com
|
|
aLICE@mgmt.somedomain.com
|
|
aLiCe@mgmt.somedomain.com
|
|
ALICE@mgmt.somedomain.com
|
|
alIce@mgmt.somedomain.com)
|
|
local bobs=(bob@mgmt.somedomain.com
|
|
Bob@mgmt.somedomain.com
|
|
boB@mgmt.somedomain.com
|
|
BOB@mgmt.somedomain.com)
|
|
local aliases=(aLICE@mgmt.anotherdomain.com
|
|
aLiCe@mgmt.anotherdomain.com
|
|
ALICE@mgmt.anotherdomain.com)
|
|
|
|
_test_mixed_case "${alices[*]}" "${bobs[*]}" "${aliases[*]}"
|
|
|
|
test_end
|
|
}
|
|
|
|
|
|
test_mixed_case_domains() {
|
|
# create mixed-case domain names
|
|
# add user to alias using different cases
|
|
# send mail from another user to that user - validates smtp, imap, delivery
|
|
# send mail from another user to the alias
|
|
# send mail from that user as the alias to the other user
|
|
|
|
test_start "mixed-case-domains"
|
|
|
|
local alices=(alice@mgmt.somedomain.com
|
|
alice@MGMT.somedomain.com
|
|
alice@mgmt.SOMEDOMAIN.com
|
|
alice@mgmt.somedomain.COM
|
|
alice@Mgmt.SomeDomain.Com)
|
|
local bobs=(bob@mgmt.somedomain.com
|
|
bob@MGMT.somedomain.com
|
|
bob@mgmt.SOMEDOMAIN.com
|
|
bob@Mgmt.SomeDomain.com)
|
|
local aliases=(alice@MGMT.anotherdomain.com
|
|
alice@mgmt.ANOTHERDOMAIN.com
|
|
alice@Mgmt.AnotherDomain.Com)
|
|
|
|
_test_mixed_case "${alices[*]}" "${bobs[*]}" "${aliases[*]}"
|
|
|
|
test_end
|
|
}
|
|
|
|
|
|
test_intl_domains() {
|
|
test_start "intl-domains"
|
|
|
|
# local intl alias
|
|
local alias="alice@bücher.example"
|
|
local alias_idna="alice@xn--bcher-kva.example"
|
|
|
|
# remote intl user / forward-to
|
|
local intl_person="hans@bücher.example"
|
|
local intl_person_idna="hans@xn--bcher-kva.example"
|
|
local intl_person_domain=$(email_domainpart "$intl_person")
|
|
local intl_person_idna_domain=$(email_domainpart "$intl_person_idna")
|
|
|
|
# local users
|
|
local bob="bob@somedomain.com"
|
|
local bob_pw="$(generate_password 16)"
|
|
local mary="mary@somedomain.com"
|
|
local mary_pw="$(generate_password 16)"
|
|
|
|
start_log_capture
|
|
|
|
# international domains are not permitted for user accounts
|
|
if mgmt_create_user "$intl_person" "$bob_pw"; then
|
|
test_failure "A user account is not permitted to have an international domain"
|
|
# ensure user is removed as is expected by the remaining tests
|
|
mgmt_delete_user "$intl_person"
|
|
delete_user "$intl_person"
|
|
delete_user "$intl_person_idna"
|
|
fi
|
|
|
|
# given an idna encoded user - the user should have 2 mail addresses
|
|
if ! mgmt_create_user "$intl_person_idna" "$bob_pw"; then
|
|
test_failure "Could not create idna-encoded user account $intl_person_idna"
|
|
else
|
|
get_attribute "$LDAP_USERS_BASE" "(mail=$intl_person_idna)" "mail"
|
|
if [ -z "$ATTR_DN" ] || \
|
|
! array_contains "$intl_person" "${ATTR_VALUE[@]}" || \
|
|
! array_contains "$intl_person_idna" "${ATTR_VALUE[@]}"
|
|
then
|
|
test_failure "Alias's ($intl_person) mail attribute expected to have both the idna and utf8 names, got ${#ATTR_VALUE[@]}: ${ATTR_VALUE[*]}, expected: $intl_person,$intl_person_idna"
|
|
[ ! -z "$ATTR_DN" ] && record_search "$ATTR_DN"
|
|
else
|
|
record_search "$ATTR_DN"
|
|
|
|
# required aliases are automatically created and should
|
|
# have both mail addresses (idna and utf8)
|
|
get_attribute "$LDAP_ALIASES_BASE" "(mail=abuse@$intl_person_idna_domain)" "mail"
|
|
if [ -z "$ATTR_DN" ]; then
|
|
test_failure "Required alias not created!"
|
|
debug_search "(objectClass=mailGroup)" >>$TEST_OF
|
|
elif ! array_contains "abuse@$intl_person_domain" "${ATTR_VALUE[@]}" || \
|
|
! array_contains "abuse@$intl_person_idna_domain" "${ATTR_VALUE[@]}"
|
|
then
|
|
test_failure "Require alias abuse@$intl_person_idna_domain expected to contain both idna and utf8 mail addresses"
|
|
record_search "$ATTR_DN"
|
|
fi
|
|
|
|
# ensure user is removed as is expected by the remaining tests
|
|
mgmt_delete_user "$intl_person_idna"
|
|
fi
|
|
fi
|
|
|
|
# at this point intl_person does not exist, so all required aliases
|
|
# should also not be present
|
|
get_attribute "$LDAP_ALIASES_BASE" "(mail=*@$intl_person_idna_domain)"
|
|
if [ ! -z "$ATTR_DN" ]; then
|
|
test_failure "No required alias should not exist for the $intl_person_domain domain"
|
|
record_search "$ATTR_DN"
|
|
fi
|
|
|
|
# create local users bob and mary
|
|
mgmt_assert_create_user "$bob" "$bob_pw"
|
|
mgmt_assert_create_user "$mary" "$mary_pw"
|
|
|
|
# create intl alias with local user bob and intl_person in it
|
|
if mgmt_assert_create_alias_group "$alias" "$bob" "$intl_person"; then
|
|
# examine LDAP server to verify IDNA-encodings
|
|
|
|
# 1. the mail attribute for the alias should have both the
|
|
# idna and utf8 addresses
|
|
get_attribute "$LDAP_ALIASES_BASE" "(mail=$alias)" "mail"
|
|
if [ -z "$ATTR_DN" ] || \
|
|
! array_contains "$alias" "${ATTR_VALUE[@]}" || \
|
|
! array_contains "$alias_idna" "${ATTR_VALUE[@]}"
|
|
then
|
|
test_failure "Alias's ($alias) mail attribute expected to have both the idna and utf8 names, got: ${ATTR_VALUE[*]}, expected: $alias,$alias_idna"
|
|
[ ! -z "$ATTR_DN" ] && record_search "$ATTR_DN"
|
|
fi
|
|
|
|
record_search "$ATTR_DN"
|
|
|
|
# 2. the mailMember attribute for the alias should contain the
|
|
# idna encoded intl_person (who is external - not a system user)
|
|
get_attribute "$LDAP_ALIASES_BASE" "(mail=$alias_idna)" "mailMember"
|
|
if [ -z "$ATTR_DN" ]; then
|
|
test_failure "IDNA-encoded alias group not found! created as:$alias expected:$alias_idna"
|
|
elif [ "$ATTR_VALUE" != "$intl_person_idna" ]; then
|
|
test_failure "Alias group with user having an international domain was not encoded properly. added as:$intl_person expected:$intl_person_idna"
|
|
fi
|
|
fi
|
|
|
|
# re-create intl alias with local user bob only
|
|
mgmt_assert_create_alias_group "$alias" "$bob"
|
|
|
|
assert_check_logs
|
|
|
|
if ! have_test_failures; then
|
|
# send mail to alias from mary, ensure bob got it
|
|
record "[Sending to intl alias from mary]"
|
|
# note PYMAIL does not do idna conversion - it'll throw
|
|
# "UnicodeEncodeError: 'ascii' codec can't encode character
|
|
# '\xfc' in position 38".
|
|
#
|
|
# we'll have to send to the idna address directly
|
|
start_log_capture
|
|
local subject="Mail-In-A-Box test $(generate_uuid)"
|
|
local output
|
|
output="$($PYMAIL -subj "$subject" -no-delete -to "$alias_idna" na $PRIVATE_IP $mary "$mary_pw" 2>&1)"
|
|
assert_python_success $? "$output"
|
|
output="$($PYMAIL -subj "$subject" -no-send $PRIVATE_IP $bob "$bob_pw" 2>&1)"
|
|
assert_python_success $? "$output"
|
|
assert_check_logs
|
|
fi
|
|
|
|
mgmt_assert_delete_alias_group "$alias"
|
|
mgmt_assert_delete_user "$bob"
|
|
mgmt_assert_delete_user "$mary"
|
|
|
|
test_end
|
|
}
|
|
|
|
|
|
|
|
test_totp() {
|
|
test_start "totp"
|
|
|
|
# alice
|
|
local alice="alice@somedomain.com"
|
|
local alice_pw="$(generate_password 16)"
|
|
|
|
start_log_capture
|
|
|
|
# create alice
|
|
mgmt_assert_create_user "$alice" "$alice_pw"
|
|
|
|
# alice must be admin to use TOTP
|
|
if ! have_test_failures; then
|
|
if mgmt_totp_enable "$alice" "$alice_pw"; then
|
|
test_failure "User must be an admin to use TOTP, but server allowed it"
|
|
else
|
|
mgmt_assert_privileges_add "$alice" "admin"
|
|
fi
|
|
fi
|
|
|
|
# add totp to alice's account (if successful, secret is in TOTP_SECRET)
|
|
if ! have_test_failures; then
|
|
mgmt_assert_totp_enable "$alice" "$alice_pw"
|
|
# TOTP_SECRET and TOTP_TOKEN are now set...
|
|
fi
|
|
|
|
# logging in with just the password should now fail
|
|
if ! have_test_failures; then
|
|
record "Expect a login failure..."
|
|
mgmt_assert_admin_login "$alice" "$alice_pw" "missing-totp-token"
|
|
fi
|
|
|
|
|
|
# logging into /admin/me with a password and a token should
|
|
# succeed, and an api_key generated
|
|
local api_key
|
|
if ! have_test_failures; then
|
|
record "Try using a password and a token to get the user api key, we may have to wait 30 seconds to get a new token..."
|
|
|
|
local old_totp_token="$TOTP_TOKEN"
|
|
if ! mgmt_get_totp_token "$TOTP_SECRET" "$TOTP_TOKEN"; then
|
|
test_failure "Could not obtain a new TOTP token"
|
|
|
|
else
|
|
# we have a new token, try logging in ...
|
|
# the token must be placed in the header "x-auth-token"
|
|
if mgmt_assert_admin_login "$alice" "$alice_pw" "ok" "--header=x-auth-token: $TOTP_TOKEN"
|
|
then
|
|
api_key="$(/usr/bin/jq -r '.api_key' <<<"$REST_OUTPUT")"
|
|
record "Success: login with TOTP token successful. api_key=$api_key"
|
|
|
|
# ensure the totpMruToken was changed in LDAP
|
|
get_attribute "$LDAP_USERS_BASE" "(mail=$alice)" "totpMruToken"
|
|
if [ "$ATTR_VALUE" != "{0}$TOTP_TOKEN" ]; then
|
|
record_search "(mail=$alice)"
|
|
test_failure "totpMruToken wasn't updated in LDAP"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# we should be able to login using the user's api key
|
|
if ! have_test_failures; then
|
|
record "[Use the session key to enum users]"
|
|
if ! mgmt_rest_as_user "GET" "/admin/mail/users?format=json" "$alice" "$api_key"; then
|
|
test_failure "Unable to use the session key to issue a rest call: $REST_ERROR"
|
|
else
|
|
record "Success: $REST_OUTPUT"
|
|
fi
|
|
fi
|
|
|
|
# disable totp on the account - login should work with just the password
|
|
# and the ldap entry should not have the 'totpUser' objectClass
|
|
if ! have_test_failures; then
|
|
if mgmt_assert_mfa_disable "$alice" "$api_key"; then
|
|
mgmt_assert_admin_login "$alice" "$alice_pw" "ok"
|
|
fi
|
|
fi
|
|
|
|
# check for errors in system logs
|
|
if ! have_test_failures; then
|
|
assert_check_logs
|
|
else
|
|
check_logs
|
|
fi
|
|
|
|
mgmt_assert_delete_user "$alice"
|
|
test_end
|
|
}
|
|
|
|
|
|
|
|
suite_start "management-users" mgmt_start
|
|
|
|
test_totp
|
|
test_mixed_case_domains
|
|
test_mixed_case_users
|
|
test_intl_domains
|
|
|
|
suite_end mgmt_end
|
|
|