# -*- indent-tabs-mode: t; tab-width: 4; -*- ##### ##### This file is part of Mail-in-a-Box-LDAP which is released under the ##### terms of the GNU Affero General Public License as published by the ##### Free Software Foundation, either version 3 of the License, or (at ##### your option) any later version. See file LICENSE or go to ##### https://github.com/downtownallday/mailinabox-ldap for full license ##### details. ##### # Available REST calls: # # general curl format: # curl -X VERB [-d "parameters"] --user {email}:{password} https://{{hostname}}/admin/mail/users[action] # ALIASES: # curl -X GET https://{{hostname}}/admin/mail/aliases?format=json # curl -X POST -d "address=new_alias@mydomail.com" -d "forwards_to=my_email@mydomain.com" https://{{hostname}}/admin/mail/aliases/add # curl -X POST -d "address=new_alias@mydomail.com" https://{{hostname}}/admin/mail/aliases/remove # USERS: # curl -X GET https://{{hostname}}/admin/mail/users?format=json # curl -X POST -d "email=new_user@mydomail.com" -d "password=s3curE_pa5Sw0rD" https://{{hostname}}/admin/mail/users/add # curl -X POST -d "email=new_user@mydomail.com" https://{{hostname}}/admin/mail/users/remove # curl -X POST -d "email=new_user@mydomail.com" -d "privilege=admin" https://{{hostname}}/admin/mail/users/privileges/add # curl -X POST -d "email=new_user@mydomail.com" https://{{hostname}}/admin/mail/users/privileges/remove mgmt_start() { # Must be called before performing any REST calls local domain="${1:-somedomain.com}" MGMT_ADMIN_EMAIL="test_admin@$domain" MGMT_ADMIN_PW="$(generate_password)" delete_user "$MGMT_ADMIN_EMAIL" record "[Creating a new account with admin rights for management tests]" create_user "$MGMT_ADMIN_EMAIL" "$MGMT_ADMIN_PW" "admin" MGMT_ADMIN_DN="$ATTR_DN" record "Created: $MGMT_ADMIN_EMAIL at $MGMT_ADMIN_DN" } mgmt_end() { # Clean up after mgmt_start delete_user "$MGMT_ADMIN_EMAIL" } mgmt_rest() { # Issue a REST call to the management subsystem local verb="$1" # eg "POST" local uri="$2" # eg "/mail/users/add" shift; shift; # remaining arguments are data # call function from lib/rest.sh rest_urlencoded "$verb" "$uri" "${MGMT_ADMIN_EMAIL}" "${MGMT_ADMIN_PW}" "$@" >>$TEST_OF 2>&1 return $? } mgmt_rest_as_user() { # Issue a REST call to the management subsystem local verb="$1" # eg "POST" local uri="$2" # eg "/mail/users/add" local email="$3" # eg "alice@somedomain.com" local pw="$4" # user's password shift; shift; shift; shift # remaining arguments are data # call function from lib/rest.sh rest_urlencoded "$verb" "$uri" "${email}" "${pw}" "$@" >>$TEST_OF 2>&1 return $? } mgmt_create_user() { local email="$1" local pass="${2:-$email}" local delete_first="${3:-yes}" local rc=0 # ensure the user is deleted (clean test run) if [ "$delete_first" == "yes" ]; then delete_user "$email" fi record "[create user $email]" mgmt_rest POST /admin/mail/users/add "email=$email" "password=$pass" rc=$? return $rc } mgmt_assert_create_user() { local email="$1" local pass="$2" local delete_first="${3}" if ! mgmt_create_user "$email" "$pass" "$delete_first"; then test_failure "Unable to create user $email" test_failure "${REST_ERROR}" return 1 fi return 0 } mgmt_delete_user() { local email="$1" record "[delete user $email]" mgmt_rest POST /admin/mail/users/remove "email=$email" return $? } mgmt_assert_delete_user() { local email="$1" if ! mgmt_delete_user "$email"; then test_failure "Unable to cleanup/delete user $email" test_failure "$REST_ERROR" return 1 fi return 0 } mgmt_create_alias_group() { local alias="$1" shift record "[Create new alias group $alias]" record "members: $@" # ensure the group is deleted (clean test run) record "Try deleting any existing entry" if ! mgmt_rest POST /admin/mail/aliases/remove "address=$alias"; then get_attribute "$LDAP_ALIASES_BASE" "mail=$alias" "dn" if [ ! -z "$ATTR_DN" ]; then delete_dn "$ATTR_DN" fi fi record "Create the alias group" local members="$1" member shift for member; do members="${members},${member}"; done mgmt_rest POST /admin/mail/aliases/add "address=$alias" "forwards_to=$members" return $? } mgmt_assert_create_alias_group() { local alias="$1" shift if ! mgmt_create_alias_group "$alias" "$@"; then test_failure "Unable to create alias group $alias" test_failure "${REST_ERROR}" return 1 fi return 0 } mgmt_delete_alias_group() { local alias="$1" record "[Delete alias group $alias]" mgmt_rest POST /admin/mail/aliases/remove "address=$alias" return $? } mgmt_assert_delete_alias_group() { local alias="$1" if ! mgmt_delete_alias_group "$alias"; then test_failure "Unable to cleanup/delete alias group $alias" test_failure "$REST_ERROR" return 1 fi return 0 } mgmt_privileges_add() { local user="$1" local priv="$2" # only one privilege allowed record "[add privilege '$priv' to $user]" mgmt_rest POST "/admin/mail/users/privileges/add" "email=$user" "privilege=$priv" rc=$? return $rc } mgmt_assert_privileges_add() { if ! mgmt_privileges_add "$@"; then test_failure "Unable to add privilege '$2' to $1" test_failure "${REST_ERROR}" return 1 fi return 0 } mgmt_get_totp_token() { local secret="$1" local mru_token="$2" TOTP_TOKEN="" # this is set to the acquired token on success # the user would normally give the secret to an authenticator app # and get a token -- we'll do that out-of-band. we have to run # the admin's python because setup does not do a 'pip install # pyotp', so the system python3 probably won't have it record "[Get the current token for the secret '$secret']" local count=0 while [ -z "$TOTP_TOKEN" -a $count -lt 10 ]; do TOTP_TOKEN="$(totp_current_token "$secret" 2>>"$TEST_OF")" if [ $? -ne 0 ]; then record "Failed: Could not get the TOTP token !" return 1 fi if [ "$TOTP_TOKEN" == "$mru_token" ]; then TOTP_TOKEN="" record "Waiting for unique token!" sleep 5 else record "Success: token is '$TOTP_TOKEN'" return 0 fi let count+=1 done record "Failed: timeout !" TOTP_TOKEN="" return 1 } mgmt_mfa_status() { local user="$1" local pw="$2" record "[Get MFA status]" if ! mgmt_rest_as_user "POST" "/admin/mfa/status" "$user" "$pw"; then REST_ERROR="Failed: POST /admin/mfa/status: $REST_ERROR" return 1 fi # json is in REST_OUTPUT... return 0 } mgmt_totp_enable() { # enable TOTP for user specified # returns 0 if successful and TOTP_SECRET will contain the secret and TOTP_TOKEN will contain the token used # returns 1 if a REST error occured. $REST_ERROR has the message # returns 2 if some other error occured # local user="$1" local pw="$2" local label="$3" # optional TOTP_SECRET="" record "[Enable TOTP for $user]" # 1. get a totp secret if ! mgmt_mfa_status "$user" "$pw"; then return 1 fi TOTP_SECRET="$(/usr/bin/jq -r ".new_mfa.totp.secret" <<<"$REST_OUTPUT")" if [ $? -ne 0 ]; then record "Unable to obtain setup totp secret - is 'jq' installed?" return 2 fi if [ "$TOTP_SECRET" == "null" ]; then record "No 'totp_secret' in the returned json !" return 2 else record "Found TOTP secret '$TOTP_SECRET'" fi if ! mgmt_get_totp_token "$TOTP_SECRET"; then return 2 fi # 2. enable TOTP record "Enabling TOTP using the secret and token" if ! mgmt_rest_as_user "POST" "/admin/mfa/totp/enable" "$user" "$pw" "secret=$TOTP_SECRET" "token=$TOTP_TOKEN" "label=$label"; then REST_ERROR="Failed: POST /admin/mfa/totp/enable: ${REST_ERROR}" return 1 else record "Success: POST /mfa/totp/enable: '$REST_OUTPUT'" fi return 0 } mgmt_assert_totp_enable() { local user="$1" mgmt_totp_enable "$@" local code=$? if [ $code -ne 0 ]; then test_failure "Unable to enable TOTP for $user" if [ $code -eq 1 ]; then test_failure "${REST_ERROR}" fi return 1 fi get_attribute "$LDAP_USERS_BASE" "(&(mail=$user)(objectClass=totpUser))" "dn" if [ -z "$ATTR_DN" ]; then test_failure "totpUser objectClass not present on $user" fi record_search "(mail=$user)" return 0 } mgmt_mfa_disable() { # returns: # 0: success # 1: a REST error occurred, message in REST_ERROR # 2: some system error occured # 3: mfa is not configured for the user specified local user="$1" local pw="$2" local mfa_id="$3" record "[Disable MFA for $user]" if [ "$mfa_id" == "all" ]; then mfa_id="" elif [ "$mfa_id" == "" ]; then # get first mfa-id if ! mgmt_mfa_status "$user" "$pw"; then return 1 fi mfa_id="$(/usr/bin/jq -r ".enabled_mfa[0].id" <<<"$REST_OUTPUT")" if [ $? -ne 0 ]; then record "Unable to use /usr/bin/jq - is it installed?" return 2 fi if [ "$mfa_id" == "null" ]; then record "No enabled mfa found at .enabled_mfa[0].id" return 3 fi fi if ! mgmt_rest_as_user "POST" "/admin/mfa/disable" "$user" "$pw" "mfa-id=$mfa_id" then REST_ERROR="Failed: POST /admin/mfa/disable: $REST_ERROR" return 1 else record "Success" return 0 fi } mgmt_assert_mfa_disable() { local user="$1" mgmt_mfa_disable "$@" local code=$? if [ $code -ne 0 ]; then test_failure "Unable to disable MFA for $user: $REST_ERROR" return 1 fi get_attribute "$LDAP_USERS_BASE" "(&(mail=$user)(objectClass=totpUser))" "dn" if [ ! -z "$ATTR_DN" ]; then test_failure "totpUser objectClass still present on $user" fi record_search "(mail=$user)" return 0 } mgmt_assert_admin_login() { local user="$1" local pw="$2" local expected_status="${3:-ok}" shift; shift; shift; # remaining arguments are data # note: POST /admin/login always returns http status 200, but errors are in # the json payload record "[POST /admin/login as $user]" if ! mgmt_rest_as_user "POST" "/admin/login" "$user" "$pw" "$@"; then test_failure "POST /admin/login as $user failed: $REST_ERROR" return 1 else local status code status="$(/usr/bin/jq -r '.status' <<<"$REST_OUTPUT")" code=$? if [ $code -ne 0 ]; then test_failure "Unable to run jq ($code) on /admin/login json" return 1 elif [ "$status" == "null" ]; then test_failure "No 'status' in /admin/login json" return 1 elif [ "$status" != "$expected_status" ]; then test_failure "Expected a login status of '$expected_status', but got '$status'" return 1 fi fi return 0 } mgmt_get_user_quota() { local user="$1" record "[get user $user quota]" mgmt_rest GET "/admin/mail/users/quota?email=$user" local rc=$? # REST_OUTPUT contains json, eg: # { "email": "alice@somedomain.com", "quota": "5000" } if [ $rc -eq 0 ]; then # output to stdout the quota value QUOTA="$(/usr/bin/jq -r ".quota" <<<"$REST_OUTPUT" 2>>$TEST_OF)" if [ $? -ne 0 ]; then record "could not obtain quota member from json using jq" rc=1 fi else QUOTA="error" fi return $rc } mgmt_set_user_quota() { local user="$1" local quota="$2" record "[set user $user quota to $quota]" mgmt_rest POST "/admin/mail/users/quota" "email=$user" "quota=$quota" local rc=$? return $rc }